【问题标题】:Hibernate exception with @MapsId, @EmbeddedId带有@MapsId、@EmbeddedId 的休眠异常
【发布时间】:2011-12-20 12:37:03
【问题描述】:

@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

线程“主”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) 在 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) 在 org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:138) ... 13 更多

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

我什至不知道到底为什么 Hibernate 需要一个 setter,但为了确保我已经为 EmployeeWebsiteWebsite 的每个字段(包括 ID)提供了 setter Server 实体。正如预期的那样——没有任何改变,Hibernate 仍然找不到(已经存在的)setter。

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

提前致谢!

【问题讨论】:

    标签: hibernate jpa jpa-2.0 eclipselink


    【解决方案1】:

    我遇到了同样的问题并通过执行以下操作来解决它,当 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;
        }
    }
    

    【讨论】:

      【解决方案2】:

      此处的解决方案:遇到了同样的问题,现在可以正常工作了

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

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

      不确定这是正常的还是 JPA 规范的错误实现。我认为是后者,因为您的 PK 的构造函数已定义,并且 hibernate 应该能够自行调用它。

      【讨论】:

      • 这个解决方案也对我有用。除非您在实体构造函数中实例化嵌入的 id,否则正确配置所有内容仍会导致错误。
      • 是的 - 这似乎是正确的......当包含 @Enitity 被实例化时,任何@MapsId 属性都需要显式实例化和分配。但是,当从EntityManger 获取时,JPA 实现会正确获取并实例化它。另见stackoverflow.com/q/45245021/744133
      • Hibernate Core 4.3.11.Final 一起工作就像一个魅力。非常感谢。
      • 在 hib 5.3、spring 5 上为我工作。对于带有附加连接表字段的多对多而言,hibernate 没有更好的解决方案。表明即使是这个伟大的库也犯了一个错误,即痴迷于 java 并且无视 RDBMS 的基本设计需求。
      【解决方案3】:

      它很旧,但可能对某些人有用

      尝试

      @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;
      

      【讨论】:

      • 这个建议似乎比其他任何事情都离正确答案更远。 @MapsId 似乎根本没有正确使用。
      【解决方案4】:

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

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

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

      如您所见,目前建议的唯一“解决方法”是Do not use @EmbeddedId,这有点奇怪。

      【讨论】:

      • 感谢@jFrentic。我觉得阅读那个“解决方法”肯定很奇怪。这就好比说你车上的雨刷坏了,所以变通方法就是不用了……
      • 是的,很遗憾,但由于它被标记为已知问题并且仍然具有open 状态,希望Hibernate 团队在下一个版本中对此进行处理.虽然,我必须承认我自己并没有尝试重现您的问题。我认为我看到有人在 Hibernate 中使用 @EmbeddedId 并且它似乎有效,但我不确定。如果我发现更多,我会在这里发帖。
      • @jFrentic - 谢谢。我还看到了一个使用@EmbeddedId 的代码,但我认为@MapsId 一定把这里的事情搞砸了。不过,我已经对 Hibernate 问题添加了评论,我会等待他们的答复。
      • 这已经过时了。此答案中提到的原始问题已按固定方式关闭。
      猜你喜欢
      • 2015-06-28
      • 2014-02-04
      • 2013-05-22
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      • 2014-08-22
      • 2011-09-20
      • 2017-07-06
      相关资源
      最近更新 更多