一、一對多雙向關聯與級聯操作
1、創建項目,配置文件代碼如下:
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <!-- name屬性用於定義持久化單元的名字 (name必選,空值也合法); transaction-type 指定事務類型(可選) --> <persistence-unit name="learn_jpa" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <!-- hibernate.dialect 指定數據庫的方言 --> <!-- <property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect"/> <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver"/> <property name="hibernate.connection.username" value="learn_orcl"/> <property name="hibernate.connection.password" value="learn_orcl"/> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521:learn_data?useUnicode=true&characterEncoding=UTF-8"/> <property name="hibernate.hbm2ddl.auto" value="update"/> --> <!-- hibernate.hbm2ddl.auto參數的作用主要用於:自動創建|更新|驗證數據庫表結構 --> <!-- create:每次加載hibernate時都會刪除上一次的生成的表,然后根據你的model類再重新來生成新表, 哪怕兩次沒有任何改變也要這樣執行,這就是導致數據庫表數據丟失的一個重要原因。 create-drop:每次加載hibernate時根據model類生成表,但是sessionFactory一關閉,表就自動刪除。 update:最常用的屬性,第一次加載hibernate時根據model類會自動建立起表的結構(前提是先建立好數據庫), 以后加載hibernate時根據 model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會刪除以前的行。 要注意的是當部署到服務器后,表結構是不會被馬上建立起來的,是要等 應用第一次運行起來后才會。 validate:每次加載hibernate時,驗證創建數據庫表結構,只會和數據庫中的表進行比較,不會創建新表, 但是會插入新值。 --> <properties> <!-- 數據庫方言 --> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" /> <!-- 數據庫驅動 --> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <!-- 數據庫用戶名 --> <property name="hibernate.connection.username" value="root" /> <!-- 數據庫密碼 --> <property name="hibernate.connection.password" value="123456" /> <!-- 數據庫連接URL --> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/learn_jpa?useUnicode=true&characterEncoding=UTF8"/> <!-- 最大抓取深度 --> <property name="hibernate.max_fetch_depth" value="3" /> <!-- 更新方式創建庫表 --> <property name="hibernate.hbm2ddl.auto" value="update" /> <!-- 顯示SQL --> <property name="hibernate.show_sql" value="false" /> <!-- 格式SQL --> <property name="hibernate.format_sql" value="true" /> </properties> </persistence-unit> </persistence>
2、創建訂單實體類,代碼如下:
package learn.jpa.entity; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; /** * 訂單 */ @Entity // 定義類為實體類 public class Order { private String orderid; private float amount = 0f; private Set<OrderItem> item = new HashSet<OrderItem>(); @Id // 實體標識符,因為是字符串類型,所有不能用 @GeneratedValue,只能人為的賦值 @Column(length=20) public String getOrderid() { return orderid; } public void setOrderid(String orderid) { this.orderid = orderid; } @Column(nullable = false) public float getAmount() { return amount; } public void setAmount(float amount) { this.amount = amount; } @OneToMany(cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE}) public Set<OrderItem> getItem() { return item; } public void setItem(Set<OrderItem> item) { this.item = item; } } /** * 1 - N * 多的一端為關系維護端,關系維護端負責外鍵記錄的更新 * */
3、創建訂單項實體類,代碼如下:
package learn.jpa.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * 訂單項 */ @Entity // 定義類為實體類 public class OrderItem { private int id; private String productName; private float sellPrice = 0f; private Order order; @Id // 實體標識符 @GeneratedValue // 主鍵自動增長 public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(length=40,nullable=false) public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } @Column(nullable=false) public float getSellPrice() { return sellPrice; } public void setSellPrice(float sellPrice) { this.sellPrice = sellPrice; } public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
注解:
1、@OneToMany(fetch=FetchType,cascade=CascadeType)
@OneToMany描述一個一對多的關聯,該屬性應該為集體類型,在數據庫中並沒有實際字段.
fetch:表示該屬性的讀取策略,有EAGER和LAZY兩種,分別表示主支抓取和延遲加載,默認為EAGER.
cascade:表示級聯操作策略,對於OneToMany類型的關聯非常重要,通常該實體更新或刪除時,其關聯的實體也應當被更新或刪除
(1)、CascadeType.MERGE級聯更新:若items屬性修改了那么order對象保存時同時修改items里的對象。對應EntityManager的merge方法
(2)、CascadeType.PERSIST級聯刷新:獲取order對象里也同時也重新獲取最新的items時的對象。對應EntityManager的refresh(object)方法有效。即會重新查詢數據庫里的最新數據
(3)、CascadeType.REFRESH級聯保存:對order對象保存時也對items里的對象也會保存。對應EntityManager的presist方法
(4)、CascadeType.REMOVE級聯刪除:對order對象刪除也對items里的對象也會刪除。對應EntityManager的remove方法
CascadeType.PERSIST只有A類新增時,會級聯B對象新增。若B對象在數據庫存(跟新)在則拋異常(讓B變為持久態)
CascadeType.MERGE指A類新增或者變化,會級聯B對象(新增或者變化)
CascadeType.REMOVE只有A類刪除時,會級聯刪除B類;
CascadeType.ALL包含所有;
綜上:大多數情況用CascadeType.MERGE就能達到級聯跟新又不報錯,用CascadeType.ALL時要斟酌下CascadeType.REMOVE
optional:是否允許該字段為null,該屬性應該根據數據庫表的外鍵約束來確定,默認為true
二、JPA中的一對多延遲加載與關系維護
1、訂單實體類,代碼:
package learn.jpa.entity; import java.util.HashSet; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; /** * 訂單 */ @Entity // 定義類為實體類 @Table(name="orders") public class Order { private String orderid; private float amount = 0f; private Set<OrderItem> item = new HashSet<OrderItem>(); @Id // 實體標識符,因為是字符串類型,所有不能用 @GeneratedValue,只能人為的賦值 @Column(length=20) public String getOrderid() { return orderid; } public void setOrderid(String orderid) { this.orderid = orderid; } @Column(nullable = false) public float getAmount() { return amount; } public void setAmount(float amount) { this.amount = amount; } /** * 如果是一對多或多對多 fetch 默認是延遲加載,反之是立即加載 * mappedBy="order" 表示由實體 OrderItem 中的 order 屬性維護 * @return */ @OneToMany(cascade={CascadeType.REFRESH,CascadeType.PERSIST,CascadeType.MERGE,CascadeType.REMOVE}, fetch=FetchType.LAZY,mappedBy="order") public Set<OrderItem> getItem() { return item; } public void setItem(Set<OrderItem> item) { this.item = item; } public void addOrderItem(OrderItem orderItem){ orderItem.setOrder(this); this.item.add(orderItem); } } /** * 1 - N * 多的一端為關系維護端,關系維護端負責外鍵記錄的更新 * */
mappedBy只有在雙向關聯時,才會使用這個屬性
mappedBy=”另一方的關系引用屬性”
2、訂單項實體類,代碼:
package learn.jpa.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; /** * 訂單項 */ @Entity // 定義類為實體類 public class OrderItem { private int id; private String productName; private float sellPrice = 0f; private Order order; @Id // 實體標識符 @GeneratedValue // 主鍵自動增長 public int getId() { return id; } public void setId(int id) { this.id = id; } @Column(length=40,nullable=false) public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } @Column(nullable=false) public float getSellPrice() { return sellPrice; } public void setSellPrice(float sellPrice) { this.sellPrice = sellPrice; } @ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},fetch=FetchType.EAGER,optional=false) @JoinColumn(name="order_id") public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } }
joinColumns屬性表示,在保存關系中的表中,所保存關聯關系的外鍵的字段。並配合@JoinColumn標記使用。
3、測試保存,代碼如下:
package learn.jpa.test; import static org.junit.Assert.*; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import learn.jpa.entity.Order; import learn.jpa.entity.OrderItem; import org.junit.Test; public class OneToManyTest { /** * 測試數據庫是否可以生成表 */ @Test public void test() { EntityManagerFactory factory = Persistence.createEntityManagerFactory("learn_jpa"); factory.close(); } @Test public void save(){ EntityManagerFactory factory = Persistence.createEntityManagerFactory("learn_jpa"); EntityManager em = factory.createEntityManager(); em.getTransaction().begin(); // 開啟事務 Order order = new Order(); order.setAmount(56f); order.setOrderid("SE001"); OrderItem item1 = new OrderItem(); item1.setProductName("足球"); item1.setSellPrice(32f); OrderItem item2 = new OrderItem(); item2.setProductName("羽毛球"); item2.setSellPrice(24f); order.addOrderItem(item1); order.addOrderItem(item2); em.persist(order); em.getTransaction().commit(); em.close(); factory.close(); } }