Hibernate的映射

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雙向相似。

相關文章
相關標籤/搜索