【问题标题】:Hibernate n:m extractHashCode throws NullPointerExceptionHibernate n:m extractHashCode 抛出 NullPointerException
【发布时间】:2011-10-08 08:05:08
【问题描述】:

使用休眠插入对象时出现以下异常。从数据库中读取就像一个魅力。我使用 MySQL 5.5 作为数据库提供程序和 hibernate 3.6.5。

我有以下数据库架构:

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

cell 中的 id 和 location 是主键,servingCell、neighbourCell 和 location 是 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)

我要插入的类: 细胞.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;
}
}

位置.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);
}
}

最后是主键映射本身 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;
}
}

【问题讨论】:

    标签: mysql hibernate gethashcode composite-primary-key


    【解决方案1】:

    问题是休眠试图保存关系对象CellAtLocation 实例,而子对象Cell 和/或Location 实例尚未持久化。因此,子对象没有与它们关联的generated ids,因此休眠无法为它们计算哈希。

    在尝试保存CellAtLocation 实例之前,先尝试通过调用saveOrUpdate 方法来保存子对象。

    【讨论】:

    • 手动保存依赖关系不是一个好习惯。它确实会影响性能并围绕事务性产生更多问题。您应该改用 hibernate 来正确映射依赖关系并让它管理关系。
    • 如果我的实体相互依赖怎么办?我先救谁?
    【解决方案2】:

    我遇到了同样的问题,并发现正确映射嵌入式 ID 的方法是使用 @Embeddable、@EmbeddedId 和 @MapsId(这是问题代码中缺少的)。 @MapsId 注释中的文档说明了一个解决此问题的示例:

    例子:

    // 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;
    }
    

    这是解决问题的正确方法。这样,您就不需要单独保存实体(这不是一个好习惯)。相反,hibernate 将通过正确映射生成的 id 来为您管理整个事务。

    希望这对以后遇到此问题的人有所帮助。

    干杯,

    【讨论】:

      【解决方案3】:

      对于同样处理此问题的任何人,在我的案例中发生这种情况仅仅是因为我没有开放且有效的交易。堆栈跟踪没有直接指出这是问题,但可以解释如下:

      父项被持久化在缓存中,hibernate 只接受没有实际 ID 的父项。如果我们能以某种方式在我们的连接上调用flush(),我们就会被通知不存在的事务。相反,当要保留子项时,父项的 ID 并不真正存在。当 hibernate 去获取 parent 的 hash id 以保存 child 时,NPE 被抛出。

      【讨论】:

        【解决方案4】:

        在我的情况下,发现外键表中的一个主键尚未设置。只设置了在表中实现哈希码的字段。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-03-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-11
          • 2013-09-07
          • 2013-06-18
          相关资源
          最近更新 更多