级联映射

简单说,级联映射就是在保存瞬时状态的主对象的时候,同时将关联的处于瞬时状态的从对象一起持久化到数据库中,删除、更新也是如此。

级联映射类型 CascadeType 可选值如下:

  • CasecadeType.PERSIST:保存主对象的时候,同时将关联的对象持久化到数据库中;

  • CasecadeType.REFRESH:查询主对象的同时,重新查询关联的对象;

  • CasecadeType.REMOVE:删除主对象的同时,删除关联的对象;

  • CasecadeType.DETACH:在主对象变为游离对象的同时,将关联的对象也转换成游离对象;

  • CasecadeType.MERGE:在更新主对象的同时,更新关联的对象;

  • CasecadeType.ALL:包含以上所有的级联关系;

示例代码

由于订单和订单明细属于组合关系,这种关系往往需要用到级联映射,因为他们是在一个模块中进行管理的。下面将通过订单和订单明细的增删改来验证 OpenJPA 的级联映射。

(1)添加 persistence.xml 配置,如下:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             version="1.0">
    <persistence-unit name="demo_casecade" transaction-type="RESOURCE_LOCAL">
        <!-- JPA提供者 -->
        <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
        <!-- 声明实体类 -->
        <class>com.hxstrive.openjpa.annotation.demo_casecade.OrderBill</class>
        <class>com.hxstrive.openjpa.annotation.demo_casecade.OrderBillDetail</class>
        <!-- 配置JPA数据库属性 -->
        <properties>
            <property name="openjpa.ConnectionURL"
                      value="jdbc:mysql://localhost:3306/openjpa_learn?useSSL=false&amp;
                      serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="openjpa.ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
            <property name="openjpa.ConnectionUserName" value="root"/>
            <property name="openjpa.ConnectionPassword" value="aaaaaa"/>
            <property name="openjpa.Log" value="SQL=TRACE"/>
            <!-- 自动生成表 -->
            <property name="openjpa.jdbc.SynchronizeMappings"
                      value="buildSchema(ForeignKeys=true)"/>
            <!-- 不使用加载时强化和编译时强化,使用运行时Unenhanced(不能发挥OpenJPA的最大效能,所以也不推荐) -->
            <property name="openjpa.ClassLoadEnhancement" value="false"/>
            <property name="openjpa.DynamicEnhancementAgent" value="false"/>
            <property name="openjpa.RuntimeUnenhancedClasses" value="supported"/>
        </properties>
    </persistence-unit>
</persistence>

(2)定义订单实体对象,代码如下:

@Data
@Entity
@Table
public class OrderBill {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column
    private String user;

    @OneToMany(cascade = {
            CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE
        }, orphanRemoval = true)
    private List<OrderBillDetail> orderBillDetails;
}

(3)定义订单详情实体对象,代码如下:

@Data
@Entity
@Table
public class OrderBillDetail {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column
    private String productName;

    @Column
    private float price;

    public OrderBillDetail() {}

    public OrderBillDetail(String productName, float price) {
        this.productName = productName;
        this.price = price;
    }
}

上面已定义了数据库表对应的实体,以及 OpenJPA 配置信息,下面将分别介绍新增、修改和删除时的级联映射。

新增订单

下面将演示新增订单,代码如下:

private static void insertData(EntityManager em) {
    try {
        em.getTransaction().begin();

        List<OrderBillDetail> orderBillDetails = new ArrayList<>();
        orderBillDetails.add(new OrderBillDetail("Java编程思想", 87.5f));
        orderBillDetails.add(new OrderBillDetail("Java核心编程", 56.2f));

        OrderBill orderBill = new OrderBill();
        orderBill.setUser("张三");
        orderBill.setOrderBillDetails(orderBillDetails);

        // 这里仅仅持久化主对象
        em.persist(orderBill);

        em.getTransaction().commit();
    } finally {
        em.close();
    }
}

执行上面方法后,输出的 SQL 语句如下:

