关联关系映射
1)一对多关联关系
Customer <--------->Order一对多双向关联关系,通过Customer可以知道Order,而通过Order同样的也可知道对应的Customer
Public class Customer implements Serilizable{
Private Long id;//--主键自动生成
Private String name;
Private Set<Order>orders = new HashSet<Order>();//一对多
....setter/getter.....
}
Public class Order implements Serilizable{
Private Long id;
Private String name;
Private Customer customer;//一对多
.....setter/getter....
}
Customer.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = “Customer” table = “customer_”>
<id name = “id” column = “id” type = “long”>
<generator class = “increment”></generator>
</id>
<property name= “name” column = “name” type = “string”/>
<!--配置一对多,通过外键关联,关联关系由多一方来维护:inverse = “false”
配置级联操作:cascade = “all”-->
<set name=”orders” inverse = “false” cascade = “all”>
<!--通过外键关联,配置对方的外键,值将根据一一方的主键生成-->
<key column = “customer_id”/>
<!--指定多一方的类型-->
<one-to-many class = “Order”>
</set>
</class>
</hibernate-mapping>
Order.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = “Order” table = “order_”>
<id name = “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<property name = “name” column = “name” type = “string”/>
<!--配置多对一,告诉一一方的类型class:由于是通过外键关联的,所以要配置得到
一一方时是通过外键来获取的-->
<many-to-one name = “customer” class = “Customer” column = “customer_id” ></many-to-one>
</class>
</hibernate-mapping>
Categary 自身关联一对多
Public class Categary implements Serilizable{
Private Long id;//hibernate自动生成
Private String name;
Private Categary parent;//指定一的一方属性;
Private Set<Categary>children = new HashSet<Categary>();//指定多的一方的属性
....setter/getter.....
}
Categary.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = “Categary” table = “categary_”>
<id name = “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<property name =”name” column = “name” type = “string”/>
<!--配置一对多一一方-->
<set name = “children” cascade = “all” inverse = “true”>
<!--配置关联的外键,值将根据一一方的主键生成-->
<key column = “categary_id”/>
<one-to-many class = “Categary”></one-to-many>
</set>
<!--配置多对一,多的一方-->
<many-to-one name= “parent” class = “Categary” column = “categary_id”/>
</class>
</hibernate-mapping>
2)一对一关联关系
分为两种情况:
A):主键关联 Person <-------> IdCard
Public class Person implememnts Serilizable{
Private Long id;//hibernate 自动生成
Private String name;
Private IdCard idCard;//一对一
...setter/getter....
}
Public class IdCard implements Serilizable{
Private Long id;//hibernate自动生成
Private String number;
Private Person person;//一对一
...setter/getter ....
}
Person.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name= “Person” table = “person_”>
<id name= “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<property name= “name” column = “name” type = “string”/>
<!--配置一对一-->
<one-to-one name= “idCard” class = “IdCard” cascade = “all”/>
</class>
</hibernate-mapping>
IdCard.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = “IdCard“ table = “idCard_”>
<!--配置主键关联,指定主键生成策略为引用外键,引用外键的值来自于属性
Property(固定):值为对应的关联对象的属性名-->
<id name= “id” column=”id” type= “long”>
<generator class = “foreign”>
<param name= “property”>person</param>
</generator>
</id>
<propery name= “number” column = “number” type = “string”/>
<!--配置一对一-->
<one-to-one name =”person” class = “Person“ cascade= “all”/>
</class>
</hibernate-mapping>
B):外键关联 Person <-------> IdCard
Person.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name= “Person” table = “person_”>
<id name= “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<property name= “name” column = “name” type = “string”/>
<!--配置一对一-->
<one-to-one name= “idCard” class = “IdCard” cascade = “all”/>
</class>
</hibernate-mapping>
IdCard.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = “IdCard“ table = “idCard_”>
<id name= “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<propery name= “number” column = “number” type = “string”/>
<!--配置一对一,由于<one-to-one/>没有column属性,因此无法使用他来指定
关联的外键,因此需要用到多对一,然后对该对多一做出限制即可unique = “true“-->
<many-to-one name= “person” column = “person_id” class = “Person” cascade = “all” unique = “true”>
</class>
</hibernate-mapping>
假设一种情况:Person与IdCard为一对一,而Person与Team 为一对多的关系。
由于一对一默认加载方式为左外连接立即检索,那么如果我需要查询一个IdCard对象,
而此时将会将对应的Person加载出来,同时也会将Person的Team对象查询出来
而进而引发将与Team对应的所有的Person一起查询,
为了查一个数据而附带出很多不需要的查询sql的情况出现.
可以通过配置查询深度来控制。
配置检索深度,需要在主配置文件中配置。hibernate.cfg.xml
<property name = “max_fetch_depth”>1</property>
3)多对多关联关系
Student <-----> Course 学生与课程多对多,使用中间表的形式来实现数据库层面的多对多。
Public class Student implements Serilizable{
Private Long id;//hibernate自动生成
Private String name;
Private Set<Course>courses = new HashSet<Course>();//一个学生多个课程
...setter/getter.....
}
Public class Course implements Serilizable{
Private Long id;//hibernate自动生成;
Private String name;
Private Set<Student>students = new HashSet<Student>();//一门课对应多个学生
....setter/getter....
}
Student.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name=”Student” table = “student_”>
<id name= “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<property name= “name” column = “name” type = “string”/>
<!--配置多对多,通过中间表的形式,cascade如何配置?要关注inverse只能配置给一方-->
<set name= “courses” table = “student_course” cascade = “save-update” lazy = “true” inverse = “true”>
<!--中间表的外键(关联到student表的外键)-->
<key column= “student_id”/>
<!--通过course_id在中间表的关联进而查询到course数据-->
<many-to-many class = “Course” column = “course_id”/>
</set>
</class>
</hibernate-mapping>
Course.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name= “Course” table = “course_”>
<id name= “id” column = “id“ type = “long”>
<generator class = “increment”/>
</id>
<property name= “name” column = “name” type = “string”/>
<!--配置多对多,通过中间表的形式-->
<set name= “students” table = “student_course” cascade = “save-update”>
<!--中间表的外键(关联course表的外键)-->
<key column = “course_id”/>
<!--配置多对多-->
<many-to-many class = “Student” column = “student_id”/>
</set>
</class>
</hibernate-mapping>
说明:配置多对多cascade = “all”就会将两张表与中间表关联的数据都删除,而一般的需求是只需要删除一方且同时删除中间表的关联数据即可,因此配置cascade = “save-update”即可。
实际应用问题:
在进行保存的时候,会建立一张中间表进行两张表关系的维护,
这样当随便保存哪一方的时候就会在中间表处出现一个插入重复数据
的错误,导致这样原因是:
当保存student对象时,student就会自己去维护多对多的关联关系,所以
同时就会向中间表插入两个表之间关系的数据记录。
而对于crouse对象而言也是同样的道理,
所以就会出现上面所描述的错误。
那么如何解决这个问题呢?
1):在hbm.xml配置文件中,对一方的设置<set inverse = "true">
表示由对方来维护这种多对多关联关系。所以在代码层次上进行保存的时候,
我们就必须要对持有维护关联关系的对象进行保存。
2):在代码层次上进行修改,最终你要保存哪个对象,只需要将两个对象中
进行保存的那个对象进行关联关系的建立即可,对另外一个就不要进行关联关系的建立。
4)集合映射Map
Team Student
分两种情况:
A):存放的是原子类型(一个映射文件生成两种表,一种存放Team信息,一张表存放其Map映射中的信息)
B):存放的是复杂类型(复杂类型单独存放在一张表中)
public class Team implements Serilizable{
Private Long id;//hibernate自动生成
Private String name;
Private Map<String,String> map1 = new HashMap<String,String>();//原子类型
Private Map<String,Person>map2 = new HashMap<String,Person>();//复杂类型
....setter/getter....
}
Public class Student implements Serilizable{
Private Long id;//主键
Private String name;
...setter/getter....
}
Team.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name= “Team” table = “team_”>
<id name= “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<property name= “name” column = “name” type = “string”/>
<!-- 配置简单类型的数据map映射,table存放的是map里面键值对的信息
key:team_map1表中的外键,其主键为外键+map中的键值字段构成,
生成的team_map1表字段有:team_id,mapkey,mapvalue-->
<map name = “map1” table = “team_map1” cascade = “all”>
<!--配置表关联的外键-->
<key column = “team_id”/>
<!--配置map中的键-->
<index column = “mapkey” type = “string”/>
<!--配置map中的值-->
<element column = “mapvalue” type = “string”/>
</map>
<!--配置复杂类型map映射,复杂类型为单独一张表student_-->
<map name = “map2” table = “student_” cascade = “all”>
<!--配置关联表student外键-->
<key column = “team_id”/>
<!--配置map中的键-->
<index column = “mapkey” type = “string”/>
<!--配置map中的值-->
<one-to-many class = “Student”/>
</map>
</class>
</hibernate-mapping>
Studnt.hbm.xml则按照正常的配置即可,生成的表student_拥有的字段有:student自己有的字段+(team_id,mapkey)主键
5)集合映射Set List Bag
Team Person
分两种情况:
A):存放的是原子类型(一个映射文件生成两种表,一种存放Team信息,一张表存放其Map映射中的信息)
B):存放的是复杂类型(复杂类型单独存放在一张表中)
Public class Team implements Serilizable{
Private Long id;
Private String name;
Private Set<Date> dates = new HashSet<Date>();//原子类型
Private Set<Student>students = new HashSet<Student>();//复杂类型
Private List<String>lists = new ArrayList<String>();//原子类型
Private List<Student>ss = new ArrayList<Student>()://复杂类型
....setter/getter..
}
Public class Student implements Serilizable{
Private Long id;//主键
Private String name;
...setter/getter....
}
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name= “Team” table = “team_”>
<id name= “id” column = “id” type = “long”>
<generator class = “increment”/>
</id>
<property name= “name” column = “name” type = “string”/>
<!--配置原子类型的set,存放set内容的表set1_-->
<set name= “dates” table = “set1_”>
<!--指定外键-->
<key column = “team_id”/>
<!--指定对应set存放的类型-->
<element column = “setValue” type = “date”/>
</set>
<!--配置原子类型list,存放list内容的表list_-->
<list name= “lists” table = “list_”>
<!--配合表list_的外键-->
<key column = “team_id”/>
<!--因为list是有顺序的,因此hibernate会在list_表中维护一个记录顺序的字段
我们只需要指定字段就可以了,值由hibernate维护-->
<index column = “index_”/>
<!--配置其值-->
<element column = “listValue” type = “string”/>
</list>
<!--使用Bag配置(可以重复,但是没有顺序的,结合了set与list)由hibernate提供的
在域模型中使用List来表示)
注意点:bag可以将inverser = true,多的一方是可以维护的-->
<bag name = “lists” table = “lists_”>
<!--配置外键-->
<key column = “team_id”/>
<element column = “student” type= “string”/>
</bag>
</class>
</hibernate-mapping>