Hibernate n: m extractHashCode lanza NullPointerException

Obtengo la siguiente excepción al insertar un objeto con hibernación. Leer de la base de datos funciona de maravilla. Yo sueloMySQL 5.5 como proveedor de base de datos yhibernate 3.6.5.

Tengo el siguiente esquema de base de datos:

cell(id,cellid,lac,mcc,mnc,insertTime)
location(id,latitude,longitude,altitude,accuracy,heading,hdop,vdop,pdop,insertTime)
cellatlocation(servingCell,neighbourCell,location,signalStrength,insertTime)

where id en la celda y la ubicación son claves primarias y serveCell, neighbourCell y location es la clave primaria compuesta en la ubicación de cellat.

java.lang.NullPointerException
at org.hibernate.type.descriptor.java.AbstractTypeDescriptor.extractHashCode(AbstractTypeDescriptor.java:88)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:196)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:191)
at org.hibernate.type.EntityType.getHashCode(EntityType.java:325)
at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:222)
at org.hibernate.engine.EntityKey.generateHashCode(EntityKey.java:126)
at org.hibernate.engine.EntityKey.<init>(EntityKey.java:70)
at org.hibernate.engine.StatefulPersistenceContext.getDatabaseSnapshot(StatefulPersistenceContext.java:286)
at org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:211)
at org.hibernate.event.def.AbstractSaveEventListener.getEntityState(AbstractSaveEventListener.java:531)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:103)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:685)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:677)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:673)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:345)
at $Proxy17.saveOrUpdate(Unknown Source)

Las clases que quiero insertar: Cell.java

@Entity
@Table(name = "cell", catalog = "crisis")
public class Cell implements Serializable {

private static final long serialVersionUID = -8532796958180260393L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Integer mnc;
private Integer mcc;
private Long cellid;
private Integer lac;
@org.hibernate.annotations.Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
private DateTime insertTime;
@OneToMany(mappedBy = "pk.servingCell")
private List<CellAtLocation> cellAtLocation = new LinkedList<CellAtLocation>();

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public Integer getMnc() {
    return mnc;
}

public void setMnc(Integer mnc) {
    this.mnc = mnc;
}

public Integer getMcc() {
    return mcc;
}

public void setMcc(Integer mcc) {
    this.mcc = mcc;
}

public Long getCellid() {
    return cellid;
}

public void setCellid(Long cellid) {
    this.cellid = cellid;
}

public Integer getLac() {
    return lac;
}

public void setLac(Integer lac) {
    this.lac = lac;
}

public DateTime getInsertTime() {
    return insertTime;
}

public void setInsertTime(DateTime insertTime) {
    this.insertTime = insertTime;
}

public List<CellAtLocation> getCellAtLocation() {
    return cellAtLocation;
}

public void setCellAtLocation(List<CellAtLocation> cellAtLocation) {
    this.cellAtLocation = cellAtLocation;
}
}

Location.java

@Entity
@Table(name = "location", catalog = "crisis")
public class Location implements Serializable {

private static final long serialVersionUID = 2197290868029835453L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private Double latitude;
private Double longitude;
private Double altitude;
private Double accuracy;
private Double heading;
private Double hdop;
private Double vdop;
private Double pdop;
@org.hibernate.annotations.Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
private DateTime insertTime;

@OneToMany(mappedBy = "pk.location")
private List<CellAtLocation> cellAtLocation = new LinkedList<CellAtLocation>();

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public Double getLatitude() {
    return latitude;
}

public void setLatitude(Double latitude) {
    this.latitude = latitude;
}

public Double getLongitude() {
    return longitude;
}

public void setLongitude(Double longitude) {
    this.longitude = longitude;
}

public Double getAltitude() {
    return altitude;
}

public void setAltitude(Double altitude) {
    this.altitude = altitude;
}

public Double getAccuracy() {
    return accuracy;
}

public void setAccuracy(Double accuracy) {
    this.accuracy = accuracy;
}

public Double getHeading() {
    return heading;
}

public void setHeading(Double heading) {
    this.heading = heading;
}

public Double getHdop() {
    return hdop;
}

public void setHdop(Double hdop) {
    this.hdop = hdop;
}

public Double getVdop() {
    return vdop;
}

public void setVdop(Double vdop) {
    this.vdop = vdop;
}

public Double getPdop() {
    return pdop;
}

public void setPdop(Double pdop) {
    this.pdop = pdop;
}

public DateTime getInsertTime() {
    return insertTime;
}

public void setInsertTime(DateTime insertTime) {
    this.insertTime = insertTime;
}

public List<CellAtLocation> getCellAtLocation() {
    return cellAtLocation;
}

public void setCellAtLocation(List<CellAtLocation> cellAtLocation) {
    this.cellAtLocation = cellAtLocation;
}

}

