hibernate中的关联关系分为多对一关联,一对一关联和多对多关联。这几种关联关系很容易弄错,索性就记录在博客里。
一、多对一关联
多对一是最常见的关联关系,我们以QQ中的联系人(ContactPerson)和分组(Group)为例说明。ContactPerson与Group是多对一关系。
1.实体类
public class ContactPerson {
private int id;
private String name;
private Group group;
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.lql.demo;
import java.util.HashSet;
import java.util.Set;
public class Group {
private int id;
private String name;
private Set<ContactPerson> persons;
public void addPerson(ContactPerson person){
if(persons == null){
persons = new HashSet<ContactPerson>();
}
persons.add(person);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<ContactPerson> getPersons() {
return persons;
}
public void setPersons(Set<ContactPerson> persons) {
this.persons = persons;
}
}
需要注意一点:在ContactPerson中保存的是一个Group对象的引用,而不是groupId,但是数据库表中保存的是groupId(引用的是group的id),因为Hibernate是这样来设计的。如果ContactPerson中引用的是groupId,映射文件直接用<property name=”groupId”>即可,这样也就没有多对一联系。多对一联系中,查询时如果没有用到引用,Hibernate是不会发出查询的,只有当用到引用,hibernate才会发出查询语句
多对一的懒加载模式有proxy和false,no-proxy三种,proxy也就相当于true,no-proxy基本上不用
2、映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--ContactPerson 映射文件 -->
<hibernate-mapping
package="com.lql.demo">
<class name="ContactPerson" table="tb_contact" lazy="true">
<comment>Users may bid for or sell auction items.</comment>
<id name="id">
<generator class="native"/>
</id>
<property name="name" ></property>
<many-to-one name="group" column="groupid"></many-to-one>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Group映射文件 -->
<hibernate-mapping package="com.lql.demo">
<class name="Group" table="tb_group" lazy="true">
<comment>Users may bid for or sell auction items.</comment>
<id name="id">
<generator class="native" />
</id>
<property name="name"></property>
<!--
多对一联系,集合名为Group中persons key是在一的那张表里加一个字段groupid 在双向关联的中如果在某一方的关联配置中指定
inverse=”true” ,那么本方就不能维护两者之间的关联关系。关联关系就由对方一方来维护。Inverse
默认是false,双方都可以维护关联关系。维护关联关系的意思是可以再本方的对象中保存对方的引用。
extra:一种比较聪明的懒加载策略,即调用集合的size/contains等方法的时候,hibernate并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据
-->
<set name="persons" order-by="id desc" lazy="extra" inverse="true">
<key column="groupid"></key>
<one-to-many class="com.lql.demo.ContactPerson" />
</set>
</class>
</hibernate-mapping>
ContactPerson表
二、一对一联系
以人(Person)和身份证(IDCard)之间的关系为例,每个人和身份证的关系都是一对一的
1.实体类
package com.lql.demo;
public class Person {
private int id;
private String name;
private String address;
private IdCard idCard;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
}
package com.lql.demo;
public class IdCard {
private int id;
private String idCardNum;
private Person person;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getIdCardNum() {
return idCardNum;
}
public void setIdCardNum(String idCardNum) {
this.idCardNum = idCardNum;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
2.映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Person映射文件 -->
<hibernate-mapping
package="com.lql.demo">
<class name="Person" table="tb_person" lazy="true">
<comment>Users may bid for or sell auction items.</comment>
<id name="id">
<generator class="native"/>
</id>
<property name="name" ></property>
<property name="address"></property>
<one-to-one name="idCard"></one-to-one>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- IdCard映射文件 -->
<hibernate-mapping
package="com.lql.demo">
<class name="IdCard" table="tb_idcard" lazy="true">
<comment>Users may bid for or sell auction items.</comment>
<id name="id">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<property name="idCardNum" ></property>
<one-to-one name="person" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
IdCard表中的id会依赖与Person表中的id,并与其保持一致。
constrained只能在one-to-one的映射中使用,(一般在主表的映射中,有外键的那个表)。如果constrained=true, 则表明存在外键与关联表对应,并且关联表中肯定存在对应的键与其对应, 另外该选项最关键的是影响save和delete的先后顺序。例如增加的时候,如果constainted=true,则会先增加关联表,然后增加本表。 删除的时候反之。
Person person = new Person();//先生成
person.setName("狗蛋");
person.setAddress("北京");
session.save(person);
IdCard idCard = new IdCard();//后生成
idCard.setIdCardNum("123213213122313");
idCard.setPerson(person);
session.save(idCard);
注意如果将idCard.setPerson(person);改成person.setIdCard(idcard);就发产生异常
因为在配置映射的时候,Person的id是自动生成的, IdCard是依赖于Person的Id的,这里要特别注意。
Person表
IdCard表
三、多对多关联
这里以角色(role)和人(Person)来说明,一个人可以有多个角色(公司职员,丈夫,儿子等),一个角色也可以有多个人。所以角色和人是多对多的关系。
package com.lql.m2m;
import java.util.HashSet;
import java.util.Set;
public class Person2 {
private int id;
private String name;
private Set<Role> roles;
public void addRoleToPerson(Role role){
if(roles == null){
roles = new HashSet<Role>();
}
roles.add(role);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
package com.lql.m2m;
import java.util.HashSet;
import java.util.Set;
public class Role {
private int id;
private String name;
private Set<Person2> persons;
public void addPersonToRole(Person2 person){
if(persons == null){
persons = new HashSet<Person2>();
}
persons.add(person);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Person2> getPersons() {
return persons;
}
public void setPersons(Set<Person2> persons) {
this.persons = persons;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Role映射文件 -->
<hibernate-mapping package="com.lql.m2m">
<class name="Role" table="tb_role" lazy="true">
<comment>Users may bid for or sell auction items.</comment>
<id name="id">
<generator class="native" />
</id>
<property name="name" ></property>
<set name="persons" table="tb_person_role" lazy="extra">
<key column="roleid" ></key>
<many-to-many class="com.lql.m2m.Person2" column="personid"/>
</set>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Person2映射文件 -->
<hibernate-mapping package="com.lql.m2m">
<class name="Person2" table="tb_person2" lazy="true">
<comment>Users may bid for or sell auction items.</comment>
<id name="id">
<generator class="native" />
</id>
<property name="name" ></property>
<set name="roles" table="tb_person_role" lazy="extra">
<key column="personid" ></key>
<many-to-many class="com.lql.m2m.Role" column="roleid"/>
</set>
</class>
</hibernate-mapping>
多对多关联会单独生成一张,关联关系表。
这个表中保存的是,角色与Person之间关系。多对多关联中,在哪一方维护关联关系都可以。