Bootstrap

JPA实战:嵌入式键与基本类型值的Map映射

在Java持久化API(JPA)中,@ElementCollection 注解为我们提供了强大的功能,能够将包含嵌入式键和基本类型值的Map进行持久化。这种映射方式在实际开发中非常实用,尤其是在需要将复杂对象作为键存储到数据库时。接下来,我们将通过一个具体的实例,深入探讨如何实现这种映射,并分析其背后的原理。
一、基本概念
在JPA中,当我们需要将一个Map存储到数据库时,通常会使用 @ElementCollection 注解。如果Map的键是嵌入式对象(@Embeddable),而值是基本类型(如字符串、整数等),那么JPA会将实体和Map映射到两个独立的表中。一个表用于存储实体本身,另一个表(连接表)用于存储嵌入式字段/属性(作为Map的键)以及单列的Map值。连接表会有一个外键指向实体表的主键,同时嵌入式字段对应的列也会有主键约束。
二、嵌入式类的要求
在使用嵌入式对象作为Map键时,嵌入式类必须实现 hashCode 和 equals 方法。这是因为Map的键需要通过这些方法来确保唯一性。这是Map键的一个通用要求,无论是否使用JPA。
三、默认命名约定与自定义
JPA默认会使用其自身的命名约定来生成表和列的名称。然而,我们可以通过 @JoinTable 和/或 @Column 注解来自定义这些名称。这为我们提供了更大的灵活性,以满足不同的数据库设计需求。
四、实例演示

  1. 实体类与嵌入式类定义
    java复制
    @Entity
    public class Customer {
    @Id
    @GeneratedValue
    private int id;
    private String name;

    @ElementCollection
    private Map<Order, String> orders;

    // 省略构造方法、getter和setter方法
    }

@Embeddable
public class Order {
private String item;
private int qty;
private Date date;

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Order order = (Order) o;
    return qty == order.qty && Objects.equals(item, order.item) && Objects.equals(date, order.date);
}

@Override
public int hashCode() {
    return Objects.hash(item, qty, date);
}

// 省略构造方法、getter和setter方法

}
2. 数据库表结构
通过默认的JPA映射,数据库中会生成以下表结构:
Customer 表:
ID:主键,整型。
NAME:字符串类型。
CUSTOMER_ORDERS 表:
CUSTOMER_ID:外键,指向 Customer 表的主键。
ORDERS:字符串类型,存储Map的值。
DATE:时间戳类型。
ITEM:字符串类型。
QTY:整型。
3. 数据持久化与查询
java复制
public class ExampleMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(“example-unit”);
try {
persistEntity(emf);
runNativeQueries(emf);
loadEntity(emf);
} finally {
emf.close();
}
}

private static void persistEntity(EntityManagerFactory emf) {
    System.out.println("-- Persisting entities --");
    EntityManager em = emf.createEntityManager();
    Customer c1 = new Customer();
    c1.setName("Lindsey Craft");
    c1.addOrder("online", "XYZ Blender", 2);
    c1.addOrder("store", "ZZZ Beer Glass", 4);

    Customer c2 = new Customer();
    c2.setName("Morgan Philips");
    c2.addOrder("online", "AA Glass Cleaner", 3);

    em.getTransaction().begin();
    em.persist(c1);
    em.persist(c2);
    em.getTransaction().commit();
    em.close();
}

private static void runNativeQueries(EntityManagerFactory emf) {
    System.out.println("-- Native queries --");
    EntityManager em = emf.createEntityManager();
    ExampleMain.nativeQuery(em, "Select * from Customer");
    ExampleMain.nativeQuery(em, "Select * from Customer_Orders");
}

private static void loadEntity(EntityManagerFactory emf) {
    System.out.println("-- Loading Customer --");
    EntityManager em = emf.createEntityManager();
    List<Customer> entityAList = em.createQuery("Select t from Customer t").getResultList();
    entityAList.forEach(System.out::println);
    em.close();
}

}
4. 输出结果
plaintext复制
– Persisting entities –
Customer{id=0, name=‘Lindsey Craft’, orders={Order{item=‘XYZ Blender’, qty=2, date=Sat Jun 10 11:12:52 CDT 2017}=online, Order{item=‘ZZZ Beer Glass’, qty=4, date=Sat Jun 10 11:12:52 CDT 2017}=store}}
Customer{id=0, name=‘Morgan Philips’, orders={Order{item=‘AA Glass Cleaner’, qty=3, date=Sat Jun 10 11:12:52 CDT 2017}=online}}

– Native queries –
‘Select * from Customer’
[1, Lindsey Craft]
[2, Morgan Philips]
‘Select * from Customer_Orders’
[1, online, 2017-06-10 11:12:52.727, XYZ Blender, 2]
[1, store, 2017-06-10 11:12:52.727, ZZZ Beer Glass, 4]
[2, online, 2017-06-10 11:12:52.73, AA Glass Cleaner, 3]

– Loading Customer –
Customer{id=1, name=‘Lindsey Craft’, orders={Order{item=‘XYZ Blender’, qty=2, date=2017-06-10 11:12:52.727}=online, Order{item=‘ZZZ Beer Glass’, qty=4, date=2017-06-10 11:12:52.727}=store}}
Customer{id=2, name=‘Morgan Philips’, orders={Order{item=‘AA Glass Cleaner’, qty=3, date=2017-06-10 11:12:52.73}=online}}
五、总结
通过上述实例,我们清晰地看到了如何在JPA中使用 @ElementCollection 注解将包含嵌入式键和基本类型值的Map进行持久化。这种方式不仅能够满足复杂的业务需求,还能保持代码的简洁性和可维护性。同时,我们还了解到了嵌入式类的 hashCode 和 equals 方法的重要性,以及如何通过注解来自定义表和列的名称。希望这篇文章能帮助你在实际开发中更好地应用JPA的这一强大功能。

;