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