JPA Composite key with ManyToOne getting org.hibernate.PropertyAccessException: 无法通过反射 setter 设置字段值

2022-09-03 03:37:01

我有一个复合键,由一个可嵌入类中长长的三个id(,,)组成。使用此复合键 的类使用注释将这些 id 映射到其对象。以下是它的外观(删除了 setter/getters 和不相关的属性):ContractServiceLocationPKcontractIdlocationIdserviceIdContractServiceLocation@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;
}

合同服务位置PK

@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更改为它们的类型,那么我得到。如果我尝试使用id类而不是可嵌入的,那么在Contract的OneToMany映射中,我得到了.如何制作具有多个映射的复合键?The type of the ID mapped by the relationship 'contract' does not agree with the primary key class of the target entity.mappedbyIn 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();

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

合同服务位置

@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()
}

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


答案 1

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

您必须在 ContractServiceLocation 类中内置 “ContractServiceLocationPK id”。替换行:

@EmbeddedId ContractServiceLocationPK id;

有了这个:

@EmbeddedId ContractServiceLocationPK id = new ContractServiceLocationPK();

然后它应该工作。因为 Hibernate 尝试在其中设置属性,但在 NullPointerException 上失败。


答案 2

你也需要把 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

推荐