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