简单说,级联映射就是在保存瞬时状态的主对象的时候,同时将关联的处于瞬时状态的从对象一起持久化到数据库中,删除、更新也是如此。
级联映射类型 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& serverTimezone=UTC&useUnicode=true&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=?]