参考:http://www.cnblogs.com/kubixuesheng/p/5300437.html
多对一单向外键关联关系
多对一关联时多方持有一方的引用。比如学生和班级,多个学生对应一个班级。
单向多对一关联中,(1)多方需要持有一方的引用,(2)多方(学生类)需要额外的配置,需要对持有一方引用的注解<many-to-one>
(3)多方必须保留一个不带参数的构造器 一方班级类无需做任何多余操作
<many-to-one>
具有以下属性:
name:一方的引用名
class:一方对应的持久化类
column:外键的列名
fetch:抓取策略
cascade:级联操作,有以下取值
- all 对所有操作进行级联操作
- save-update执行保存和更新操作时进行级联操作
- delete执行删除操作时进行级联操作
- none对所有操作不进行级联操作
多方:学生类和其对应的hbm.xml(因为hibernate具有二级缓存,缓存会将对象写进缓存,所以一般要实现serializable接口)
public class Student implements Serializable{
private int sid;
private String sname;
private String sex;
private Grade grade;//持有多方的引用
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Student() {
// TODO Auto-generated constructor stub
}
public Student(String sname, String sex) {
super();
this.sname = sname;
this.sex = sex;
}
}
<?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">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="entity.Student" table="student">
<id name="sid" type="java.lang.Integer">
<column name="sid" />
<generator class="increment" />
</id>
<property name="sname" type="java.lang.String">
<column name="sname" length="20" not-null="true"/>
</property>
<property name="sex" type="java.lang.String">
<column name="sex"></column>
</property>
<many-to-one name="grade" class="entity.Grade" column="gid" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
一方:班级类(无需多余定义)
public class Grade implements Serializable {
private int gid;
private String gname;
private String gdesc;
public int getGid() {
return gid;
}
public void setGid(int gid) {
this.gid = gid;
}
public String getGname() {
return gname;
}
public void setGname(String gname) {
this.gname = gname;
}
public String getGdesc() {
return gdesc;
}
public void setGdesc(String gdesc) {
this.gdesc = gdesc;
}
public Grade(String gname, String gdesc) {
super();
this.gname = gname;
this.gdesc = gdesc;
}
public Grade() {
// TODO Auto-generated constructor stub
}
}
<?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">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="entity.Grade" table="grade">
<id name="gid" type="java.lang.Integer">
<column name="gid" />
<generator class="increment" />
</id>
<property name="gname" type="java.lang.String">
<column name="gname" length="20" not-null="true"/>
</property>
<property name="gdesc" type="java.lang.String">
<column name="gdesc" length="50"></column>
</property>
</class>
</hibernate-mapping>
HibernateSessionFactoryUtil类用于获得sessionFactory和获得Session
public class HibernateSessionFactoryUtil {
private static SessionFactory sessionFactory;
private static Session session;
static {
//创建Configuration对象,读取hibernate.cfg.xml文件,完成初始化
Configuration cof = new Configuration().configure();
//ServiceRegistry 是 Service 的注册表, 它为Service提供了一个统一的加载 /初始化 /存放 /获取机制.
ServiceRegistryBuilder ssrb = new ServiceRegistryBuilder()
.applySettings(cof.getProperties());
ServiceRegistry ssr=ssrb.buildServiceRegistry();
sessionFactory=cof.buildSessionFactory(ssr);
}
public static SessionFactory getSessionFactory(){
return sessionFactory;
}
public static Session getSession(){
session=sessionFactory.openSession();
return session;
}
public static void closeSession(Session session){
if(session!=null)
session.close();
}
}
下面进行测试
public class Test {
public static void main(String[] args) {
add();
}
public static void add(){
Grade g=new Grade("java开发","java开发一班");
Student stu1=new Student("张三","女");
Student stu2=new Student("李四","男");
Session session=HibernateSessionFactoryUtil.getSession();
Transaction tx=session.beginTransaction();
stu1.setGrade(g);
stu2.setGrade(g);
//由于进行了级联操作,只需save学生类,班级类会自动存储
session.save(stu1);
session.save(stu2);
tx.commit(); HibernateSessionFactoryUtil.closeSession(session);
}
hibernate.cfg.xml加上:
<mapping resource="entity/Grade.hbm.xml" />
<mapping resource="entity/Student.hbm.xml"/>
执行结果:
控制台输出:
Hibernate: select max(sid) from student
Hibernate: select max(gid) from grade
Hibernate: insert into grade (gname, gdesc, gid) values (?, ?, ?)
Hibernate: insert into student (sname, sex, gid, sid) values (?, ?, ?, ?)
Hibernate: insert into student (sname, sex, gid, sid) values (?, ?, ?, ?)
数据库添加的记录:
student表
grade表
一对多单向外键关联
当类与类建立了关联,程序能很方便的从一个对象导航到另一个或一组与之关联的对象,有了student对象,就可以通过student对象得到grade的信息–student.getGrade(),对于班级对象,如果想要得到学生的信息,怎么办呢?这时候可以反过来控制,一方控制多方,接下来进行一对多单向外键关联
(1)一方持有多方的集合(2)一方的hbm.xml有<set>
配置,其含有子标签<one-to-many>
和<key>
子标签(3)不管是一对多还是多对一,多方都要保留无参构造器
多方:grade增加了Set<Stuent>
public class Grade implements Serializable {
private int gid;
private String gname;
private String gdesc;
//在一方定义多方的集合
private Set<Student> students=new HashSet<Student>();
public int getGid() {
return gid;
}
public void setGid(int gid) {
this.gid = gid;
}
public String getGname() {
return gname;
}
public void setGname(String gname) {
this.gname = gname;
}
public String getGdesc() {
return gdesc;
}
public void setGdesc(String gdesc) {
this.gdesc = gdesc;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
public Grade(String gname, String gdesc) {
super();
this.gname = gname;
this.gdesc = gdesc;
}
public Grade() {
// TODO Auto-generated constructor stub
}
}
Grade.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">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="entity.Grade" table="grade">
<id name="gid" type="java.lang.Integer">
<column name="gid" />
<generator class="increment" />
</id>
<property name="gname" type="java.lang.String">
<column name="gname" length="20" not-null="true"/>
</property>
<property name="gdesc" type="java.lang.String">
<column name="gdesc" length="50"></column>
</property>
<set name="students" table="student" inverse="false" cascade="save-update">
<key column="gid"></key>
<one-to-many class="entity.Student"/>
</set>
</class>
<!-- 添加了set,name表示Grade类中Set变量的名字,table表示对应的数据库中的表,inverse指定关联关系的控制方向,默认由one方控制,也就是在多方的inverse属性,有关这个理解见我另一篇博文 -->
student类不做任何添加,在这里省去
下面执行test
public class Test {
public static void main(String[] args) {
add();
}
public static void add(){
Grade g=new Grade("java开发","java开发一班");
Student stu1=new Student("张三","女");
Student stu2=new Student("李四","男");
Session session=HibernateSessionFactoryUtil.getSession();
Transaction tx=session.beginTransaction();
g.getStudents().add(stu1);
g.getStudents().add(stu2);
session.save(g);
tx.commit();
HibernateSessionFactoryUtil.closeSession(session);
}
控制台输出:
Hibernate: select max(gid) from grade
Hibernate: select max(sid) from student
Hibernate: insert into grade (gname, gdesc, gid) values (?, ?, ?)
Hibernate: insert into student (sname, sex, sid) values (?, ?, ?)
Hibernate: insert into student (sname, sex, sid) values (?, ?, ?)
Hibernate: update student set gid=? where sid=?
Hibernate: update student set gid=? where sid=?
数据库数据也载入正常
总结:多对一的时候,多方设置EAGER(积极加载),一方设置LAZY(懒加载),也就是说,如果是多对一,多方控制一方,那么多方设置积极加载,一方无需多余配置,反过来,如果是一对多关系,一方控制多方,那么一方设置懒加载,多方无需多余配置,但是不论哪种,多方都显式加上一个不带参数的构造器。(其实对于懒加载与否,这些配置是默认的)
一对多双向关联
其实类似之前的一对一双向外键关联,也是互相持有对方的引用,故也叫双向一对多自身关联。多方持有一方的引用,反过来,一方也持有多方的集合
多对多单向关联
在多对多关联关系中,显然不能互相持有对方主键做外键,那么就需要用到一个新的表作为中间表进行映射
举例:学生和老师
创建表teacher和teachers_students
create table teacher(
-> tid int primary key,
-> tname varchar(20));
create table teachers_students(
-> sid int not null,
-> tid int not null);
alter table teachers_students add primary key (sid,tid);
Student类新增:
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
public Grade getGrade() {
return grade;
}
Student.hbm.xml新增
<!-- table设置中间表 -->
<set name="teachers" table="teachers_students" cascade="all" inverse="false">
<!-- key设置本对象在中间表的外键sid -->
<key column="sid" />
<!-- tid设置对方的表(老师)在中间表的外键tid -->
<many-to-many class="entity.Teacher" column="tid"></many-to-many>
</set>
老师不做多余配置
public class Teacher implements Serializable{
private int tid;
private String tname;
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
public String getTname() {
return tname;
}
public void setTname(String tname) {
this.tname = tname;
}
public Teacher(String tname) {
super();
this.tname = tname;
}
public Teacher() {
super();
// TODO Auto-generated constructor stub
}
}
<?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">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="entity.Teacher" table="teacher">
<id name="tid" type="java.lang.Integer">
<column name="tid" />
<generator class="increment" />
</id>
<property name="tname" type="java.lang.String">
<column name="tname" length="20" not-null="true"/>
</property>
</class>
</hibernate-mapping>
测试:
Student s=new Student("学生1","女");
Set<Teacher> teas=s.getTeachers();
Teacher t1=new Teacher("张三");
Teacher t2=new Teacher("李四");
Teacher t3=new Teacher("王五");
teas.add(t1);
teas.add(t2);
teas.add(t3);
Session session=HibernateSessionFactoryUtil.getSession();
Transaction tx=session.beginTransaction();
session.save(s);
tx.commit();
HibernateSessionFactoryUtil.closeSession(session);
控制台输出
Hibernate: select max(sid) from student
Hibernate: select max(tid) from teacher
Hibernate: insert into student (sname, sex, gid, sid) values (?, ?, ?, ?)
Hibernate: insert into teacher (tname, tid) values (?, ?)
Hibernate: insert into teacher (tname, tid) values (?, ?)
Hibernate: insert into teacher (tname, tid) values (?, ?)
Hibernate: insert into teachers_students (sid, tid) values (?, ?)
Hibernate: insert into teachers_students (sid, tid) values (?, ?)
Hibernate: insert into teachers_students (sid, tid) values (?, ?)
数据库存储正常
多对多双向外键关联
和之前的类似,是互相持有对方的集合,双方持有对方的集合对象
Teacher类添加:
private Set<Student> students=new HashSet<Student>();
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
Teacher.hbm.xml添加
<set name="students" table="teachers_students" cascade="all" inverse="false">
<key column="tid"></key>
<many-to-many class="entity.Student" column="sid"></many-to-many>
</set>
关联关系的优缺点
用关联关系,就可以直接操作内存中的对象,不用每次都查询数据库,会提高效率;而且域模型真实反映了客观世界的关系,但是缺点就是建立复杂的关联关系会给程序开发带来麻烦,当修改一个对象时,会牵连其它的对象
问题小结
(1)注意在多对一/一对多关系里:多方必须保留一个不带参数的构造器!
(2)如果没有设置级联ALL,那么需要在保存的时候先保存班级,在保存学生,否则出错: object references an unsaved transient instance - save the transient instance before flushing:
(3)多对一时候,多方设置EAGER加载,一对多的时候,一方设置LAZY加载
(4)多对多关联,多方需要保留一个无参构造器。