-- 创建订单和订单明细表
CREATE TABLE OrderBill (id INTEGER NOT NULL, user VARCHAR(255), PRIMARY KEY (id)) ENGINE = innodb
CREATE TABLE OrderBillDetail (id INTEGER NOT NULL, price REAL, productName VARCHAR(255), PRIMARY KEY (id)) ENGINE = innodb
CREATE TABLE OrderBill_OrderBillDetail (ORDERBILL_ID INTEGER, ORDERBILLDETAILS_ID INTEGER) ENGINE = innodb
-- 为中间表创建索引
CREATE INDEX I_RDRBDTL_ELEMENT ON OrderBill_OrderBillDetail (ORDERBILLDETAILS_ID)
CREATE INDEX I_RDRBDTL_ORDERBILL_ID ON OrderBill_OrderBillDetail (ORDERBILL_ID)
-- 获取数据ID
SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE [params=?]
INSERT INTO OPENJPA_SEQUENCE_TABLE (ID, SEQUENCE_VALUE) VALUES (?, ?) [params=?, ?]
SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE [params=?]
UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VALUE = ? [params=?, ?, ?]
SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID = ? FOR UPDATE [params=?]
UPDATE OPENJPA_SEQUENCE_TABLE SET SEQUENCE_VALUE = ? WHERE ID = ? AND SEQUENCE_VALUE = ? [params=?, ?, ?]
-- 插入订单数据
INSERT INTO OrderBill (id, user) VALUES (?, ?) [params=?, ?]
-- 插入订单详情数据表
INSERT INTO OrderBillDetail (id, price, productName) VALUES (?, ?, ?) [params=?, ?, ?]
INSERT INTO OrderBillDetail (id, price, productName) VALUES (?, ?, ?) [params=?, ?, ?]
-- 插入订单中间表数据
INSERT INTO OrderBill_OrderBillDetail (ORDERBILL_ID, ORDERBILLDETAILS_ID) VALUES (?, ?) [params=?, ?]
INSERT INTO OrderBill_OrderBillDetail (ORDERBILL_ID, ORDERBILLDETAILS_ID) VALUES (?, ?) [params=?, ?]

更新订单

下面将演示修改订单数据,代码如下:

private static void updateData(EntityManager em) {
    try {
        em.getTransaction().begin();

        // 根据ID查询订单信息
        OrderBill orderBill = em.find(OrderBill.class, 1);
        orderBill.setUser("update-" + orderBill.getUser());

        // 获取订单详情
        List<OrderBillDetail> orderBillDetailList = orderBill.getOrderBillDetails();
        for(OrderBillDetail orderBillDetail : orderBillDetailList) {
            orderBillDetail.setProductName("update-" + orderBillDetail.getProductName());
        }
        
        // 修改订单数据
        em.persist(orderBill);

        em.getTransaction().commit();
    } finally {
        em.close();
    }
}

执行上面方法后,输出的 SQL 语句如下:

-- 查询订单数据
SELECT t0.user FROM OrderBill t0 WHERE t0.id = ? [params=?]
-- 查询订单详情数据
SELECT t1.id, t1.price, t1.productName FROM OrderBill_OrderBillDetail t0 INNER JOIN OrderBillDetail t1 ON t0.ORDERBILLDETAILS_ID = t1.id WHERE t0.ORDERBILL_ID = ? [params=?]
-- 更新订单数据
UPDATE OrderBill SET user = ? WHERE id = ? [params=?, ?]
-- 更新订单详情数据
UPDATE OrderBillDetail SET productName = ? WHERE id = ? [params=?, ?]
UPDATE OrderBillDetail SET productName = ? WHERE id = ? [params=?, ?]

删除订单

下面将演示删除订单数据,代码如下:

private static void deleteData(EntityManager em) {
    try {
        em.getTransaction().begin();

        // 根据ID查询订单信息
        OrderBill orderBill = em.find(OrderBill.class, 1);
        // 删除订单数据
        em.remove(orderBill);

        em.getTransaction().commit();
    } finally {
        em.close();
    }
}

执行上面方法后,输出的 SQL 语句如下:

-- 查询订单数据
SELECT t0.user FROM OrderBill t0 WHERE t0.id = ? [params=?]
-- 查询订单详情数据
SELECT t1.id, t1.price, t1.productName FROM OrderBill_OrderBillDetail t0 INNER JOIN OrderBillDetail t1 ON t0.ORDERBILLDETAILS_ID = t1.id WHERE t0.ORDERBILL_ID = ? [params=?]
-- 删除订单中间表
DELETE FROM OrderBill_OrderBillDetail WHERE ORDERBILL_ID = ? [params=?]
-- 删除订单数据
DELETE FROM OrderBill WHERE id = ? [params=?]
-- 删除订单详情数据
DELETE FROM OrderBillDetail WHERE id = ? [params=?]
DELETE FROM OrderBillDetail WHERE id = ? [params=?]
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号