使用@MapsId,@ EmbeddedId进行Hibernate异常

Pio*_*cki 7 hibernate jpa eclipselink jpa-2.0

我的@MapsId注释有问题@EmbeddedId.在Hibernate中运行代码时,我得到:

由以下原因引起:org.hibernate.PropertyAccessException:无法通过com.test.entities.EmployeeId.serverId的反射设置器设置字段值

但是,让我们从头开始......我有一个实体的复合主键,Employee它包含两个其他实体的外键(ServerWebsite).为了实现干净的设计,我在Employee实体中使用实体关系,这应该反映在EmployeeIdembeddable中.这个例子很简单,如下:

@Entity
public class Server implements Serializable {

    @Id
    @GeneratedValue
    private int id;

    private String url;

    public Server() {}

    public Server(String name) {
        this.url = name;
    }
}
Run Code Online (Sandbox Code Playgroud)
@Entity
public class Website implements Serializable {

    @Id
    @GeneratedValue
    private int id;

    private String name;

    public Website() {}

    public Website(String name) {
        this.name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)
@Embeddable
public class EmployeeId implements Serializable {

    protected int websiteId;
    protected int serverId;
}
Run Code Online (Sandbox Code Playgroud)
@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我有一些用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();
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我在这里没有做任何花哨的东西 - 只需在Employee对象中设置实体并坚持下去.据我所知,@MapsId注释应该反映注释实体的状态EmbeddedId.

现在的问题是,在EclipseLink中,一切都运行顺畅,实体被正确保存.当我将JPA 2.0提供程序更改为Hibernate(4.0 CR5)时,它会抛出一个PropertyAccessException:

例外在线程"主" javax.persistence.PersistenceException:org.hibernate.PropertyAccessException:无法设置通过在org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl com.test.entities.EmployeeId.serverId的反射设定部的字段值.的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:无法设置字段值通过在org.hibernate.property.DirectPropertyAccessor $ DirectSetter.set(DirectPropertyAccessor.java:150)在org.hibernate.mapping.Component $ ValueGenerationPlan.execute(Component.java:436 com.test.entities.EmployeeId.serverId的反射设定器在org.hibe 在org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java)的org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:120)中的rnate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:121): 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)at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:847)... 2更多引起:java.lang.NullPointerE xception在sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:36)在sun.reflect.UnsafeIntegerFieldAccessorImpl.set(UnsafeIntegerFieldAccessorImpl.java:57)在java.lang.reflect.Field.set(Field.java:657)在有机.hibernate.property.DirectPropertyAccessor $ DirectSetter.set(DirectPropertyAccessor.java:138)... 13更多

我不知道NullPointerException可能来自哪里 - 因为你可以看到所有实体属性都已设置.

我甚至不知道为什么地球上Hibernate需要一个二传手,但只是要确定我已经为各个领域(includng编号),提供制定者Employee,Website以及Server实体.正如所料 - 没有任何改变,Hibernate仍然找不到(已经存在)setter.

你认为它可能是一些错误,或者我只是不理解JPA 2.0合同的某些部分?

提前致谢!

Ric*_*ati 15

解决方案:遇到同样的问题,现在就开始工作了.

为了使它工作,EmployeeId你的Employee类应该在Employee的构造函数中实例化,或者在建立持久性之前在SessionBean中实例化.

否则,它会尝试填充embeddedid的值,并将其设置为null.

不确定这是否正常或者是否是JPA规范的错误实现.我认为后者因为你的PK的构造函数被定义而且hibernate应该能够自己调用它.


jFr*_*tic 5

看到这个问题.它只是说明了这一点

如果实体包含以下条件,则可能抛出org.hibernate.PropertyAccessException:

  1. 使用@EmbeddedId
  2. 在集合或关联属性/字段上使用@JoinTable,它引用实体的另一个属性/字段.

如你所见,到目前为止唯一的"解决方法"是Do not use @EmbeddedId,这有点奇怪.

  • 谢谢@jFrentic。我发现阅读“解决方法”绝对很奇怪。这就像是说您汽车中的刮水器损坏了,因此解决方法是不使用它们。 (2认同)