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

相关文章
相关标签/搜索