【问题标题】:JPA Composite key with ManyToOne getting org.hibernate.PropertyAccessException: could not set a field value by reflection setter ofJPA Composite key with ManyToOne getting org.hibernate.PropertyAccessException: could not set a field value by reflect setter of
【发布时间】:2014-05-19 11:20:08
【问题描述】:

我有一个复合键 ContractServiceLocationPK 由三个 id(contractIdlocationIdserviceId)组成,类型为可嵌入类中的 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


    【解决方案1】:

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

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

    @EmbeddedId ContractServiceLocationPK id;

    用这个:

    @EmbeddedId ContractServiceLocationPK id = new ContractServiceLocationPK();

    那么它应该可以工作。因为 Hibernate 试图在里面设置属性,但是在 NullPointerException 上失败。

    【讨论】:

    • 如果您使用 ContractServiceLocation 的构造函数初始化 ContractServiceLocationPK 也可以使用
    • 很高兴我找到了这个问题和这个答案。这是相当“令人惊讶”的。例如,EclipseLink 2.6.4 在相同的上下文中工作得很好。
    • 谢谢。可怕的情况是这样一个重要且有价值的库,hibernate,忽略了与连接表中的附加字段的多对多关系。这是一个核心的、基本的 RDBMS 设计组件。并且必须创建一个嵌入的 id(不要忘记实例化它)并经历如此多的阴谋才能使其工作是不真实的。如此典型的hibernate和对Java如此着迷的社区,在这种情况下,错过了RDBMS的基础......
    • 经过 3 天的搜索和阅读此答案(并验证这是我的情况)。 内心的尖叫
    【解决方案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
    

    【讨论】:

      【解决方案3】:

      为连接实体创建新实例时,应手动初始化@EmbeddedId 复合主键字段,因为 Hibernate 无法通过反射设置值

      所以在 ContractServiceLocation 构造函数中设置 ContractServiceLocationPK 复合类字段的值

      【讨论】:

        猜你喜欢
        • 2018-05-13
        • 2021-10-29
        • 2022-12-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-01
        相关资源
        最近更新 更多