我的@MapsId
注释和@EmbeddedId
存在问题。在Hibernate中运行代码时,我得到:
引起:org.hibernate.PropertyAccessException:无法设置 反射设定器的场值 com.test.entities.EmployeeId.serverId
但是,让我们从头开始......我有一个实体Employee
的复合主键,它由两个其他实体(Server
和Website
)的外键组成。为了获得干净的设计,我在Employee实体中使用实体关系,这应该反映在EmployeeId
嵌入式中。这个例子很简单,如下:
@Entity
public class Server implements Serializable {
@Id
@GeneratedValue
private int id;
private String url;
public Server() {}
public Server(String name) {
this.url = name;
}
}
@Entity
public class Website implements Serializable {
@Id
@GeneratedValue
private int id;
private String name;
public Website() {}
public Website(String name) {
this.name = name;
}
}
@Embeddable
public class EmployeeId implements Serializable {
protected int websiteId;
protected int serverId;
}
@Entity
public class Employee implements Serializable {
@EmbeddedId
private EmployeeId id;
private String firstName;
@ManyToOne
@MapsId("serverId")
private Server server;
@OneToOne
@MapsId("websiteId")
private Website website;
public Employee() {}
public Employee(String firstName, Server server, Website website) {
this.firstName = firstName;
this.server = server;
this.website = website;
}
}
现在我有一些用Java SE编写的简单测试方法(注意这是一个从静态main方法执行的常规类 - 它是不一个JUnit类):
private void executeTest() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("standaloneTests");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Server s = new Server("BigServer");
em.persist(s);
Website w = new Website("domain.com");
em.persist(w);
Employee e = new Employee("John", s, w);
em.persist(e);
tx.commit();
em.close();
emf.close();
}
正如你所看到的,我在这里没有做任何花哨的事情 - 只需在Employee
对象中设置实体并坚持下去。据我了解,@MapsId
注释应该反映EmbeddedId
中带注释的实体的状态。
现在的问题是,在EclipseLink中,一切都运行顺畅,实体被正确保留。当我将JPA 2.0提供程序更改为Hibernate(4.0 CR5)时,它会抛出 PropertyAccessException :
线程“main”中的异常javax.persistence.PersistenceException: org.hibernate.PropertyAccessException:无法设置字段值 com.test.entities.EmployeeId.serverId的反射设定器 org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1353) 在 org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1281) 在 org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1287) 在 org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:853) 在com.test.Standalone.executeTest(Standalone.java:101)at com.test.Standalone.main(Standalone.java:35)引起: org.hibernate.PropertyAccessException:无法设置字段值 com.test.entities.EmployeeId.serverId的反射设定器 org.hibernate.property.DirectPropertyAccessor $ DirectSetter.set(DirectPropertyAccessor.java:150) 在 org.hibernate.mapping.Component $ ValueGenerationPlan.execute(Component.java:436) 在 org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:121) 在 org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:120) 在 org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78) 在 org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:180) 在 org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:136) 在 org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:64) 在 org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:729) 在org.hibernate.internal.SessionImpl.persist(SessionImpl.java:705) 在org.hibernate.internal.SessionImpl.persist(SessionImpl.java:709) 在 org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:847) ... 2更多引起:java.lang.NullPointerException at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:36) 在 sun.reflect.UnsafeIntegerFieldAccessorImpl.set(UnsafeIntegerFieldAccessorImpl.java:57) 在java.lang.reflect.Field.set(Field.java:657)at org.hibernate.property.DirectPropertyAccessor $ DirectSetter.set(DirectPropertyAccessor.java:138) ......还有13个
我不知道NullPointerException可能来自哪里 - 因为您可以看到所有实体属性都已设置。
我甚至不知道为什么Hibernate需要一个setter ,但为了确保我为Employee
,{{{{}}的每个字段(包括ID)提供了setter 1}}和Website
个实体。正如预期的那样 - 没有任何改变,Hibernate仍然找不到(已经存在)setter。
你认为这可能是一些错误,或者我只是不理解JPA 2.0合同的某些部分?
提前致谢!
答案 0 :(得分:15)
解决方案:遇到同样的问题,现在让它正常工作。
为了使它工作,你的Employee类的EmployeeId
应该在Employee的构造函数中实例化,或者在建立持久性之前在SessionBean中实例化。
否则,它会尝试填充您的embeddedid的值,并为null。
不确定这是否正常或者是否是JPA规范的错误实现。我认为后者因为PK的构造函数已经定义,而hibernate应该能够自己调用它。
答案 1 :(得分:5)
请参阅this问题。它只是陈述
如果是实体,则可能抛出org.hibernate.PropertyAccessException 包含以下条件:
- 使用@EmbeddedId
- 在集合或关联属性/字段上使用@JoinTable,它引用实体的另一个属性/字段。
醇>
如您所见,到目前为止建议的唯一“解决方法”是Do not use @EmbeddedId
,这有点奇怪。
答案 2 :(得分:2)
它是旧的,但可能对某些人有用
尝试
@Entity
public class Employee implements Serializable {
@EmbeddedId
private EmployeeId id;
private String firstName;
@ManyToOne
@MapsId("id")
@JoinColumn(name ="serverId")
private Server server;
@OneToOne
@MapsId("id")
@JoinColumn(name= "websiteId")
private Website website;
答案 3 :(得分:0)
我遇到了同样的问题,并通过以下操作解决了此问题,当Hibernate尝试设置Employee.id的值时,employee.id为null,只需实例化employee.id即可。
@Entity
public class Employee implements Serializable {
@EmbeddedId
private EmployeeId id = new EmployeeId();
private String firstName;
@ManyToOne
@MapsId("serverId")
private Server server;
@OneToOne
@MapsId("websiteId")
private Website website;
public Employee() {}
public Employee(String firstName, Server server, Website website) {
this.firstName = firstName;
this.server = server;
this.website = website;
}
}