2011-11-02 67 views
3

我有一个@MapsId注释和@EmbeddedId问题。当在Hibernate中运行的代码,我得到:Hibernate例外与@MapsId,@EmbeddedId

产生的原因: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一些简单的测试方法(注意,这是从静态主要方法执行的常规类 - 它是而不是一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提供商休眠(4.0 CR5),它将引发我一个的PropertyAccessException:螺纹

异常 “主要” javax.persistence.PersistenceException: org.hibernate.PropertyAccessException:无法设置在 org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1353) 在 org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java由com.test.entities.EmployeeId.serverId的 反射设定部字段值:1281) at org.hibernate.ejb.AbstractEntityManagerImpl.convert( AbstractEntityManagerImpl.java:1287) 在 org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:853) 在com.test.Standalone.executeTest(Standalone.java:101)在 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(CompositeNestedGenera tedValueGenerator。的java:121) 在 org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:120) 在 org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78) 在 有机.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) at 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作为。在 sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:36)显示java.lang.NullPointerException 在 sun.reflect.UnsafeIntegerFieldAccessorImpl:ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:847) ... 2更所致.set(UnsafeIntegerFieldAccessorImpl.java:57) at java.lang.reflect.Field.set(Field.java:657)at org.hibernate.property.DirectPropertyAccessor $ DirectSetter.set(DirectPropertyAccessor.java:138) .. 13更多

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

我甚至不知道为什么地球上Hibernate需要一个二传手,但只是要确定我为EmployeeWebsiteServer实体提供的制定者,每场(includng的ID)。正如所料 - 没有什么改变,Hibernate仍然无法找到(已经存在的)setter。

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

在此先感谢!

回答

4

请参阅this问题。它只是说,

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

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

正如你看到的,唯一的“处理方法”提出至今Do not use @EmbeddedId,这是一种奇怪的。

+1

谢谢@ jFrentic。我发现读“解决方法”确实很奇怪。这就像是说你的汽车里的雨刷坏了,所以解决方法是不使用它们... –

+0

是的,这很伤心,但是因为它被标记为*已知问题*并且仍然具有'打开'状态,所以希望Hibernate团队将在下一个版本中为此做点什么。虽然,我必须承认,我没有试图自己重现你的问题。我认为我看到有人在Hibernate中使用@EmbeddedId,它似乎工作,但我不确定。如果我发现更多,我会回到这里。 – jFrenetic

+0

@jFrentic - 谢谢你。我也看到了一个使用'@ EmbeddedId'的代码,但我认为'@ MapsId'必须弄乱这里的东西。尽管如此,我已经为Hibernate问题添加了一条评论,我会等待他们的回答。 –

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; 
+0

这个建议似乎比其他所有问题都更为正确。它没有出现“@ MapsId”被正确使用。 – YoYo

9

这里的解决方案:得到了同样的问题,得到现在工作

对于它的工作,您的Employee类的EmployeeId应该在进行持久化之前在Employee的构造函数或SessionBean中实例化。

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

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

+0

这是唯一可行的解​​决方案,恐怕。 –

+0

该解决方案也适用于我。正确配置一切仍然会导致错误,除非您在实体构造函数中实例化嵌入的ID。 –

+0

是的 - 这似乎是正确的...任何'@ MapsId'属性都需要在包含'@ Enitity'实例化时显式实例化和分配。但是,从“EntityManger”中获取时,它会被JPA实现正确提取并实例化。另请参阅https://stackoverflow.com/q/45245021/744133 – YoYo