具有ManyToOne的JPA组合键获取org.hibernate.PropertyAccessException:无法通过反射设置器设置字段值 [英] JPA Composite key with ManyToOne getting org.hibernate.PropertyAccessException: could not set a field value by reflection setter of
问题描述
我有一个由三个id( contractId
, locationId)组成的组合键
, ContractServiceLocationPK
serviceId
)在可嵌入类中的long类型。使用这个组合键的类 ContractServiceLocation
,使用 @MapsId
注释将这些id映射到它们的对象。下面是它的样子(删除了setter / getters和不相关的属性):
合同
@Entity
@Table(name =Contract)
public class Contract implements Serializable {
public Contract(){
}
@Id
@GeneratedValue
私人长ID;
@OneToMany(mappedBy =contract,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
Collection< ContractServiceLocation> contractServiceLocation;
}
ContractServiceLocationPK
@Embeddable
public class ContractServiceLocationPK实现Serializable {
private long contractId;
private long locationId;
私人长期serviceId;
}
ContractServiceLocation
@Entity
@Table(name =Contract_Service_Location)
public class ContractServiceLocation实现Serializable {
@EmbeddedId
ContractServiceLocationPK id ;
@ManyToOne(cascade = CascadeType.ALL)
@MapsId(contractId)
合约;
@ManyToOne(cascade = CascadeType.ALL)
@MapsId(locationId)
位置位置;
@ManyToOne(cascade = CascadeType.ALL)
@MapsId(serviceId)
服务服务;
BigDecimal价格;
}
当试图以任何方式持续保存一个类型为ContractServiceLocation的对象时(直接或通过合同)我得到:
线程main中的异常javax.persistence.PersistenceException:org.hibernate.PropertyAccessException:无法设置在org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
。通过com.test.model.ContractServiceLocationPK.contractId
的反射设定部字段值在org.hibernate.jpa。 spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
处org.hibernate.jpa.spi.AbstractEntityManagerImpl org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
。 persist(AbstractEntityManagerImpl.java:1187)
at com.test.MainTest.main(MainTest.java:139)
引起:org.hibernate.PropertyAccessException:无法通过反射设置字段值setter com.test.model.Contract ServiceLocationPK.contractId $在org.hibernate.property.DirectPropertyAccessor $ DirectSetter.set(DirectPropertyAccessor.java:134)
在org.hibernate.mapping.Component $ ValueGenerationPlan.execute(Component.java:441)
b $ b。在在org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:117)
org.hibernate.id.CompositeNestedGeneratedValueGenerator.generate(CompositeNestedGeneratedValueGenerator.java:121)
。在组织。 hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:84)
在org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:206)
。在组织。 hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:149)
在org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
。在org.hibernate.internal。硒(org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:811)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:784)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java: 789)
在org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1181)
... 1 more
引起: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变量,但是如果我将变量嵌入long中变为它们的类型,那么我得到由关系'合同'映射的ID不符合主表ry目标实体的关键类。
。如果我尝试使用id类而不是可嵌入的,那么在Contract的OneToMany映射中 mappedby
我得到在属性'contractServiceLocation'中,mapped by属性'contract '这个关系有一个无效的映射类型。
。我应该怎么做一个具有多个 ManyToOne
映射的组合键?
编辑:添加了一个片段,我尝试坚持这些项目:
Service service = new Service();
//设置所有服务属性
Contract contract = new Contract();
//设置所有合约属性
位置位置= new Location();
//设置所有位置属性
ContractServiceLocation csl = new ContractServiceLocation();
csl.setContract(contract);
csl.setLocation(location);
csl.setService(service);
Collection< ContractServiceLocation> cslItems = new ArrayList<>();
cslItems.add(csl);
em.getTransaction()。begin();
em.persist(location);
em.persist(service);
em.persist(csl);
em.persist(合约);
em.getTransaction()。commit();
它看起来像这样而不是在某些DAO中的原因是因为我正在生成数据库并且在开始开发应用程序的其余部分之前先测试这些项目。
编辑2:我重写了我的模型,似乎工作,除了在Eclipse中我得到一个持久性错误。以下是目前的情况:
合约 - 无变化(除去Eager加载)
ContractServiceLocationPK - 现在是一个ID类
public class ContractServiceLocationPK实现Serializable {
@ManyToOne(cascade = CascadeType。 ALL,fetch = FetchType.EAGER)
@JoinColumn(name =contract_id)
私人合约合约;
@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinColumn(name =location_id)
私人位置位置;
@ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@JoinColumn(name =service_id)
私人服务服务;
$ b $ // getters和setters
//覆盖equals()和hashCode()
}
ContractServiceLocation
@Entity
@Table(name =Contract_Service_Location )
@IdClass(ContractServiceLocationPK.class)
public class ContractServiceLocation实现Serializable {
@Id
合约;
@Id
位置位置;
@Id
服务服务;
BigDecimal价格;
// getters和setters
//覆盖equals()和hashCode()
}
现在看起来可以正常工作。它创建一个组合键并与所有组合属性保持多对一的关系。然而有些奇怪。在合约eclipse中, mappedBy
在 @OneToMany
批注中为 ContractServiceLocation
收集错误消息在属性'contractServiceLocation'中,映射的属性'contract'具有该关系的无效映射类型
。我假设这是因为 ContractServiceLocation
中定义的 Contract
属性没有 @ManyToOne
注释,但是它是在复合类中定义的。我偶然发现不兼容的JPA,但使用Hibernate陷阱或这里发生了什么? 解决方案
(未修改变体):
您必须在ContractServiceLocation类中创建ContractServiceLocationPK id。替换行:
@EmbeddedId
ContractServiceLocationPK id;
与此:
@EmbeddedId
ContractServiceLocationPK id = new ContractServiceLocationPK();
然后它应该可以工作。因为Hibernate试图在内部设置属性,但在NullPointerException中失败。
I have a composite key ContractServiceLocationPK
made out of three id's (contractId
, locationId
, serviceId
) of type long in an embeddable class. The class which uses this composite key, ContractServiceLocation
, maps these ids, using @MapsId
annotation, to their objects. Here's how it looks like (removed setters/getters and irrelevant properties):
Contract
@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> 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;
}
When attempting to persist an object of type ContractServiceLocation in any way(directly or throught contract) I get:
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
My assumption is that JPA/Hibernate expects a Contract object instead of a long variable, but if I change the variables in embeddable from long to their type then I get The type of the ID mapped by the relationship 'contract' does not agree with the primary key class of the target entity.
. If I try using id class instead of embeddable then mappedby
in Contract's OneToMany mapping I get In attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.
. What should I do to make a composite key with multiple ManyToOne
mappings?
EDIT: Added a snippet where I try to persist the items:
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<ContractServiceLocation> cslItems = new ArrayList<>();
cslItems.add(csl);
em.getTransaction().begin();
em.persist(location);
em.persist(service);
em.persist(csl);
em.persist(contract);
em.getTransaction().commit();
The reason it looks like this instead of being in some DAO is because I'm generating the database and testing the items first before I get on with developing the rest of the app.
EDIT 2: I've rewrote my models and now everything seems to work except in Eclipse I get a persistent error. Here's how the things currently look:
Contract - No change (Except removed the Eager loading)
ContractServiceLocationPK - Is now an ID class
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()
}
This appears to work correctly for now. It creates a composite key and maintains a many-to-one relationship with all the composite properties. However there is something weird. In Contract eclipse marks mappedBy
on the @OneToMany
annotation for the ContractServiceLocation
collection with the error message In attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.
. I'm assuming that this is because the Contract
property defined in ContractServiceLocation
doesn't have a @ManyToOne
annotation, but that is defined in the composite class. Did I stumble upon "non-compliant JPA but working with Hibernate" trap or what's going on here?
For your original question (not modified variant):
You have to instatiate "ContractServiceLocationPK id" in your ContractServiceLocation class. Replace line:
@EmbeddedId ContractServiceLocationPK id;
with this:
@EmbeddedId ContractServiceLocationPK id = new ContractServiceLocationPK();
Then it should works. Because Hibernate is trying to set properties inside, but fail on NullPointerException.
这篇关于具有ManyToOne的JPA组合键获取org.hibernate.PropertyAccessException:无法通过反射设置器设置字段值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!