【问题标题】:JPA mapped model returns null elements with Composite-KeysJPA 映射模型返回具有复合键的空元素
【发布时间】:2011-03-07 22:18:57
【问题描述】:

我已经使用 JPA 构建了我的数据模型,并且正在使用 Hibernate 的 EntityManager 3 来访问数据。我使用 HSQLDB 进行测试(junit)。我将此配置用于其他类并且没有问题。

但是,最新一批表使用复合键作为主键,并且在实施时我无法从数据库中检索填充的行。我没有收到错误,查询只是返回空对象。

例如,如果我查询(使用 jsql)“FROM Order o”以返回表中所有订单的列表,我的 list.size() 具有正确数量的元素 (2),但元素为空。

我希望比我更敏锐的人能够辨别我做错了什么。提前致谢!

(简化的)表定义为:

CREATE TABLE member (
    member_id INTEGER NOT NULL IDENTITY PRIMARY KEY);

CREATE TABLE orders (
    orders_id INTEGER NOT NULL,
    member_id INTEGER NOT NULL,
    PRIMARY KEY(orders_id, member_id));

ALTER TABLE orders 
    ADD CONSTRAINT fk_orders_member 
    FOREIGN KEY (member_id) REFERENCES member(member_id);

实体 POJO 的定义如下:

@Entity
public class Member extends Person implements Model<Integer>{
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="MEMBER_ID", nullable=false)
    private Integer memberId;

    @OneToMany(fetch=FetchType.LAZY, mappedBy="member", cascade=CascadeType.ALL)
    private Set<Order> orderList;
}

@Entity
@Table(name="ORDERS")
@IdClass(OrderPK.class)
public class Order extends GeneralTableInformation implements Model<Integer>{

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ORDERS_ID", nullable=false)
    private Integer orderId;

    @Id
    @Column(name="MEMBER_ID", nullable=false)
    private Integer memberId;

    @ManyToOne(optional=false, fetch=FetchType.LAZY)
    @JoinColumn(name="MEMBER_ID", nullable=false)
    private Member member;

    @OneToMany(mappedBy="order", fetch=FetchType.LAZY)
    private Set<Note> noteList;
}

OrderPK 定义了一个默认构造函数和 2 个属性(orderId、memberId)以及它们的 get/set 方法。

public class OrderPK implements Serializable {
private static final long serialVersionUID = 1L;

private Integer orderId;
private Integer memberId;

public OrderPK() {}

public OrderPK(Integer orderId, Integer memberId) {
    this.orderId = orderId;
    this.memberId = memberId;
}

/**Getters/Setters**/

@Override
public int hashCode() {
    return orderId.hashCode() + memberId.hashCode(); 
}

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (!(obj instanceof OrderPK))
        return false;

    OrderPK other = (OrderPK) obj;
    if (memberId == null) {
        if (other.memberId != null) return false;
    } else if (!memberId.equals(other.memberId))
        return false;
    if (orderId == null) {
        if (other.orderId != null) return false;
    } else if (!orderId.equals(other.orderId))
        return false;
    return true;
}   

}

(见长见谅)

entityManager 在一个抽象类中实例化,然后由我的其他 DAO 扩展

protected EntityManager em;

@PersistenceContext
public void setEntityManager(EntityManager em) {
    this.em = em;
}

并由spring上下文配置文件配置

<bean id="entityManagerFactory" 
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    p:dataSource-ref="dataSource" 
    p:jpaVendorAdapter-ref="jpaAdapter">
    <property name="loadTimeWeaver">
        <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
    </property>
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
</bean>

