2.一、映射簡介java
對於全部的對象實體而言,有以下三種關係:程序員
1:1,1:n,m:nsql
一種雙向,一種單向。數據庫
2.二、一對多映射網絡
多對一單向:many-to-one單向,指的是在多的這一端增長關聯。session
配置文件的寫法:數據結構
<hibernate-mapping package="org.pm.hibernate.model"> <class name="Student" table="t_stu"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="no"/> <!-- many-to-one用來映射多對一,name表示對象中的屬性名稱,column用來表示數據庫中的外鍵的名稱 --> <!-- 當設置了cascade的時候,會自動完成關聯,若是添加時沒有關聯對象,會自動建立一個關聯對象 --> <!-- 最佳實踐:若是沒有特殊狀況,不要使用cascade,特別注意:可能使用cascade的地方 通常都是1的一方進行刪除時使用,特殊需求才會使用cascade的add, 正常狀況add方法都是應該由程序員完成添加 --> <many-to-one name="classroom" column="cid"/> </class> </hibernate-mapping>
一、添加方式1:先建立many再建立oneapp
//先添加many的一方以後才添加one的一方 /** * 建立兩個學生,而且保存 */ Student stu1 = new Student(); stu1.setName("小紅帽"); stu1.setNo("003"); session.save(stu1); Student stu2 = new Student(); stu2.setName("小紅帽"); stu2.setNo("004"); session.save(stu2); /** * 建立班級對象而且保存 */ Classroom c = new Classroom(); c.setGrade(2015); c.setName("計算機應用技術"); session.save(c); //設置兩個學生的班級信息,此時因爲是持久化對象,被session所管理,因此會發出兩條update stu1.setClassroom(c); stu2.setClassroom(c); //這個例子的問題就是:先發出了3條insert以後又會發出2條update //最佳實踐:必定要先添加一的一方,以後再添加多的一方 session.getTransaction().commit();
二、添加方式2:先建立one以後再建立many,這種是最佳實踐。hibernate
//先添加one的一方以後才添加many的一方 Classroom c = new Classroom(); c.setGrade(2015); c.setName("計算機網絡技術"); session.save(c); Student stu1 = new Student(); stu1.setClassroom(c); stu1.setName("小明"); stu1.setNo("001"); session.save(stu1); Student stu2 = new Student(); stu2.setClassroom(c); stu2.setName("小明"); stu2.setNo("001"); session.save(stu2); //只會發出3條insert語句,因此最佳實踐就是先建立one以後才建立many session.getTransaction().commit();
三、load和get和延遲加載計算機網絡
session = HibernateUtil.openSession(); session.beginTransaction(); //load會存在延遲加載 Student stu = session.load(Student.class, 1); //此時僅僅只是發一條sql System.out.println(stu.getName()); //many-to-one也存在延遲加載問題,只有在使用到one這一方時才發出sql查詢 //此時student的關聯對象classroom也是延遲加載的,會再發一條sql來取對象 System.out.println(stu.getClassroom().getName()); session.getTransaction().commit();
session = HibernateUtil.openSession(); session.beginTransaction(); Student stu = session.get(Student.class, 1); //get只是對本身不會進行延遲加載,可是在進行many-to-one時也會對one的對象進行延遲加載 System.out.println(stu.getName()); //對於get而言,若是要加載one的一方依然和load同樣,都是有延遲加載 //此時student的關聯對象classroom也是延遲加載的,會再發一條sql來取對象 System.out.println(stu.getClassroom().getName()); session.getTransaction().commit();
四、更新
session = HibernateUtil.openSession(); session.beginTransaction(); //使用離線對象進行更新 Student stu = new Student(); stu.setId(1); stu.setName("張三"); Classroom cla = new Classroom(); cla.setId(2); stu.setClassroom(cla); session.update(stu); session.getTransaction().commit();
session = HibernateUtil.openSession(); session.beginTransaction(); //使用持久化對象進行更新 Student stu = session.load(Student.class, 3); Classroom cla = new Classroom(); cla.setId(1); stu.setClassroom(cla); session.getTransaction().commit();
五、cascade
<!-- 當設置了cascade的時候,會自動完成關聯,若是添加時沒有關聯對象,會自動建立一個關聯對象 --> <!-- 最佳實踐:若是沒有特殊狀況,不要使用cascade,特別注意:可能使用cascade的地方 通常都是一的一方進行刪除時使用,特殊需求才會使用cascade的add, 正常狀況add方法都是應該由程序員完成添加 --> <many-to-one name="classroom" column="cid" cascade="all"/>
session = HibernateUtil.openSession(); session.beginTransaction(); Classroom c = new Classroom(); c.setGrade(2015); c.setName("計算機信息管理"); //此時classroom沒有存儲,因此在添加student的時候沒有外鍵,會拋出異常 //若是在many-to-one的配置選項中設置cascade="all"以後就能夠完成級聯更新 //可是不建議這樣使用 Student stu1 = new Student(); stu1.setName("小三"); stu1.setNo("005"); session.save(stu1); Student stu2 = new Student(); stu2.setName("小四"); stu2.setNo("006"); session.save(stu2); stu1.setClassroom(c); stu2.setClassroom(c); session.getTransaction().commit();
六、delete
session = HibernateUtil.openSession(); session.beginTransaction(); //若是使用了cascade在刪除stu時會自動級聯刪除classroom //而classroom仍是外鍵,沒法刪除,會拋出異常 Student stu = session.load(Student.class, 7); session.delete(stu); session.getTransaction().commit();
one-to-many單向
在one的這個對象中插入一個many的列表:
<!-- name="comments"對應實體類中的屬性名稱 --> <set name="comments"> <!-- key用來指定在對方表中的外鍵的名稱 --> <key column="mid"/> <!-- class用來設置列表中的對象類型 --> <one-to-many class="Comment"/> </set>
使用one-to-many來維護關係時,效率不高
session = HibernateUtil.openSession(); session.beginTransaction(); Student stu1 = new Student(); stu1.setName("張三"); stu1.setNo("007"); session.save(stu1); Student stu2 = new Student(); stu2.setName("趙四"); stu2.setNo("009"); session.save(stu2); Classroom cla = new Classroom(); cla.setGrade(2018); cla.setName("計算機應用技術"); Set<Student> stus = new HashSet<Student>(); stus.add(stu1); stus.add(stu2); cla.setStudents(stus); session.save(cla); //此時會發出5條sql,3條insert,2條update,效率不高 //因此使用one的這一方來維護關係很是不推薦 //以上寫法能夠完成一種改進,在Classroom對象中添加一個addStudent方法 session.getTransaction().commit();
session = HibernateUtil.openSession(); session.beginTransaction(); Comment c1 = new Comment(); c1.setContent("記事本1"); Comment c2 = new Comment(); c2.setContent("記事本2"); session.save(c1); session.save(c2); Message msg = new Message(); msg.setTitle("開大會"); msg.setContent("你們開始暢所欲言"); msg.addComment(c1); msg.addComment(c2); session.save(msg); //在Message對象中建立一個addComment的方法來添加Comment,這樣靈活性會高一些 //可是依然也會發出5條sql,3條插入,2條更新,效率沒有獲得提升 session.getTransaction().commit();
特別注意:oneToMany在添加和維護關係時比較麻煩,因此在開發過程當中不建議使用oneToMany的單向
最佳實踐:不要在one的這一方來維護關係。
二、load:在進行load時,若是僅僅只是爲了取出many這一端的數據,默認狀況也會發出查找list的sql語
句,這樣效率不高,因此在映射文件的set中能夠設置lazy屬性爲extra,會自動根據咱們查找的內容發
出不一樣的sql語句,效率會高一些。
<!-- 使用了lazy="extra"以後會稍微智能一些,會根據取的值的不一樣來判斷是調用count仍是獲取投影 --> <set name="comments" lazy="extra"> <!-- key用來指定在對方的外鍵的名稱 --> <key column="mid"/> <!-- class用來設置列表中的對象類型 --> <one-to-many class="Comment"/> </set>
Message msg = session.load(Message.class, 1); //執行了2條sql,把值輸出 //僅僅只是取出size而且設置了lazy="extra",此時會發出 //select count(id) from t_comment where mid =? System.out.println(msg.getComments().size()); session.getTransaction().commit();
Message msg = session.load(Message.class, 1); System.out.println(msg.getContent()); //此時要取出的是數據,會自動發出select xxx,xxx的list的語句 for(Comment c:msg.getComments()) { System.out.println(c.getContent()); } session.getTransaction().commit();
最佳實踐:在set中設置屬性lazy="extra"。
雙向關聯:就是把以上二者結合起來。
<hibernate-mapping package="org.pm.hibernate.model"> <class name="Classroom" table="t_cla"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="grade"/> <!-- 1這一方 --> <set name="students" lazy="extra" inverse="true"> <key column="cid"/> <one-to-many class="Student"/> </set> </class> </hibernate-mapping>
<hibernate-mapping package="org.pm.hibernate.model"> <class name="Student" table="t_stu"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="no"/> <!-- 多的一方 --> <many-to-one name="classroom" column="cid"/> </class> </hibernate-mapping>
<key column="cid"/>與<many-to-one column="cid">兩個外鍵最好保持一致,能夠不一致,若是設置爲不一
致很難維護關係。
一、add
session = HibernateUtil.openSession(); session.beginTransaction(); //先建立1的這一端的對象 Classroom cla = new Classroom(); cla.setGrade(2018); cla.setName("計算機網絡技術"); session.save(cla); //建立n的這一端 Student stu1 = new Student(); stu1.setName("張三"); stu1.setNo("007"); stu1.setClassroom(cla); session.save(stu1); Student stu2 = new Student(); stu2.setName("趙四"); stu2.setNo("009"); stu2.setClassroom(cla); session.save(stu2); //此時只會發出3條sql session.getTransaction().commit();
這是一種很是正常的操做(實際開發中的操做)。
二、update
<!-- inverse="true"表示不在本身這一端維護關係 --> <set name="students" lazy="extra" inverse="true"> <key column="cid"/> <one-to-many class="Student"/> </set>
session = HibernateUtil.openSession(); session.beginTransaction(); /** * 由於cla是離線對象Set爲null,此時當進行更新操做的時候 * 會清空全部的Student對象。 * 再次證實不該該容許在1的這一方來維護關係。 * 能夠經過在1的這一方設置一個inverse="true"的屬性強制要求本身不維護關係,而由對方維護。 */ Classroom cla = new Classroom(); cla.setId(4); cla.setName("計算機科學與技術"); cla.setGrade(2018); session.update(cla); //設置了inverse="true",此時不會由Classroom來維護關係,Set中的內容不會更新 session.getTransaction().commit();
最佳實踐:在雙向關聯中,在set標籤中設置inverse="true"來代表本身不維護關係。
三、load
//此處發2條sql:1條取Student,1條取Classroom,這裏的Classroom的id是Student //爲13所對應的Classroom的id爲6 //已經把id爲6的Classroom放到session中 Student stu = session.load(Student.class, 13); System.out.println(stu.getName()+","+stu.getClassroom().getGrade());
Message msg = session.load(Message.class, 1); //執行了2條sql,把值輸出 //僅僅只是取出size而且設置了lazy="extra",此時會發出 //select count(id) from t_comment where mid =? System.out.println(msg.getComments().size());
//此處發2條sql:1條取Student,1條取Classroom,這裏的Classroom的id是Student //爲13所對應的Classroom的id爲6 //已經把id爲6的Classroom放到session中 Student stu = session.load(Student.class, 13); System.out.println(stu.getName()+","+stu.getClassroom().getGrade()); Classroom cla = session.load(Classroom.class, 6); System.out.println(cla.getStudents().size()); //會發出select count(id),由於set設置了lazy="extra" for(Student s:cla.getStudents()) { //此處會發出1條sql:由於id爲6的Classroom已經在session的管理中, //因此不會再發sql去取Classroom,僅僅發1條sql取這個cla中的Student System.out.println(s.getName()); } session.getTransaction().commit();
//此處發2條sql:1條取Student,1條取Classroom,這裏的Classroom的id是Student //爲13所對應的Classroom的id爲6 //已經把id爲6的Classroom放到session中 Student stu = session.load(Student.class, 13); System.out.println(stu.getName()+","+stu.getClassroom().getGrade()); //注意:此處取的是id爲1的Classroom Classroom cla = session.load(Classroom.class, 1); // System.out.println(cla.getStudents().size()); //會發出select count(id),由於set設置了lazy="extra" for(Student s:cla.getStudents()) { //此處會發出2條sql:由於要取的id爲1的Classroom不存在session的管理中, //因此會多發1條sql取Classroom System.out.println(s.getName()); } session.getTransaction().commit();
2.三、一對一映射
1:1單向:
一對一有兩種關聯方式。
一、主鍵關聯(兩個實體的主鍵徹底一致,使用不是不少)
二、外鍵關聯(在任意一張表中增長另外一張表的外鍵,和多對一相似)
外鍵關聯:
<hibernate-mapping package="org.pm.hibernate.model"> <class name="IDCard" table="t_id_card"> <id name="id"> <generator class="native"/> </id> <property name="no"/> <!-- 外鍵關聯使用mang-to-one完成 one2one和oneToMany相似,只用增長unique="true"說明只能有一個對應關係 --> <many-to-one name="person" column="pid" unique="true"/> </class> </hibernate-mapping>
add:
session = HibernateUtil.openSession(); session.beginTransaction(); Person person = new Person(); person.setName("寶寶"); session.save(person); IDCard card = new IDCard(); card.setNo("150000000"); //由有外鍵的IDCard來維護關係 card.setPerson(person); session.save(card); session.getTransaction().commit();
session = HibernateUtil.openSession(); session.beginTransaction(); //獲取已經存在的person Person person = session.load(Person.class, 1); //再次爲該person添加IDCard IDCard card = new IDCard(); card.setNo("260000000"); card.setPerson(person); session.save(card); /* * 因爲使用了unique,因此一個Person只能有一個IDCard,這裏就會報錯 */ session.getTransaction().commit();
1:1雙向:
<hibernate-mapping package="org.pm.hibernate.model"> <class name="Person" table="t_person"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- name表示本身的屬性的名稱,property-ref是本身在另外一端的屬性名稱, 表示由對端維護關係 --> <one-to-one name="idCard" property-ref="person"/> </class> </hibernate-mapping>
特別注意:在沒有外鍵的這一端沒法維護關係。
add:
session = HibernateUtil.openSession(); session.beginTransaction(); /* * 此時,因爲使用的是IDCard來維護關係(外鍵在哪一端就由哪一端來維護) * 經過person.setIdCard()就無效,因此外鍵關係不會更新 */ IDCard idCard = new IDCard(); idCard.setNo("123"); session.save(idCard); Person person = new Person(); person.setName("one2one-雙向"); person.setIdCard(idCard); session.save(person); session.getTransaction().commit();
session = HibernateUtil.openSession(); session.beginTransaction(); Person person = new Person(); person.setName("one2one-雙向-成功"); session.save(person); IDCard idCard = new IDCard(); idCard.setNo("123456"); //idCard來維護關係,外鍵纔會更新成功 idCard.setPerson(person); session.save(idCard); session.getTransaction().commit();
update:
session = HibernateUtil.openSession(); session.beginTransaction(); Person person = new Person(); person.setName("update"); person.setId(1); /** * 此處person是離線對象,而且沒有設置IDCard, * 可是因爲person這一端沒有維護關係,因此person和IDCard的關係依然存在 */ session.update(person); session.getTransaction().commit();
load:
session = HibernateUtil.openSession(); session.beginTransaction(); Person person = session.load(Person.class, 3); //只要取出的是沒有維護關係的這一方,會自動將關聯對象取出,會發出1條sql //因爲person端沒有維護關係,因此不會進行延遲加載,因此1條sql就搞定了 System.out.println(person.getName()+","+person.getIdCard().getNo()); session.getTransaction().commit();
session = HibernateUtil.openSession(); session.beginTransaction(); //特別注意:若是沒有雙向,此時會發出2條sql,一條取IDCard,一條延遲加載取person //此時會發出3條sql語句 IDCard idCard = session.load(IDCard.class, 4); //此時沒有使用IDCard的Person,會延遲加載,目前只是發出1條sql System.out.println(idCard.getNo()); //要去取person同時也會取出這個person的IDCard,這裏就不會使用join來取,因此會發出2條sql System.out.println(idCard.getPerson().getName()); session.getTransaction().commit();
最佳實踐就是,one2one的時候最好不要使用雙向關聯,若是使用雙向關聯,儘量在沒有維護
關係的一端取數據,Hibernate會自動完成join,僅僅只會發1條sql,若是使用維護關係端取
數據,在經過延遲加載取關聯對象時會同時再去取person的IDCard關聯,因此會發3條sql語句。
2.四、多對多
m:n單向:
m:n的這種關係,必須在中間增長關聯表。
<hibernate-mapping package="org.pm.hibernate.model"> <class name="Admin" table="t_admin"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- table表示指明映射的中間表 --> <set name="roles" table="t_admin_role" lazy="extra"> <!-- key和one-to-many中的key同樣,表示本身在對方的外鍵名稱 --> <key column="aid"/> <!-- 使用many-to-many完成映射,這裏要增長column表示所放置的元素的外鍵名稱 --> <many-to-many class="Role" column="rid"/> </set> </class> </hibernate-mapping>
add:
session = HibernateUtil.openSession(); session.beginTransaction(); Admin a1 = new Admin(); a1.setName("張三"); session.save(a1); Admin a2 = new Admin(); a2.setName("李四"); session.save(a2); Role r1 = new Role(); r1.setName("超級管理員"); r1.add(a1); session.save(r1); Role r2 = new Role(); r2.setName("財務管理人員"); r2.add(a1); r2.add(a2); session.save(r2); session.getTransaction().commit(); /* * 使用many-to-many不論在哪一方來維護關係都比較的麻煩,並且不少時候關聯表中須要加入其它的 * 屬性,因此在開發中,常用兩個一對多來替代多對多 */
load:
session = HibernateUtil.openSession(); session.beginTransaction(); Admin a = session.load(Admin.class, 1); System.out.println(a.getName()); for(Role r:a.getRoles()) { System.out.println(r.getName()); } session.getTransaction().commit();
m:n雙向:
m:n雙向關聯就是兩個單向關聯的集合,兩端配置基本一致。
<hibernate-mapping package="org.pm.hibernate.model"> <class name="Role" table="t_role"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="admins" table="t_admin_role" lazy="extra"> <key column="rid"/> <many-to-many class="Admin" column="aid"/> </set> </class> </hibernate-mapping> <hibernate-mapping package="org.pm.hibernate.model"> <class name="Admin" table="t_admin"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <!-- table表示指明映射的中間表 --> <set name="roles" table="t_admin_role" lazy="extra"> <!-- key和one-to-many中的key同樣,表示本身在對方的外鍵名稱 --> <key column="aid"/> <!-- 使用many-to-many完成映射,這裏要增長column表示所放置的元素的外鍵名稱 --> <many-to-many class="Role" column="rid"/> </set> </class> </hibernate-mapping>
在開發中名稱對應必須同樣。
add:
session = HibernateUtil.openSession(); session.beginTransaction(); //由TeacherCourse完成關係映射關聯 Teacher t1 = new Teacher(); t1.setName("張老師"); session.save(t1); Teacher t2 = new Teacher(); t2.setName("王老師"); session.save(t2); Course c1 = new Course(); c1.setName("數據結構"); session.save(c1); Course c2 = new Course(); c2.setName("C語法基礎"); session.save(c2); TeacherCourse tc1 = new TeacherCourse(); tc1.setAchievement(89); tc1.setTeacher(t1); tc1.setCourse(c1); session.save(tc1); tc1 = new TeacherCourse(); tc1.setAchievement(66); tc1.setTeacher(t1); tc1.setCourse(c2); session.save(tc1); tc1 = new TeacherCourse(); tc1.setAchievement(99); tc1.setTeacher(t2); tc1.setCourse(c1); session.save(tc1); tc1 = new TeacherCourse(); tc1.setAchievement(79); tc1.setTeacher(t2); tc1.setCourse(c2); session.save(tc1); session.getTransaction().commit();
load:
session = HibernateUtil.openSession(); session.beginTransaction(); Teacher t = session.load(Teacher.class, 1); //load的時候因爲延遲加載,會根據不一樣的狀況取相應的關聯對象,因此會發出大量的sql /** * 整體來講:最佳實踐就是,通常不使用雙向關聯,特別不建議使用1的這一方的關聯, * 由於從1的這一端取關聯對象頗有可能會涉及到分頁操做,因此基本不會使用 * 在設計的時候不是特殊狀況不要使用雙向關聯 */ System.out.println(t.getName()); for(TeacherCourse tc:t.getTcs()) { System.out.println(tc.getCourse().getName()+","+tc.getAchievement()); } session.getTransaction().commit();
最佳實踐:因爲使用多對多不論在哪一方來維護關係都很是的麻煩,在具體的開發中基本不會使用,而是使
用兩個一對多來完成映射。
2.五、對多對多的變形
因爲多對多的關係不論在哪一方來維護都很是的麻煩,因此通常狀況都是把多對多拆分爲兩個一對多。
<hibernate-mapping package="org.pm.hibernate.model"> <class name="Course" table="t_course"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="tcs" lazy="extra" inverse="true"> <key column="cid"/> <one-to-many class="TeacherCourse"/> </set> </class> </hibernate-mapping> <hibernate-mapping package="org.pm.hibernate.model"> <class name="Teacher" table="t_teacher"> <id name="id"> <generator class="native"/> </id> <property name="name"/> <set name="tcs" lazy="extra" inverse="true"> <key column="tid"/> <one-to-many class="TeacherCourse"/> </set> </class> </hibernate-mapping> <hibernate-mapping package="org.pm.hibernate.model"> <class name="TeacherCourse" table="t_teacher_course"> <id name="id"> <generator class="native"/> </id> <property name="achievement"/> <many-to-one name="teacher" column="tid"/> <many-to-one name="course" column="cid"/> </class> </hibernate-mapping>
整個寫法和one-to-many雙向相似。