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

时间:2011-11-02 17:04:03

标签: hibernate jpa jpa-2.0 eclipselink

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

  

引起:org.hibernate.PropertyAccessException:无法设置   反射设定器的场值   com.test.entities.EmployeeId.serverId

但是,让我们从头开始......我有一个实体Employee的复合主键,它由两个其他实体(ServerWebsite)的外键组成。为了获得干净的设计,我在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合同的某些部分?

提前致谢!

4 个答案:

答案 0 :(得分:15)

解决方案:遇到同样的问题,现在让它正常工作

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

否则,它会尝试填充您的embeddedid的值,并为null。

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

答案 1 :(得分:5)

请参阅this问题。它只是陈述

  如果是实体,则可能抛出

org.hibernate.PropertyAccessException   包含以下条件:

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

如您所见,到目前为止建议的唯一“解决方法”是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;
    }
}