我的测试课

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class OrderDaoTest {

    @Autowired
    protected OrderDao dao = null;

    @Test
    public void findAllOrdersTest() {
    List<Order> ol = dao.findAll();
    assertNotNull(ol); //pass
        assertEquals(2, ol.size(); //pass
        for (Order o : ol) {
            assertNotNull(o); //fail
            ...
        } 
    }
}

当我从 Order 类中删除复合键时,我能够检索数据,但我不确定我的映射或配置做错了什么。非常感谢任何帮助。

【问题讨论】:

    标签: java hibernate spring orm jpa


    【解决方案1】:

    OrderPK 定义了一个默认构造函数和 2 个属性(orderId、memberId)以及它们的 get/set 方法。

    我不确定这是否能解释一切,但是……这还不够。来自 JPA 1.0 规范:

    2.1.4 主键和实体标识

    ...

    以下规则适用于复合主键。

    • 主键类必须是公共的,并且必须有一个公共的无参数构造函数。
    • 如果使用基于属性的访问,则主键类的属性必须是公共的或受保护的。
    • 主键类必须是可序列化的。
    • 主键类必须定义equalshashCode 方法。这些方法的值相等语义必须与键映射到的数据库类型的数据库相等性一致。
    • 复合主键必须表示和映射为可嵌入类(参见第 9.1.14 节,“EmbeddedId 注释”),或者必须表示并映射到实体类的多个字段或属性(参见第 9.1.15 节, "IdClass 注释")。
    • 如果复合主键类映射到实体类的多个字段或属性,则主键类中的主键字段或属性的名称与实体类的名称必须对应且类型必须相同。

    您能否修复您的OrderPK 类以符合规范并重试?

    附带说明一下,我想知道您为什么在 Spring 配置中使用 loadTimeWeaver(因为您使用的是 Hibernate)。

    更新:我无法重现该问题...按预期工作。

    【讨论】:

    • 我的 OrderPK 类应该满足这些要求。我会将其发布在原始问题的更新中。
    • 我的理解是必须使用 loadTimeWeaver。我已将其删除,没有任何不良影响。谢谢。
    • @SethM:Hibernate 不依赖编织(与 TopLink 不同),但我认为这不是问题的一部分。不幸的是,我无法重现它。
    • 不让数据对象完全匹配数据库中的关系会是不好的做法吗?比如orderId其实是一个唯一值,但是orders表通过memberId与成员表有标识关系。应用中只使用orderId作为PK可以吗?
    【解决方案2】:

    在解决这个问题一段时间后,我了解到我在错误的类中配置了我的 Id 属性。

    原来我是在 Order 类中配置 orderId 和 memberId

    @Entity
    @Table(name="ORDERS")
    @IdClass(OrderPK.class)
    public class Order extends GeneralTableInformation implements Model<Integer>{
    
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        @Column(name="ORDERS_ID", nullable=false)
        private Integer orderId;
    
        @Id
        @Column(name="MEMBER_ID", nullable=false)
        private Integer memberId;
    

    但是,我了解到,如果您使用 IdClass 或 EmbeddedId,则必须为 ID 类中的 Id 列进行适当的字段注释。

    @Entity
    @Table(name="ORDERS")
    
    @IdClass(OrderPK.class)
    public class Order extends GeneralTableInformation implements Model<Integer>{
    
        @Id
        private Integer orderId;
    
        @Id
        private Integer memberId;
    }
    
    public class OrderPK implements Serializable {
        private static final long serialVersionUID = 1L;
    
        @Column(name="ORDERS_ID", nullable=false)
        private Integer orderId;
    
        @Column(name="MEMBER_ID", nullable=false)
        private Integer memberId;
    }
    

    通过此更改,我能够通过测试返回预期结果。

    【讨论】:

      【解决方案3】:

      我有同样的问题。我必须在现有数据库上创建后端,该数据库有一些没有主键和可空列的表(我知道!!)。这意味着此类表的 Hibernate 实体将具有由所有表列组成的复合主键。

      当我通过 JpaRepository 的 findAll 方法查询表行时,我会得到一个结果列表,其中一些结果为空值,而另一些则正常。

      我终于发现,如果复合键的某个字段为空,它会将整行渲染为空结果。

      【讨论】:

      • 在尝试接受的答案后,这是为我做的。因此,复合 id 中只有非空值是一个重要信息。
      猜你喜欢
      • 1970-01-01
      • 2016-03-08
      • 1970-01-01
      • 2013-12-24
      • 2017-02-21
      • 2019-04-22
      • 1970-01-01
      • 2011-03-20
      • 1970-01-01
      相关资源
      最近更新 更多