Hibernate的映射

2.1、映射简介

对于所有的对象实体而言,有如下三种关系:

1:1,1:n,m:n

一种双向,一种单向。

2.2、一对多映射

    多对一单向:many-to-one单向,指的是在多的这一端增加关联。

    配置文件的写法:

<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、添加方式1:先创建many再创建one

			//先添加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、添加方式2:先创建one之后再创建many,这种是最佳实践。

			//先添加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();

    3、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();

    4、更新

			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();

    5、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();

    6、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的这一方来维护关系。

    2、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">两个外键最好保持一致,可以不一致,如果设置为不一
致很难维护关系。

1、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();

这是一种非常正常的操作(实际开发中的操作)。

2、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"来表明自己不维护关系。

3、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.3、一对一映射

    1:1单向:

    一对一有两种关联方式。

1、主键关联(两个实体的主键完全一致,使用不是很多)

2、外键关联(在任意一张表中增加另一张表的外键,和多对一类似)

外键关联:

<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.4、多对多

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.5、对多对多的变形

    由于多对多的关系不论在哪一方来维护都非常的麻烦,所以一般情况都是把多对多拆分为两个一对多。

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

转载于:https://my.oschina.net/pmos/blog/759450

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值