Hibernate框架是一个自动生成数据表,sql语句的超强框架,一般的增删改查操作起来与我们之前操作数据库没什么大的区别,调用相应的方法即可.但是当我们的数据表中有外键关联,并且要处理这些外键关联的对象数据时,hibernate提供了独特并且更有效的方法.
数据表与数据表的外键关联主要是用于对象的一对多(多对一)和多对多的情况.hibernate也是针对这两种情况作了一些处理
一对多(多对一)
这里假设有一组模型是父亲(Fathers)和儿子(Sons),一个父亲可以有多个儿子,但是一个儿子只有一个父亲,是一个一对多的模型
首先java实体类的书写
public class Fathers implements Serializable{
private String id;
private String username;
private Set<Sons> sons = new HashSet<Sons>();
get/set...
}
public class Sons implements Serializable{
private String id;
private String name;
private Fathers father;
set/get...
}
在这个模型中,一对多,一指的是father,多指的是sons.在java实体中"一"的那一方需要定义一个泛型是"多"的set,用来存放信息,"多"的那一方则需要定义一个"一"类型的成员变量在这个例子中,father的类中需要放一个sons类型的set.Sons类中需要放一个fathers成员变量.
配置文件书写
Fathers.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="domain" >
<class name="Fathers" table="father" >
<id name="id">
<generator class="uuid"></generator>
</id>
<property name="username" column="username"></property>
<!-- inverse反转控制,把维护的权利交出去-->
<!-- name:"一"的那一方的set的名称
colume:"多"的那一方生成表时的外键名
class:"多"的那一方的类名
-->
<set name="sons" inverse="true">
<key column="fatherid"></key>
<one-to-many class="Sons"></one-to-many>
</set>
</class>
</hibernate-mapping>
Sons.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="domain" >
<class name="Sons" table="sons" >
<id name="id">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" ></property>
<!--
name:"一"的那一方的数据成员的名称
colume:"多"的那一方的表中的外键名
class:"一"的那一方的类名
-->
<many-to-one name="father" column="fatherid" class="Fathers"></many-to-one>
</class>
</hibernate-mapping>
在father的配置文件中,有个invers反转控制的配置,在hibernate中,每个实体都要维护他本书对应的那张数据表,外键把两张表(两个实体)联系了起来,所以两个实体在维护两张表时可能会出现重复的操作.可以理解为每一个实体类都会维护它本身和属于他本身的外键.所以father每次都要检查一下sons表中的外键是否正确,而且sons还会自己检查一遍表中外键是否正确,所以导致外键被检查了多次,发生了重复维护的情况.invers配置配置为true则说明father这个实体不在对sons中的这个外键进行维护(因为还有sons在维护,所以不用担心没人维护).这样减少了多余重复的检查操作.
注:invers配置在一对多中可以理解为是提高效率的配置,不是必须的,而且在配置invers时一定要保证有一方能维护外键.
测试
@Test
public void test1(){
Configuration cf = new Configuration().configure();
SessionFactory sf = cf.buildSessionFactory();
Session session = sf.openSession();
Transaction transaction = session.beginTransaction();
//创建对象
Fathers fathers = new Fathers();
fathers.setUsername("老王");
Sons s1 = new Sons();
Sons s2 = new Sons();
s1.setName("张三");
s2.setName("李四");
//相互为实体对象中添加信息
fathers.getSons().add(s1);
fathers.getSons().add(s2);
s1.setFather(fathers);
s2.setFather(fathers);
//保存操作
session.save(fathers);
session.save(s1);
session.save(s2);
transaction.commit();
session.close();
}
多对多
这里假设我们有一组模型是老师和学生模型,一个老师可以有多个学生,一个学生有多个老师
java实体类
public class Student implements Serializable {
private String id;
private String name;
Set<Teacher> tealist = new HashSet<Teacher>();
set/get...
}
public class Teacher implements Serializable{
private String tid;
private String name;
private Set<Student> stulist = new HashSet<Student>();
set/get..
}
在多对多中每个实体中都要对方的set.
配置文件
<?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">
<!-- 配置表与实体对象的关系 -->
<!-- package属性:填写一个包名.在元素内部凡是需要书写完整类名的属性,可以直接写简答类名了. -->
<hibernate-mapping package="domain" >
<class name="Student" table="student" >
<id name="id">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" ></property>
<!--
name:自己类中的set名称
table:新生成的表名
key-colume:新表中自己的外键
class:多对多对方的类名
many-to-many-colume:新表中对方的外键
-->
<set name="tealist" table="grade" inverse="true">
<key column="id"></key>
<many-to-many class="Teacher" column="tid"></many-to-many>
</set>
</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">
<!-- 配置表与实体对象的关系 -->
<!-- package属性:填写一个包名.在元素内部凡是需要书写完整类名的属性,可以直接写简答类名了. -->
<hibernate-mapping package="domain" >
<class name="Teacher" table="teacher" >
<id name="tid">
<generator class="uuid"></generator>
</id>
<property name="name" column="name" ></property>
<!--
name:自己类中的set名称
table:新生成的表名
key-colume:新表中自己的外键
class:多对多对方的类名
many-to-many-colume:新表中对方的外键
-->
<set name="stulist" table="grade">
<key column="tid"></key>
<many-to-many class="Student" column="id"></many-to-many>
</set>
</class>
</hibernate-mapping>
配置方法是一样的.在多对多的情况中,invers反转控制是必须要配置的.在一对多中,维护外键的方式是检查另一张表中的外键信息,在多对多中不同,两张表有多对多关系时,通常会再创建一张新的表去描述多对多的关系,这时维护外键的方法就变成想这个新表中插入相应的外键记录了,所以当两张表都去维护外键时,就会发生同一条数据被插入两次的情况,这时数据库就会报错,所以这多对多的两张表肯定要有一方放弃外键的维护工作(invers=true).具体哪一方放弃就要看业务逻辑了.
测试
@Test
public void test2(){
Configuration cf = new Configuration().configure();
SessionFactory sf = cf.buildSessionFactory();
Session session = sf.openSession();
Transaction transaction = session.beginTransaction();
Teacher t1 = new Teacher();
t1.setName("小明");
Teacher t2 = new Teacher();
t2.setName("小白");
Student s1 = new Student();
s1.setName("张三");
Student s2 = new Student();
s2.setName("李四");
//向teacher中添加student的信息
t1.getStulist().add(s1);
t1.getStulist().add(s2);
t2.getStulist().add(s1);
t2.getStulist().add(s2);
//向student中添加teacher信息
s1.getTealist().add(t1);
s1.getTealist().add(t2);
s2.getTealist().add(t1);
s2.getTealist().add(t2);
//保存数据
session.save(t1);
session.save(t2);
session.save(s1);
session.save(s2);
transaction.commit();
session.close();
}