Hibernate n:m extractHashCode throws NullPointerException

ε祈祈猫儿з 提交于 2019-11-30 09:16:19

问题


I get the following exception while inserting an object with hibernate. Reading from the database works like a charm. I use MySQL 5.5 as database provider and hibernate 3.6.5.

I have the following database schema:

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 in cell and location are primary keys and servingCell,neighbourCell and location is the composite primary key in cellatlocation.

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)

The classes I want to insert: 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);
}
}

and finally the primary key mapping itself 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;
}
}

回答1:


The problem is that hibernate is trying to save the relationship object, CellAtLocation instance, while the children objects, Cell and/or Location instances are not yet persisted. Thus, children objects don't have generated ids associated with them and therefore hibernate can not compute the hash for them.

Before trying to save CellAtLocation instance, try saving the children objects first by calling saveOrUpdate method on them.




回答2:


For anyone also dealing with this issue, it occurred in my case simply because I did not have an open and active transaction. The stack trace did not point directly to this being the issue but can be explained as follows:

The parent item was being persisted in the cache and hibernate simply accepted the parent not having an actual ID. If we could have somehow called flush() on our connection we would have then been notified of the non-existent transaction. Instead, when the child item was to be persisted the parent's ID did not TRULY exist. When hibernate went to get the parent's hashed id for the purpose of saving the child, the NPE was thrown.




回答3:


In my case, found out that one primary key in a foreign key table has not set. Only the fields that implement hashcode int the table were set.




回答4:


I had the same problem and figured out that the way to properly map embedded ids is by using @Embeddable, @EmbeddedId and @MapsId (which is the one missing in the problem code). The docs from @MapsId annotation states an example that fixes this issue:

Example:

// parent entity has simple primary key

@Entity
public class Employee {
    @Id
    private long employeeId;

    private String name;
    ...
}

// dependent entity uses EmbeddedId for composite key

@Embeddable
public class DependentId {
    private String name;

    private long employeeId;   // corresponds to primary key type of Employee
}

@Entity
public class Dependent {
    @EmbeddedId
    private DependentId dependentId;
    ...
    @MapsId("employeeId")   //  maps the employeeId attribute of embedded id
    @ManyToOne
    private Employee employee;
}

This is the proper way to fix the issue. This way, you wouldn't need to save the entities separately (which is not a good practice). Instead, hibernate will manage the entire transaction for you by mapping the generated ids properly.

Hope this helps for anyone having this issue in the future.

Cheers,



来源:https://stackoverflow.com/questions/6744451/hibernate-nm-extracthashcode-throws-nullpointerexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!