使用ManyToOne获取org.hibernate.PropertyAccessException的JPA Composite键:无法通过反射设置器设置字段值

MrP*_*low 10 java hibernate jpa one-to-many many-to-one

我有一个复合键ContractServiceLocationPK做出来的三个ID.( ,,contractId 在嵌入类型长).使用此复合键的类使用注释将这些ID映射到其对象.这是它的样子(删除了setter/getters和不相关的属性):locationIdserviceIdContractServiceLocation@MapsId

合同

@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;
}
Run Code Online (Sandbox Code Playgroud)

ContractServiceLocationPK

@Embeddable
public class ContractServiceLocationPK implements Serializable {

    private long contractId;
    private long locationId;
    private long serviceId;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

当试图以任何方式(直接或通过合同)持久化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
Run Code Online (Sandbox Code Playgroud)

我的假设是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然后mappedby在Contract的OneToMany映射中得到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<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();
Run Code Online (Sandbox Code Playgroud)

它看起来像这样而不是在某些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()
}
Run Code Online (Sandbox Code Playgroud)

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()
}
Run Code Online (Sandbox Code Playgroud)

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

小智 23

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

您必须在ContractServiceLocation类中实例化"ContractServiceLocationPK id".替换线:

@EmbeddedId ContractServiceLocationPK id;

有了这个:

@EmbeddedId ContractServiceLocationPK id = new ContractServiceLocationPK();

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

  • 经过三天的搜索和阅读这个答案(并验证这是我的情况)。*内心的尖叫* (3认同)

Bil*_*ins 5

您还需要将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
Run Code Online (Sandbox Code Playgroud)