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之間關係。多對多關聯中,在哪一方維護關聯關係都可以。