有用的参考文章:
http://blog.csdn.net/laner0515/article/details/12905711 重要
http://blog.csdn.net/linminqin/article/details/6324567
http://www.cnblogs.com/jyh317/p/3691842.html
http://blog.csdn.net/lovelyhermione/article/details/2020881
前言:
hibernate关系映射十分重要,对于掌握hibernate是必备的知识,关系映射分为一对一 一对多 多对一 多对多 看起来关系关系复杂,但其实了解其中原理就可以灵活自如运用。在了解详细分类之前,有一些点和关键系必须掌握其意义:
cascade关键词:表示级联包括add update delete 一般情况不使用 不好。 作用:一般在保存对象时 需要先显示的save对端关联的对象 变为持久太之后才可以在被本端set进去,但在配置文件中加入级联之后 可以不显示的save,我们save本端对象后(set完对端) 自动save关联对象。最佳实践: 如果没有特殊情况 不要使用cascade 特别注意:可能使用cascade的地方一般都是在一的一方进行删除时候 多的一方删除会出问题
特殊需求才会使用cascade的add 正常情况add方法都是应该由程序员添加(为了防止级联添加乱七八糟的其他东西)
inverse:默认为false 。设置为true表示让对端来进行维护关系 即让对端来set。
lazy=extra:使用了lazy=extra之后会稍微只能些 会根据取得值得不同来判断是调用的count还是获取投影
property-ref:xml文件中主表端property-ref表示一对一时让对端(从表端)维护关系
1: 所谓哪端维护关系指含有对方引用的一端 可以对对端的对象set操作 可能由一端维护 也可能由多的一端维护
如果有设置了Inverse=true 或 property-ref(1对1)的话 维护关系端变成对端
所谓单双向 就是指在哪端加对端的引用
2: 在哪端添加时 要先把关联的对端对象给save了 不允许 不save 因为那是瞬时态,否则说瞬时对象没有被保存
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing但是有一种情况 就是xml配置时 加入cascade 可以不显示的save关联对象 但这在多对一级联删除多端某个对象时又出现问题 因为级联删除一端后 相同外键的多端值就错了
3:所谓One to many 双向 就是 one to many 单向加many to one 单向
4:注意:即使是双向设置 即两边都加了引用 但如果我在添加时 在一的一方添即维护关系 只在一端set了多端 而多端没有把一端set进去
此时仍然会发出5条语句 三个插入 两个更新 因为需要更新外键值
5:注意:如果在一端加入了inverse=true 表示让对端(多端)维护关系 如果仍然像4中在一端set多端的值
此时只会产生3条插入语句 不会有更新语句 多端外键的则值为Null 因为多端根本不知道一端的情况即多端没有set一端的classroom值
6:一句话 如果让多端维护关系 那么添加时需要从多端添加 把一端的对象给set进去 才能正确
如果让一端维护关系 那么添加时需要从一端添加set多端的值 但此时效率低 多产生update语句
7:一端加载时 可以把多的也加载出来 同理 多端加载时 可以把一端也加载进来(只要设置了关系即有了对方的引用即可) 注意 查询是不用级联的 与级联无关 用load时候 会自动懒加载关联对象(不会产生SQL语句 在调用它的属性时候才会) get会自动加载关联对象(产生SQL语句)
1) 加载和cascade是没有关系的 cascade只有增加 删除 修改
2) 加载也与Inverse没有关系 只要在某一端配置了关系 就可以进行加载对端或者懒加载
8:重要:关联加载产生的条件:
只要本端持有对端的对象 也就是本端也维护关系 那么就可以用Load或者get来关联调用对方对象
注意:在一对一双向关联时最特殊(延迟加载在其他关联时都正常):在先加载非维护关系端的对象时,
取维护关系一端的对象才能实现对端的延迟加载 如果取得是不维护关系的一端 那么只会发出1条语句(用join)
把关联对象全部查询 不会延迟加载
9:一对一的时候 最好不要使用双向关联
如果使用双向关联 尽可能在没有维护关系的一边取数据
hibernate会自动完成join操作 仅仅只会发出一条SQL
如果从维护关系端取数据 在通过延迟加载取关联对象时 会同时再去取person的IDcard关联
10:在双向关系中 需要加一个设置 让某一端来进行维护 比如一对一时候 在主表端添加property-ref让从表端维护 一对多双向时候 设置inverse=true
11:多对多时 使用关联对象以及关联表(两个一对多关系) 不要单独使用关联表
12:总体来说:最佳实践就是,一般不使用双向关联,特别不建议使用一的这一方的关联
在设计的时候不是特殊情况不要使用双向关联
一:多对一单向
配置文件:
hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hiber?characterEncoding=UTF8</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="show_sql">true</property>
<!-- <property name="format_sql">true</property>-->
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="com/itany/model/User.hbm.xml"/>
<mapping resource="com/itany/model/Book.hbm.xml"/>
<mapping resource="com/itany/model/Classroom.hbm.xml"/>
<mapping resource="com/itany/model/Student.hbm.xml"/>
<mapping resource="com/itany/model/Message.hbm.xml"/>
<mapping resource="com/itany/model/Comment.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Classroom.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Classroom" table="t_cla">
<id name="id" >
<!-- 主键的生成策略 native increment assigned-->
<generator class="native"></generator>
</id>
<property name="name" ></property>
<property name="grade" ></property>
</class>
</hibernate-mapping>
Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Student" table="t_stu">
<id name="id" >
<!-- 主键的生成策略 native increment assigned-->
<generator class="native"></generator>
</id>
<property name="name" ></property>
<property name="no" ></property>
<!-- 当设置cascade时候 会自动完成关联 若添加(写)时没有显示的添加关联对象(此处是之前写入父关联对象classroom) 那么会自动添加一个关联对象
最佳实践: 如果没有特殊情况 不要使用cascade 特别注意:可能使用cascade的地方一般都是在一的一方进行删除时候 多的一方删除会出问题
特殊需求才会使用cascade的add 正常情况add方法都是应该由程序员添加(为了防止级联添加乱七八糟的其他东西)-->
<many-to-one name="classRoom" class="Classroom" column="cid" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
junit4测试代码:
public class TestMany2One
{
/*
* 多对一单向
*/
@Test
public void testAdd1()
{
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
//先添加一的一方 再添加多的一方 关系由多的一方维护 共发出三条SQL语句
Classroom c=new Classroom();
c.setGrade(2016);
c.setName("计算机");
session.save(c);
Student stu1=new Student();
stu1.setName("张三");
stu1.setNo("001");
stu1.setClassRoom(c);
Student stu2=new Student();
stu2.setName("张三2");
stu2.setNo("003");
stu2.setClassRoom(c);
session.save(stu1);
session.save(stu2);
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testAdd2()
{
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
//先添加多的一方 再添加一的一方 此时要多修改两次 发出5条语句
Student stu1=new Student();
stu1.setName("张三3");
stu1.setNo("001");
session.save(stu1);
Student stu2=new Student();
stu2.setName("张三4");
stu2.setNo("003");
session.save(stu2);
Classroom c=new Classroom();
c.setGrade(2017);
c.setName("计算机");
session.save(c);
stu1.setClassRoom(c);
stu2.setClassRoom(c);
/*
* 最佳实践 一定要先添加一的一方 再添加多的一方 总之关系让多的一方来维护
*/
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testAdd3WithoutCascade()
{
/*
* 注意;查询是不用级联的 与级联无关 用load时候 会自动懒加载 get会自动加载
* 在多的配置文件中没有加入cascade 也没有保存classroom 直接保存student
* 在保存student时 是需要联系外键的 但此时 没有显示的保存classsroom 因此student和classroom都无法保存
* 抛出异常
* org.hibernate.TransientObjectException:
* object references an unsaved transient instance - save the transient instance before flushing: com.itany.model.Classroom
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
Classroom c=new Classroom();
c.setGrade(2018);
c.setName("计算机科学");
Student stu1=new Student();
stu1.setName("张三5");
stu1.setNo("001");
session.save(stu1);
Student stu2=new Student();
stu2.setName("张三6");
stu2.setNo("003");
session.save(stu2);
stu1.setClassRoom(c);
stu2.setClassRoom(c);
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testAdd4WithCascade()
{
/*
* 不需要显示保存关联对象了
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
Classroom c=new Classroom();
c.setGrade(2018);
c.setName("计算机科学");
Student stu1=new Student();
stu1.setName("张三5");
stu1.setNo("001");
session.save(stu1);
Student stu2=new Student();
stu2.setName("张三6");
stu2.setNo("003");
session.save(stu2);
stu1.setClassRoom(c);
stu2.setClassRoom(c);//发出五条语句 前三条为插入对象 后两条为修改stu对象
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testLoad()
{
/*
* 多对一 加载多的对象 可以把一的对象也加载出来(多端维护关系)
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
Student s1=(Student)session.load(Student.class,1);//这一步没有SQL产生
System.out.println(s1.getName());//此时仅仅发出1条SQL 并没有加载classroom对象 查询出的是代理 懒加载
System.out.println(s1.getClassRoom().getName());//此时又发出一条sql 才真正查询classroom对象
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testLoad2()
{
/*
* 单向多对一 加载一端的对象 (多端维护关系)这种从一端加载多端的对象是不可能的
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
Classroom c1=(Classroom)session.load(Classroom.class,1);//这一步没有SQL产生 SQL产生在c1.getName()的时候
//Classroom c1=(Classroom)session.get(Classroom.class,1);//这一步有SQL产生
System.out.println(c1.getName());
}
catch (Exception e)
{
if(null!=session)
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testDeleteWithCascade()
{
/*
* 在多的一方进行级联删除 配置文件已加cascade=all
* 当有多个多的一方外键只想同一个一的对象
* 在级联删除多的某一个时 想把一的一方也级联删除 会异常
* Cannot delete or update a parent row: a foreign key constraint
* 因为多的一方还有其他的行关联了同一个一的一方的对象
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
Student s=(Student)session.load(Student.class,11);
session.delete(s);
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
}
二:一对多单向
一对多单向 开发中不建议使用 麻烦 效率低
Message.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Message" table="t_message">
<id name="id" >
<!-- 主键的生成策略 assigned 开发人员指定 -->
<!-- <generator class="assigned"></generator> -->
<!-- 主键的生成策略 uuid会自动生成字符串16进制 128位 16字节 注意此时主键必须为String类型 -->
<generator class="native"></generator>
</id>
<property name="title" ></property>
<property name="content" ></property>
<!-- 使用了lazy=extra之后会稍微只能些 会根据取得值得不同来判断是调用的count还是获取投影 -->
<set name="comments" lazy="extra">
<!-- key 指的是(对方)多的一方的外键名称 此处为one to many 单向-->
<key column="mid" />
<one-to-many class="Comment"/>
</set>
</class>
</hibernate-mapping>
Comment.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Comment" table="t_comment">
<id name="id" >
<!-- 主键的生成策略 assigned 开发人员指定 -->
<!-- <generator class="assigned"></generator> -->
<!-- 主键的生成策略 uuid会自动生成字符串16进制 128位 16字节 注意此时主键必须为String类型 -->
<generator class="native"></generator>
</id>
<property name="content" ></property>
</class>
</hibernate-mapping>
junit4测试
public class TestOne2Many
{
/*
* 一对多单向
* 开发中不建议使用 麻烦 效率低
*/
@Test
public void testAdd()
{
Session session=null;
Transaction trans=null;
try
{
session=HibernateUtil.openSession();
trans=session.beginTransaction();
Comment c1=new Comment();
c1.setContent("123");
Comment c2=new Comment();
c2.setContent("456");
session.save(c1);
session.save(c2);
Message m1=new Message();
m1.setTitle("圣兽山");
m1.setContent("sssssssssssssssssssssssss");
m1.addComments(c1);
m1.addComments(c2);
session.save(m1);
trans.commit();
/*
* 一共发出5条SQL语句 三个Insert 两个Update 因为在一的一方维护关系(一种有多方的引用 一方来set)
* 多的一方无法知道主表是什么 因为多的一方没有一方的引用
* 因此 一的一方保存后 为了通过One2many维护关系 需要通过外键update2次 在一方维护效率低下
*/
}
catch (HibernateException e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testLoad()
{
/*
* 一端加载时 可以把多的也加载出来 同理 多端加载时 可以把一端也加载进来
* 1 加载和cascade是没有关系的 cascade只有增加 删除 修改
* 2 加载也与Inverse没有关系 只要在某一端配置了关系 就可以进行加载对端或者懒加载
*/
Session session=null;
session=HibernateUtil.openSession();
Message m1=(Message)session.load(Message.class,1);
System.out.println(m1.getContent());
Set<Comment> comments=m1.getComments();
for (Comment comment : comments)
{
//懒加载
System.out.println(comment.getContent());
}
try
{
session=HibernateUtil.openSession();
}
catch (HibernateException e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testLoad2()
{
/*
* 在set标签中添加 lazy="extra" 使得我们调用set.size()方法时候 SQL用count()稍微智能
* <set name="comments" lazy="extra">
*/
Session session=null;
session=HibernateUtil.openSession();
Message m1=(Message)session.load(Message.class,1);
System.out.println(m1.getContent());
Set<Comment> comments=m1.getComments();
System.out.println(comments.size());
try
{
session=HibernateUtil.openSession();
}
catch (HibernateException e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
}
主要还是看谁来维护关系 即让哪端进行添加操作等
如果在这种设置下,代码中让一端维护关系 在配置文件中一端inverse=true 让多端配置
那么会造成多端的外键值为Null
Classroom.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Classroom" table="t_cla">
<id name="id" >
<!-- 主键的生成策略 native increment assigned-->
<generator class="native"></generator>
</id>
<property name="name" ></property>
<property name="grade" ></property>
<set name="students" lazy="extra" inverse="true">
<key column="cid"/>
<one-to-many class="Student"/>
</set>
</class>
</hibernate-mapping>
Student.hbm.xml
</pre><pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Student" table="t_stu">
<id name="id" >
<!-- 主键的生成策略 native increment assigned-->
<generator class="native"></generator>
</id>
<property name="name" ></property>
<property name="no" ></property>
<!-- 当设置cascade时候 会自动完成关联 若添加(写)时没有显示的添加关联对象(此处是之前写入父关联对象classroom) 那么会自动添加一个关联对象
最佳实践: 如果没有特殊情况 不要使用cascade 特别注意:可能使用cascade的地方一般都是在一的一方进行删除时候 多的一方删除会出问题
特殊需求才会使用cascade的add 正常情况add方法都是应该由程序员添加(为了防止级联添加乱七八糟的其他东西)-->
<many-to-one name="classRoom" class="Classroom" column="cid" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
junit4测试
@Test
public void testAdd3()
{
/*
* 所谓维护关系 就是指在哪端添加 所谓单双向 就是指在哪端加对端引用
* 在一端维护 表示在一端set进多端的值
* 在多端维护 表示在多端set进一端的值
*
* 注意:在哪端添加时 要先把关联的对端对象给save了 不允许 不save 因为那是顺势态
*
* 所谓One to many 双向 就是 one to many 单向加many to one 单向
* 1 注意:即使是双向设置 即两边都加了引用 但如果我在添加时 在一的一方添即维护关系 只在一端set了多端 而多端没有把一端set进去
* 此时仍然会发出5条语句 三个插入 两个更新 因为需要更新外键值
*
* 2 注意:如果在一端加入了inverse=true 表示让对端(多端)维护关系 如果仍然像情况1中在一端set多端的值
* 此时只会产生3条插入语句 不会有更新语句 多端外键的则值为Null 因为多端根本不知道一端的情况即多端没有set一端的classroom值
*
* 一句话 如果让多端维护关系 那么添加时需要从多端添加 把一端的对象给set进去 才能正确
* 如果让一端维护关系 那么添加时需要从一端添加set多端的值 但此时效率低 多产生update语句
*/
Session session=null;
Transaction trans=null;
try
{
session=HibernateUtil.openSession();
trans=session.beginTransaction();
Student s1=new Student();
s1.setName("lisi3");
s1.setNo("333");//注意!!! 瞬时对象要先session.save 然后在被set进classroom对象中 否则报错 说瞬时对象没有被保存
//org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing
session.save(s1);
Student s2=new Student();
s2.setName("lisi4");
s2.setNo("444");
session.save(s2);
Set<Student> students=new HashSet<Student>();
students.add(s1);
students.add(s2);
Classroom c=new Classroom();
c.setName("C++班");
c.setGrade(2019);
c.setStudents(students);
session.save(c);
trans.commit();//5条SQL
/*
* 一共发出5条SQL语句 三个Insert 两个Update 因为在一的一方维护关系(一种有多方的引用 一方来set)
* 多的一方无法知道主表是什么 因为多的一方没有一方的引用
* 因此 一的一方保存后 为了通过One2many维护关系 需要通过外键update2次 在一方维护效率低下
*/
}
catch (HibernateException e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testAdd4()
{
/*
* 与testAdd3对应 这是双向 时限制在多端进行维护(inverse) 先把一端关联的给save了
*/
Session session=null;
Transaction trans=null;
try
{
session=HibernateUtil.openSession();
trans=session.beginTransaction();
Classroom c=new Classroom();
c.setName("C班");
c.setGrade(2020);
session.save(c);
Student s1=new Student();
s1.setName("lisi5");
s1.setNo("555");//注意 瞬时对象要先session.save 然后在被set进classroom对象中 否则报错 说瞬时对象没有被保存
//org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing
s1.setClassRoom(c);
session.save(s1);
Student s2=new Student();
s2.setName("lisi6");
s2.setNo("666");
s2.setClassRoom(c);
session.save(s2);
trans.commit();//5条SQL
/*
* 一共发出5条SQL语句 三个Insert 两个Update 因为在一的一方维护关系(一种有多方的引用 一方来set)
* 多的一方无法知道主表是什么 因为多的一方没有一方的引用
* 因此 一的一方保存后 为了通过One2many维护关系 需要通过外键update2次 在一方维护效率低下
*/
}
catch (HibernateException e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testLoad3()
{
/*
*环境是 一对多 双向 两端都进行了配置 同上
*/
Session session=null;
session=HibernateUtil.openSession();
Classroom c=(Classroom)session.load(Classroom.class,7);//从一端加载
System.out.println(c.getStudents().size());
Student s=(Student)session.load(Student.class,17);//从多端加载
System.out.println(s.getName());
System.out.println(s.getClassRoom().getName());//此处不会产生SQL 用的是session缓存
//以上一共3条SQL
try
{
session=HibernateUtil.openSession();
}
catch (HibernateException e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
四:一对一单向(从表端进行维护关系)
主表:person.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Person" table="t_person">
<id name="id" column="id">
<!-- 主键的生成策略 native increment assigned-->
<generator class="native"></generator>
</id>
<property name="name" />
</class>
</hibernate-mapping>
从表:idcard.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="IDCard" table="t_id_card">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="no" ></property>
<!-- 一对一外键关联 -->
<many-to-one name="person" column="pid" unique="true" />
</class>
</hibernate-mapping>
junit4测试:
public class TestOne2One
{
//person是主表 idcard是从表
//-----------------------------------以下为一对一单向(由外键即从表端维护关系)------------------------------------------
@Test
public void testAdd()
{
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
Person p=new Person();
p.setName("隔壁老王");
session.save(p);
IDCard card=new IDCard();
card.setNo("111111");
card.setPerson(p);
session.save(card);
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testAdd02()
{
/*
* 由于使用了Unique 为一对一单向外键关联 此时添加两个一端绑定同一个Person 包重复错误
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
Person p=(Person)session.load(Person.class,1);
IDCard card=new IDCard();
card.setNo("22222222");
card.setPerson(p);
session.save(card);
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testload1()
{
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
//从多的一端进行延迟加载
/*
* 所谓哪端维护关系指含有对方引用的一端 可以对对端的对象set操作 可能由一端维护 也可能由多的一端维护
* 如果有设置了Inverse=true 或 property-ref(1对1)的话 维护关系端变成对端
*
* 重要:延迟加载产生的条件:
* 1 加载时候使用Load方法
* 2 取维护关系一端的对象才能实现对端的延迟加载 如果取得是不维护关系的一端 那么只会发出1条语句(用join)
* 把关联对象全部查询 不会延迟加载
*
* 因此这个一对一单向 从表端维护 可以做到延迟加载 会产生2条SQL语句
*/
IDCard card=(IDCard)session.load(IDCard.class,5);
System.out.println(card.getNo()+","+card.getPerson().getName());
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
}
如果一定要使用 在查询时候 要从不维护关系的一端进行查询 这样只会产生1条语句 ,若从维护关系的一端查询会产生3条语句 效率低
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Person" table="t_person">
<id name="id" column="id">
<!-- 主键的生成策略 native increment assigned-->
<generator class="native"></generator>
</id>
<property name="name" />
<!-- name表示属性的名次
property-ref表示由对端来维护关系 也就是对端的person属性维护关系 -->
<one-to-one name="card" property-ref="person" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="IDCard" table="t_id_card">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="no" ></property>
<!-- 一对一外键关联 -->
<many-to-one name="person" column="pid" unique="true" />
</class>
</hibernate-mapping>
测试代码:
//-----------------------------------以下为一对一双向(两边都进行了配置)------------------------------------------
@Test
public void testAdd3()
{
/*
* 重要 xml文件中主表端property-ref表示一对一时让对端(从表端)维护关系 因此
* 下面的添加操作 虽然我们是在主表端进行的 但是从表端的外键值为null 关系不会更新(因为添加代码没有从从表端维护 即没有从表.set(主表对象))
* 和在双向一对多关联时 一端Inverse时发生的情形一样
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
IDCard card=new IDCard();
card.setNo("3333");
session.save(card);
Person p=new Person();
p.setName("隔离老张");
p.setCard(card);
session.save(p);
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testAdd4()
{
/*
* 一对一双向都配置时 主表端配置了property-ref 表示让对端从表进行维护
* 我们这里从从表外键端进行添加维护 完全正确
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
trans = session.beginTransaction();
Person p=new Person();
p.setName("lao liu");
session.save(p);
IDCard card=new IDCard();
card.setNo("6666");
card.setPerson(p);
session.save(card);
trans.commit();
}
catch (Exception e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testload2()
{
/*
* 一对一双向时 从没有维护关系的一端(此时是主表端 因为主表端设置了property-ref表示对端维护)进行加载
* 此时会产生一条SQL语句 不会延迟加载
* 因为延迟加载是由维护关系的一端也就是外键端进行的
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
Person p=(Person)session.load(Person.class,1);
System.out.println(p.getName()+p.getCard().getNo());
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testload3()
{
/*这个地方比较特殊:
* 一对一双向时 从维护关系的一端 此处为从表端进行取从表对象 会发3条
* 第一条为从表对象 第二条为把懒加载的主表对象取出 因为主表关联从表 此时又会发一条去取idcard 共3条
*
* 特别注意:如果没有双向,此时会发两条 一条取IDcard 一条延迟加载person
*
*/
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
IDCard card=(IDCard)session.load(IDCard.class,5);
System.out.println(card.getNo()+","+card.getPerson().getName());
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
/*
* 最佳实践:
* 一对一的时候 最好不要使用双向关联
* 如果使用双向关联 尽可能在没有维护关系的一边取数据
* hibernate会自动完成join操作 仅仅只会发出一条SQL
* 如果从维护关系端取数据 在通过延迟加载取关联对象时 会同时再去取person的IDcard关联
*/
六:多对多(三张表 两个类 使用关联表 不推荐 因为很多字段只能存在关联表中 又没有对应的类对应 很是不方便)
admin 表示用户 role表示角色 是多对多的关系
Admin.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Admin" table="t_admin">
<id name="id" >
<generator class="native"></generator>
</id>
<property name="name" />
<set name="roles" table="t_admin_role" lazy="extra">
<key column="aid" /><!-- 是我在对方的外键 在别人的表里-->
<many-to-many class="Role" column="rid"/>
</set>
</class>
</hibernate-mapping>
Role.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Role" table="t_role">
<id name="id" >
<generator class="native"></generator>
</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>
测试代码:
/*
* 使用ManyToMany(两个实体类 三个表 其中一个为关联表)无论从哪端来维护关系都比较麻烦
* 而且很多属性只能加在关联表中 比如学生-课程关系 学生每门课的成绩就必须保存在关联表中 而在实体类中没有类与之对应
*
* 所以在开发中,经常是使用两个一对多的关系来代替多对多(三个实体类 三张表)
*/
//------------------------------------两个实体类 三个表 其中一个为关联表------------------------------------------------
@Test
public void testAdd()
{
Session session=null;
Transaction trans=null;
try
{
session=HibernateUtil.openSession();
trans=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);
r1.add(a2);
session.save(r1);
Role r2=new Role();
r2.setName("财务管理员");
r2.add(a1);
r2.add(a2);
session.save(r2);
//以上共产生8条语句
//2条admin插入 2条role插入
//4条中间表插入
trans.commit();
}
catch (HibernateException e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testLoad()
{
Session session=null;
Transaction trans=null;
try
{
session = HibernateUtil.openSession();
Admin a1=(Admin)session.load(Admin.class,1);
for (Role role : a1.getRoles())
{
System.out.println(role.getName());
}//以上产生两条语句
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
七:多对多(三张表 三个类 两个一对多关系的合并 推荐使用)
teacher和course的关系 使用TeacherCourse作为关联对象 且有对应的关联表
teacher.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Teacher" table="t_teacher">
<id name="id" >
<generator class="native"></generator>
</id>
<property name="name" />
<set name="tcs" lazy="extra" inverse="true">
<key column="tid" />
<one-to-many class="TeacherCourse"/>
</set>
</class>
</hibernate-mapping>
course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="Course" table="t_course">
<id name="id" >
<generator class="native"></generator>
</id>
<property name="name" />
<set name="tcs" lazy="extra" inverse="true">
<key column="cid" />
<one-to-many class="TeacherCourse" />
</set>
</class>
</hibernate-mapping>
teachercourse.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itany.model">
<class name="TeacherCourse" table="t_teacher_course">
<id name="id" >
<generator class="native"></generator>
</id>
<property name="score" />
<many-to-one name="teacher" column="tid" class="Teacher" />
<many-to-one name="course" column="cid" class="Course" />
</class>
</hibernate-mapping>
TeacherCourse.java
public class TeacherCourse
{
private int id;
private double score;
private Teacher teacher;
private Course course;
//set get方法
}
测试类:
//------------------------------------三个实体类 三个表 两个一对多关系的合并------------------------------------------------
@Test
public void testAdd2()
{
Session session=null;
Transaction trans=null;
try
{
session=HibernateUtil.openSession();
trans=session.beginTransaction();
Teacher t1=new Teacher();
t1.setName("张三");
session.save(t1);
Teacher t2=new Teacher();
t2.setName("李四");
session.save(t2);
Course c1=new Course();
c1.setName("java");
session.save(c1);
Course c2=new Course();
c2.setName("c++");
session.save(c2);
TeacherCourse tc1=new TeacherCourse();
tc1.setScore(99);
tc1.setTeacher(t1);
tc1.setCourse(c1);
session.save(tc1);
TeacherCourse tc2=new TeacherCourse();
tc2.setScore(98);
tc2.setTeacher(t1);
tc2.setCourse(c2);
session.save(tc2);
TeacherCourse tc3=new TeacherCourse();
tc3.setScore(97);
tc3.setTeacher(t2);
tc3.setCourse(c1);
session.save(tc3);
TeacherCourse tc4=new TeacherCourse();
tc4.setScore(96);
tc4.setTeacher(t2);
tc4.setCourse(c2);
session.save(tc4);
trans.commit();
}
catch (HibernateException e)
{
if(null!=session)
trans.rollback();
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
@Test
public void testLoad2()
{
Session session=null;
Transaction trans=null;
try
{
//共产生4条SQL
session = HibernateUtil.openSession();
Teacher t=(Teacher)session.load(Teacher.class, 1);//这步执行完并没有产生SQL
for (TeacherCourse tc:t.getTcs())//第一次到这会产生两条SQL语句
{
System.out.println(tc.getCourse().getName()+","+tc.getScore());//每次到这都会延迟加载一次
}
/*
* load的时候由于延迟加载 会根据不同的情况取相应的关联对象 所以会发出大量的sql
*
* 总体来说:最佳实践就是,一般不使用双向关联,特别不建议使用一的这一方的关联
* 因为从一的这一端取关联对象很有可能涉及到分页操作(一次就要取出大量对象 不能使用延迟加载),所以基本不会使用
*
* 在设计的时候不是特殊情况不要使用双向关联
*/
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(null!=session)
HibernateUtil.closeSession(session);
}
}
把hibernate关联映射总结下,可终结。也为日后复习方便。