JPA映射模型使用Composite-Keys返回null元素

时间:2010-06-24 16:56:57

标签: java hibernate spring orm jpa

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

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

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

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

(简化)表定义为:

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类中删除复合键时,我能够检索数据,我不确定我的映射或配置错误。非常感谢任何帮助。

3 个答案:

答案 0 :(得分:4)

在解决这个问题一段时间之后,我了解到我在错误的类中配置了我的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;
}

通过此次更改,我可以通过测试返回预期结果。

答案 1 :(得分:0)

  

OrderPK定义了一个默认构造函数和2个属性(orderId,memberId)及其get / set方法。

我不确定这会解释一切,但是......这还不够。从JPA 1.0规范:

  

2.1.4主键和实体标识

     

...

     

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

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

您能否修好OrderPK课程以符合规范并重试?

作为旁注,我想知道你为什么在Spring配置中使用loadTimeWeaver(因为你正在使用Hibernate)。

更新:我无法重现这个问题......对我来说是预期的。

答案 2 :(得分:0)

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

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

我最终发现,如果复合键的某些字段为null,则它将整个行呈现为null结果。