【发布时间】:2014-05-19 11:20:08
【问题描述】:
我有一个复合键 ContractServiceLocationPK 由三个 id(contractId、locationId、serviceId)组成,类型为可嵌入类中的 long。使用此复合键 ContractServiceLocation 的类使用 @MapsId 注释将这些 id 映射到它们的对象。下面是它的样子(删除了 setter/getter 和不相关的属性):
合同
@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;
}
合同服务位置
@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 类而不是可嵌入的,那么在 Contract 的 OneToMany 映射中mappedby 我得到In attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.。我应该怎么做才能创建具有多个 ManyToOne 映射的复合键?
编辑:添加了一个 sn-p,我尝试在其中保留项目:
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();
之所以看起来像这样,而不是在某个 DAO 中,是因为我要先生成数据库并测试项目,然后再继续开发应用程序的其余部分。
编辑 2: 我已经重写了我的模型,现在似乎一切正常,除了在 Eclipse 中我得到一个持续性错误。以下是目前的情况:
合同 - 没有变化(除了删除 Eager 加载)
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()
}
合同服务位置
@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 中,@OneToMany 注释上的 ContractServiceLocation 集合上的 mappedBy 带有错误消息 In attribute 'contractServiceLocation', the "mapped by" attribute 'contract' has an invalid mapping type for this relationship.。我假设这是因为ContractServiceLocation 中定义的Contract 属性没有@ManyToOne 注释,但它是在复合类中定义的。我是否偶然发现了“不符合 JPA 但使用 Hibernate”的陷阱或这里发生了什么?
【问题讨论】:
标签: java hibernate jpa one-to-many many-to-one