本文主要介紹Hibernate如何實現一對多、多對多表,實現相關操作。 ...
一、一對多關係
1.概念
一對多關係是關係型資料庫中兩個表之間的一種關係。通常在資料庫層級中,兩表之間是有主外鍵關係的。在ORM中,如何通過對象描述表之間的關係,是ORM核心。
2.Hibernate的一對多關聯映射【重點】
2.1表關係的分析
MySql語句
CREATE TABLE `t_category` ( `cid` int(11) NOT NULL AUTO_INCREMENT, `cname` varchar(255) DEFAULT NULL, PRIMARY KEY (`cid`) ); CREATE TABLE `t_product` ( `pid` int(11) NOT NULL AUTO_INCREMENT, `pname` varchar(255) DEFAULT NULL, `price` double DEFAULT NULL, `cid` int(11) DEFAULT NULL, PRIMARY KEY (`pid`), KEY `FKq8yr5sflwtcj3jqp58x0oy7lx` (`cid`), CONSTRAINT `FKq8yr5sflwtcj3jqp58x0oy7lx` FOREIGN KEY (`cid`) REFERENCES `t_category` (`cid`) );
2.2創建持久化類
-
Category.java(一的一方)
1 public class Category { 2 private Integer cid; 3 private String cname; 4 //在一的一方,用一個集合表示和product的關係 5 private Set<Product> products = new HashSet<Product>(); 6 7 public Integer getCid() { 8 return cid; 9 } 10 public void setCid(Integer cid) { 11 this.cid = cid; 12 } 13 public String getCname() { 14 return cname; 15 } 16 public void setCname(String cname) { 17 this.cname = cname; 18 } 19 public Set<Product> getProducts() { 20 return products; 21 } 22 public void setProducts(Set<Product> products) { 23 this.products = products; 24 } 25 }
-
Product.java(多的一方)
1 public class Product { 2 private Integer pid; 3 private String pname; 4 private double price; 5 //用一個對象表示,當前商品屬於哪個類別 6 private Category category; 7 8 public Integer getPid() { 9 return pid; 10 } 11 12 public void setPid(Integer pid) { 13 this.pid = pid; 14 } 15 16 public String getPname() { 17 return pname; 18 } 19 20 public void setPname(String pname) { 21 this.pname = pname; 22 } 23 24 public double getPrice() { 25 return price; 26 } 27 28 public void setPrice(double price) { 29 this.price = price; 30 } 31 32 public Category getCategory() { 33 return category; 34 } 35 36 public void setCategory(Category category) { 37 this.category = category; 38 } 39 40 }
2.3創建映射
-
分類的映射
<hibernate-mapping> <class name="com.pri.bean.Category" table="t_category"> <!--一,主鍵屬性 --> <id name="cid" column="cid"> <generator class="native"></generator> </id> <!-- 二,其它屬性 --> <property name="cname" column="cname"/> <!-- 三,表示和商品的關係 --> <!--3.1 set標簽的name屬性:多的一方的集合的名字 --> <set name="products"> <!--3.2 key的 column表示多的一方外鍵名 --> <key column="cid"/> <!--3.3 one-to-many的class屬性表示多的一方類的全限定名 --> <one-to-many class="com.pri.bean.Product"/> </set> </class> </hibernate-mapping>
-
商品的映射
<hibernate-mapping> <class name="com.pri.bean.Product" table="t_product"> <!--一,主鍵屬性 --> <id name="pid" column="pid"> <generator class="native"></generator> </id> <!-- 二,其它屬性 --> <property name="pname" column="pname"/> <property name="price" column="price"/> <!-- 三,表示和分類的關係 --> <!--3.1name:一的一方對象的名字 class: 一的一方類的全限定名 column:外鍵的名字 --> <many-to-one name="category" class="com.pri.bean.Category" column="cid"/> </class> </hibernate-mapping>
2.4將映射添加到配置文件
<!-- 三、載入映射關係配置 --> <mapping resource="com/pri/bean/Product.hbm.xml"/> <mapping resource="com/pri/bean/Category.hbm.xml"/>
2.5寫代碼測試
-
TestDb
public class TestDb { @Test public void fun01(){ Session session = HibernateUtils.openSession(); Transaction transaction = session.beginTransaction(); transaction.commit(); session.close(); } }
3.級聯操作
3.1概念
如果我們對某一個表進行了操作,那麼也會順帶對關聯表進行操作。或者說當對主對象進行某種操作時是也對其關聯的從對象也作類似的操作.
eg:如果我們刪除了一個公司,那麼就得要求,員工表也得把屬於這個公司的所有員工也刪除掉。
如果我們刪除了一個類別, 那麼該類別下的商品是否要刪除或者不刪除只刪除外鍵,那麼這樣的操作就屬於級聯操作了.
3.2 級聯保存和修改
3.2.1 操作一方級聯保存多方【掌握】
-
保存Category(一的一方)的時候級聯保存Product(多的一方)
-
應用場景: 保存一方的同時也需要保存多方(並且多方的數據比較多). eg: 訂單和訂單項
-
<!-- 三、表示和商品的關係 --> <!-- 3.1 set標簽的name屬性:多的一方的集合的名字 --> <set name="products" cascade="save-update"/> <!-- 3.2 key的column表示多的一方外鍵名 --> <key column="cid"/> <!-- 3.3 one-to-many的class屬性表示多的一方類的全限定名 --> <one-to-many class="com.pri.bean.Product"/> </set>
1 /** 2 * 只保存分類,通過級聯把當前分類下的商品也保存 3 * 配置: 在category.hbm.xml下設置: 4 * <set cascade="save-update"> 5 */ 6 @Test 7 public void fun02(){ 8 Session session = HibernateUtils.openSession(); 9 Transaction transaction = session.beginTransaction(); 10 11 Category c1 = new Category(); 12 c1.setCname("水果"); 13 14 Product p1 = new Product(); 15 p1.setPname("蘋果"); 16 p1.setPrice(5); 17 18 //c1和p1 產生關係 19 c1.getProducts().add(p1); 20 //p1和c1產生關係 21 p1.setCategory(c1); 22 session.save(c1); 23 //session.save(p1);不需要了,通過級聯保存特性自動保存 24 transaction.commit(); 25 session.close(); 26 }
3.2.2操作多方級聯保存一方【瞭解】
<!--三.表示和分類的關係 --> <!-- 3.1 name:一的一方對象的名字 class: 一的一方類的全限定名 column:外鍵的名字 --> <many-to-one name="category" class="com.pri.bean.Category" column="cid" cascade="save-update"/>
-
保存Product(多的一方的)的時候級聯保存Category(一的一方)
1 /** 2 * 只保存商品,通過級聯的特性保存商品對應的分類 3 * 配置: 在product.hbm.xml下設置: 4 * <many-to-one cascade="save-update"/> 5 */ 6 @Test 7 public void fun03(){ 8 Session session = HibernateUtils.openSession(); 9 Transaction transaction = session.beginTransaction(); 10 11 Category c1 = new Category(); 12 c1.setCname("水果"); 13 14 Product p1 = new Product(); 15 p1.setPname("蘋果"); 16 p1.setPrice(5); 17 18 //p1和c1發生關係 19 p1.setCategory(c1); 20 21 session.save(p1); 22 23 //session.save(c1); 不需要了 24 25 transaction.commit(); 26 session.close(); 27 }
3.3級聯刪除
3.3.1刪除一方級聯刪除多方【掌握】
-
刪除Category(一的一方)的時候級聯刪除Product(多的一方)
-
場景: 刪除一方的時候確保多方的數據沒有任何用途的時候的才會用. eg: 公司和員工
-
實際開發裡面很少刪除用戶的數據. 通常用一個欄位標識的
-
<!-- 三.表示和商品的關係 --> <!-- 3.1 set標簽的name屬性:多的一方的集合的名字 --> <set name="products" cascade="delete"> <!--3.2 key 的column表示多的一方外鍵名 --> <key column="cid"/> <!-- 3.3 one-to-many的class屬性表示多的一方類的全限定名 --> <one-to-many class="com.pri.bean.Product"/> </set>
-
1 @Test 2 //級聯刪除商品: 刪除id為1的類別,級聯刪除當前類別下的所有商品 3 //因為刪除的是類別,所有在Category.hbm.xml配置:<set name="products" cascade="delete"> 4 public void fun03(){ 5 Session session = HibernateUtils.getCurrentSession(); 6 Transaction transaction = session.beginTransaction(); 7 8 //先查 id為1的類別 9 Category category = session.get(Category.class, 1); 10 session.delete(category); 11 transaction.commit(); 12 }
3.3.2 刪除多方級聯刪除一方【瞭解】
-
刪除Product(多的一方)的時候級聯刪除Category(一的一方)
<!--三.表示和分類的關係 --> <!-- 3.1 name:一的一方對象的名字 class: 一的一方類的全限定名 column:外鍵的名字 --> <many-to-one name="category" class="com.pri.bean.Category" column="cid" cascade="delete"/>
1 @Test 2 //級聯刪除分類: 刪除id為1的商品,級聯刪除這個商品所屬的類別(瞭解) 3 //把當前id為1商品刪除,把這個商品所屬的類別刪除, 商品表裡面該類別其它的商品的記錄的外鍵置為null; 4 public void fun04(){ 5 Session session = HibernateUtils.getCurrentSession(); 6 Transaction transaction = session.beginTransaction(); 7 8 //先查 id為1的類別 9 Product product = session.get(Product.class, 1); 10 session.delete(product); 11 12 13 transaction.commit(); 14 }
4.外鍵維護
4.1 雙向關聯產生多餘的SQL語句
一旦存在主外鍵關係,那麼外鍵就由雙方對象去維護了, 那麼就會造成對這個外鍵執行了多次操作, 有一次操作其實可以避免的。 也就是說這個外鍵只要讓一個人去維護即可。 外鍵屬於誰,就讓誰去維護。
-
eg:修改產品對應的類別
4.2inverse標簽屬性
4.2.1概念
inverse
:外鍵維護,預設為false。代表一方不去維護多方外鍵。(inverse有反轉的意思)
外鍵屬於誰, 誰就負責維護.
4.2.2一方的放棄外鍵維護
-
只能在一的一方放棄外鍵維護
<set inverse="true"> ... </set>
-
EG:
-
<!-- 三.表示和商品的關係 --> <!-- 3.1 set標簽的name屬性:多的一方的集合的名字 --> <set name="products" cascade="delete" inverse="true"> <!--3.2 key 的column表示多的一方外鍵名 --> <key column="cid"/> <!-- 3.3 one-to-many的class屬性表示多的一方類的全限定名 --> <one-to-many class="com.pri.bean.Product"/> </set>
1 /** 2 * 把蘋果(p1)的類別設置為水果(c2) 3 * 衣服是(c1) 4 */ 5 @Test 6 public void fun02(){ 7 Session session = HibernateUtils.openSession(); 8 Transaction transaction = session.beginTransaction(); 9 //獲得蘋果 10 Product p1 = session.get(Product.class, 1); 11 //獲得食物類別 12 Category c2 = session.get(Category.class, 2); 13 14 //p1和c2關聯 15 p1.setCategory(c2); 16 //c2和p1關聯 17 c2.getProducts().add(p1); 18 19 transaction.commit(); 20 session.close(); 21 }
二、多對多的關係
1.概念
多對多關係是關係資料庫中兩個表之間的一種數據關係,為了維護這種關係,通常會存在一張中間關係表。兩張表都只和關係表間建立主外鍵關係。
2.Hibernate的多對多關聯映射【重點】
2.1表關係的分析
-
MySql語句
2.2創建實體
-
Student.java
1 public class Student { 2 3 private Integer sid; 4 private String sname; 5 6 private Set<Course> courses = new HashSet<Course>(); 7 8 public Integer getSid() { 9 return sid; 10 } 11 12 public void setSid(Integer sid) { 13 this.sid = sid; 14 } 15 16 public String getSname() { 17 return sname; 18 } 19 20 public void setSname(String sname) { 21 this.sname = sname; 22 } 23 24 public Set<Course> getCourses() { 25 return courses; 26 } 27 28 public void setCourses(Set<Course> courses) { 29 this.courses = courses; 30 } 31 32 33 34 }
-
Course.java
1 public class Course { 2 3 private Integer cid; 4 private String cname; 5 6 private Set<Student> students = new HashSet<Student>(); 7 8 public Integer getCid() { 9 return cid; 10 } 11 12 public void setCid(Integer cid) { 13 this.cid = cid; 14 } 15 16 public String getCname() { 17 return cname; 18 } 19 20 public void setCname(String cname) { 21 this.cname = cname; 22 } 23 24 public Set<Student> getStudents() { 25 return students; 26 } 27 28 public void setStudents(Set<Student> students) { 29 this.students = students; 30 } 31 32 }
2.3創建映射
-
學生的映射
1 <hibernate-mapping> 2 <class name="com.itheima.bean.Student" table="t_student"> 3 <!--一,主鍵屬性 --> 4 <id name="sid" column="sid"> 5 <generator class="native"></generator> 6 </id> 7 <!-- 二,其它屬性 --> 8 <property name="sname" column="sname"/> 9 10 <!-- 三,表示和課程的關係 --> 11 <!--3.1 set標簽的name屬性:當前類中集合的名字 12 table:第三方表名 13 --> 14 <set name="courses" table="s_c_tab"> 15 <!--3.2 key的 column表示當前類在中間表中的外鍵 --> 16 <key column="sid" /> 17 <!--3.3 many-to-many表示多對多關係 18 column:表示另一方在中間表中的外鍵 19 class:表示另一方類的全限定名 20 --> 21 <many-to-many column="cid" class="com.itheima.bean.Course" ></many-to-many> 22 </set> 23 </class> 24 </hibernate-mapping>
-
課程的映射
1 <hibernate-mapping> 2 <class name="com.itheima.bean.Course" table="t_course"> 3 <!--一,主鍵屬性 --> 4 <id name="cid" column="cid"> 5 <generator class="native"></generator> 6 </id> 7 <!-- 二,其它屬性 --> 8 <property name="cname" column="cname"/> 9 10 <!-- 三,表示和課程的關係 --> 11 <!--3.1 set標簽的name屬性:當前類中集合的名字 12 table:第三方表名 13 --> 14 <set name="students" table="s_c_tab"> 15 <!--3.2 key的 column表示當前類在中間表中的外鍵 --> 16 <key column="cid"/> 17 <!--3.3 many-to-many表示多對多關係 18 column:表示另一方在中間表中的外鍵 19 class:表示另一方類的全限定名 20 --> 21 <many-to-many column="sid" class="com.itheima.bean.Student"></many-to-many> 22 </set> 23 </class> 24 </hibernate-mapping>
2.4將映射添加到配置文件
<!-- 三.載入映射關係配置 --> <mapping resource="com/pri/bean/Student.hbm.xml"/> <mapping resource="com/pri/bean/Course.hbm.xml"/>
2.5寫代碼測試
-
保存
1 /** 2 * 正常保存 3 */ 4 @Test 5 public void fun01(){ 6 Session session = HibernateUtils.openSession(); 7 Transaction transaction = session.beginTransaction(); 8 9 Student s1 = new Student(); 10 s1.setSname("張三"); 11 Student s2 = new Student(); 12 s2.setSname("李四"); 13 14 Course c1 = new Course();