CellAtLocation.java

@Entity
@Table(name = "cellatlocation", catalog = "crisis")
@AssociationOverrides({ @AssociationOverride(name = "pk.servingCell", joinColumns = @JoinColumn(name = "servingCell")),
    @AssociationOverride(name = "pk.neighbourCell", joinColumns = @JoinColumn(name = "neighbourCell")),
    @AssociationOverride(name = "pk.location", joinColumns = @JoinColumn(name = "location")) })

public class CellAtLocation implements Serializable {
private static final long serialVersionUID = -4440795783726362367L;
private CellAtLocationPk pk = new CellAtLocationPk();
private Integer signalStrength;

@EmbeddedId
private CellAtLocationPk getPk() {
    return pk;
}

@SuppressWarnings("unused")
private void setPk(CellAtLocationPk pk) {
    this.pk = pk;
}

@Transient
public Cell getServingCell() {
    return getPk().getServingCell();
}

public void setServingCell(Cell cell) {
    getPk().setServingCell(cell);
}

@Transient
public Cell getNeighbourCell() {
    return getPk().getNeighbourCell();
}

public void setNeighbourCell(Cell cell) {
    getPk().setNeighbourCell(cell);
}

@Transient
public Location getLocation() {
    return getPk().getLocation();
}

public void setLocation(Location location) {
    getPk().setLocation(location);
}

public Integer getSignalStrength() {
    return signalStrength;
}

public void setSignalStrength(Integer signalStrength) {
    this.signalStrength = signalStrength;
}

public boolean equals(Object o) {
    if (this == o)
        return true;
    if (o == null || getClass() != o.getClass())
        return false;
    CellAtLocation that = (CellAtLocation) o;
    if (getPk() != null ? !getPk().equals(that.getPk()) : that.getPk() != null)
        return false;
    return true;
}

public int hashCode() {
    return (getPk() != null ? getPk().hashCode() : 0);
}
}

y, finalmente, el mapeo de la clave primaria en sí CellAtLocationPk.java

@Embeddable
public class CellAtLocationPk implements Serializable {
private static final long serialVersionUID = 5286485161491158083L;
private Cell servingCell;
private Cell neighbourCell;
private Location location;

@ManyToOne
public Cell getServingCell() {
    return servingCell;
}

public void setServingCell(Cell servingCell) {
    this.servingCell = servingCell;
}

@ManyToOne
public Cell getNeighbourCell() {
    return neighbourCell;
}

public void setNeighbourCell(Cell neighbourCell) {
    this.neighbourCell = neighbourCell;
}

@ManyToOne
public Location getLocation() {
    return location;
}

public void setLocation(Location location) {
    this.location = location;
}

public boolean equals(Object o) {
    if (this == o)
        return true;
    if (o == null || getClass() != o.getClass())
        return false;
    CellAtLocationPk that = (CellAtLocationPk) o;
    if (servingCell != null ? !servingCell.equals(that.servingCell) : that.servingCell != null)
        return false;
    if (neighbourCell != null ? !neighbourCell.equals(that.neighbourCell) : that.neighbourCell != null)
        return false;
    if (location != null ? !location.equals(that.location) : that.location != null)
        return false;

    return true;
}

public int hashCode() {
    int result;
    result = (servingCell != null ? servingCell.hashCode() : 0);
    result = 31 * result + (neighbourCell != null ? neighbourCell.hashCode() : 0);
    result = 31 * result + (location != null ? location.hashCode() : 0);
    return result;
}
}

Respuestas a la pregunta(3)

Su respuesta a la pregunta