带有ManyToOne的JPA Composite键获取org.hibernate.PropertyAccessException:无法通过reflection设置器设置字段值

我有一个复合键ContractServiceLocationPK由嵌入类中的三个id类型( contractIdlocationIdserviceId )组成。 使用此组合键ContractServiceLocation的类使用@MapsId注释将这些ID映射到其对象。 这是它的样子(删除了setter / getters和不相关的属性):

合同

 @Entity @Table(name = "Contract") public class Contract implements Serializable { public Contract() { } @Id @GeneratedValue private long id; @OneToMany(mappedBy = "contract", cascade = CascadeType.ALL, fetch= FetchType.EAGER) Collection contractServiceLocation; } 

ContractServiceLocationPK

 @Embeddable public class ContractServiceLocationPK implements Serializable { private long contractId; private long locationId; private long serviceId; } 

ContractServiceLocation

 @Entity @Table(name="Contract_Service_Location") public class ContractServiceLocation implements Serializable { @EmbeddedId ContractServiceLocationPK id; @ManyToOne(cascade = CascadeType.ALL) @MapsId("contractId") Contract contract; @ManyToOne(cascade = CascadeType.ALL) @MapsId("locationId") Location location; @ManyToOne(cascade = CascadeType.ALL) @MapsId("serviceId") Service service; BigDecimal price; } 

当试图以任何方式(直接或通过合同)持久化ContractServiceLocation类型的对象时,我得到:

 Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.test.model.ContractServiceLocationPK.contractId at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1187) at com.test.MainTest.main(MainTest.java:139) Caused by: org.hibernate.PropertyAccessException: could not set a field value by reflection setter of com.test.model.ContractServiceLocationPK.contractId at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:134) at org.hibernate.mapping.Component$ValueGenerationPlan.execute(Component.java:441) at org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:121) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:117) at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:811) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:789) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181) ... 1 more Caused by: java.lang.NullPointerException at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source) at sun.reflect.UnsafeLongFieldAccessorImpl.set(Unknown Source) at java.lang.reflect.Field.set(Unknown Source) at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:122) ... 12 more 

我的假设是JPA / Hibernate期望一个Contract对象而不是一个long变量,但如果我将embeddable中的变量从long更改为它们的类型,那么我得到The type of the ID mapped by the relationship 'contract' does not agree with the primary key class of the target entity. 。 如果我尝试使用id类而不是embeddable然后在Contract的OneToMany映射中使用mappedby ,我得到In attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship. 。 如何制作具有多个ManyToOne映射的复合键?

编辑:添加了一个片段,我尝试持久化项目:

  Service service = new Service(); // Set all service properties Contract contract = new Contract(); // Set all contract properties Location location = new Location(); // Set all location properties ContractServiceLocation csl = new ContractServiceLocation(); csl.setContract(contract); csl.setLocation(location); csl.setService(service); Collection cslItems = new ArrayList(); cslItems.add(csl); em.getTransaction().begin(); em.persist(location); em.persist(service); em.persist(csl); em.persist(contract); em.getTransaction().commit(); 

它看起来像这样而不是在某些DAO中的原因是因为我在开发应用程序的其余部分之前首先生成数据库并测试项目。

编辑2:我已经重写了我的模型,现在一切似乎都工作,除了在Eclipse中我得到一个持久的错误。 以下是目前的情况:

合同 – 没有变化(除非删除了预先加载)

ContractServiceLocationPK – 现在是一个ID类

 public class ContractServiceLocationPK implements Serializable { @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "contract_id") private Contract contract; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "location_id") private Location location; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "service_id") private Service service; //getters and setters //overridden equals() and hashCode() } 

ContractServiceLocation

 @Entity @Table(name="Contract_Service_Location") @IdClass(ContractServiceLocationPK.class) public class ContractServiceLocation implements Serializable { @Id Contract contract; @Id Location location; @Id Service service; BigDecimal price; //getters and setters //overridden equals() and hashCode() } 

这似乎现在正常工作。 它创建一个复合键,并与所有复合属性保持多对一关系。 然而,有一些奇怪的东西。 在契约中,eclipse标记@OneToManyContractServiceLocation集合的@OneToMany批注,并带有错误消息In attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship. 。 我假设这是因为Contract定义的Contract属性没有@ManyToOne注释,但是在复合类中定义了它。 我是否偶然发现“不合规的JPA但使用Hibernate”陷阱或者这里发生了什么?

对于您的原始问题(未修改的变体):

您必须在ContractServiceLocation类中实例化“ContractServiceLocationPK id”。 替换线:

@EmbeddedId ContractServiceLocationPK id;

有了这个:

@EmbeddedId ContractServiceLocationPK id = new ContractServiceLocationPK();

然后它应该工作。 因为Hibernate试图在里面设置属性,但在NullPointerException上失败。

你需要将getter和setter放在你的@Embeddable类中,你的hashCode()和equals()方法将进入你在这里发布的类中看不到的那个类。

为了保存ContractServiceLocation,需要首先保存以下对象,因为您使用它们的ID作为ContractServiceLocation的复合键,对吧? 在这里你正在做的是你正在创建这些作为新对象,所以显然他们不会有他们的id,因为它们不会被持久化。 所以你需要先保留它们并使用持久化对象并将对象设置到ContractServiceLocation中。

  Service service = new Service(); // Set all service properties Contract contract = new Contract(); // Set all contract properties Location location = new Location(); // Set all location properties