一对一关联
一对一关联包括如下的两种类型:
1. 主键关联
2. 唯一外键关联
主键关联
一对一的主键关联形式,即两张关联表通过主键形成一对一映射关系。
下面是一个例子,oham 阵型与lulu阵型配对进行精神单挑,要求两组阵型人数相同,一对一PK:
TOham.java
package learnHibernate.bean;
import java.io.Serializable;
public class TOham implements Serializable {
private static final long serialVersionUID = -3474820872619575460L;
private int id;
private String name;
private int groupId;
private String meditation;
private TLulu lu;
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 int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public TLulu getLu() {
return lu;
}
public void setLu(TLulu lu) {
this.lu = lu;
}
public String getMeditation() {
return meditation;
}
public void setMeditation(String meditation) {
this.meditation = meditation;
}
}
TLulu.java
package learnHibernate.bean;
import java.io.Serializable;
public class TLulu implements Serializable {
private static final long serialVersionUID = -252962688967803016L;
private int id ;
private String name;
private String sixthSense;
private TOham oh;
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 TOham getOh() {
return oh;
}
public void setOh(TOham oh) {
this.oh = oh;
}
public String getSixthSense() {
return sixthSense;
}
public void setSixthSense(String sixthSense) {
this.sixthSense = sixthSense;
}
}
TOham.hbm.xml
<?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">
<hibernate-mapping package="learnHibernate.bean">
<class name="TOham" table="t_oham">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name"
column="name"
type="java.lang.String" />
<property name="meditation"
column="meditation"
type="java.lang.String" />
<property name="groupId"
column="group_id"
type="java.lang.Integer" />
<!-- 通过one-to-one节点,将Toham类与TLulu类相关联 -->
<!-- cascade属性,当主控方执行操作的时候,关联对象是否执行同一操作,
如主控对象save-update-delete的时候,是否也同时对关联对象做相同动作,
此处为“all”,表示对关联类做同样的动作 -->
<one-to-one name="lu"
class="TLulu"
cascade="all"
outer-join="true">
</one-to-one>
</class>
</hibernate-mapping>
TLulu.hbm.xml
<?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">
<hibernate-mapping package="learnHibernate.bean">
<class name="TLulu" table="t_lulu">
<id name="id" column="id" type="java.lang.Integer">
<!-- t_oham与t_lulu采用了主键关联方式,所以关联的记录主键值必须保持同步 -->
<!-- 所以只需为一张表设定主键生成器,而另一张表的主键与之共享相同的主键值 -->
<!-- 此处用foreign类型的主键生成器,表示t_lulu的主键id同时为外键,
通过property:oh的TOham类表明关联的映射表为t_oham -->
<generator class="foreign">
<param name="property">oh</param>
</generator>
</id>
<!-- constrained为true,表明表t_oham的主键,被当前表t_lulu的外键关联着 -->
<one-to-one name="oh"
class="TOham"
constrained="true"/>
<property name="name"
column="name"
type="java.lang.String" />
<property name="sixthSense"
column="sixthsense"
type="java.lang.String" />
</class>
</hibernate-mapping>
执行代码:
package learnHibernate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.junit.Test;
import learnHibernate.bean.TLulu;
import learnHibernate.bean.TOham;
import learnHibernate.util.HibernateLocalUtil;
public class TestCase7 {
@Test
public void saveOrUpdateOhamlulu() {
TOham oh = new TOham();
oh.setName("Oham");
oh.setMeditation("foresee");
TLulu lu = new TLulu();
lu.setName("Lulu");
lu.setSixthSense("Perspective");
//互相设置关联,否则会抛
//org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property
oh.setLu(lu);
lu.setOh(oh);
SessionFactory factory = HibernateLocalUtil.getSessionFactory();
Session session = factory.openSession();
Transaction tx = session.beginTransaction();
//由于TOham映射文件中的one-to-one节点设置了cascade="all"
//此处会被联级保存,若设置为"none",则只保存TOham
session.save(oh);
tx.commit();
session.close();
}
//@Test
public void getOhamlulu() {
SessionFactory factory = HibernateLocalUtil.getSessionFactory();
Session session = factory.openSession();
TOham oh = (TOham)session.load(TOham.class, new Integer(9));
//TLulu lu = (TLulu)session.load(TLulu.class, new Integer(9));
//System.out.println(oh.getName());
//System.out.println(lu.getName());
session.close();
}
}
后台输出sql:
Hibernate:
insert
into
t_oham
(name, meditation, group_id)
values
(?, ?, ?)
Hibernate:
insert
into
t_lulu
(name, sixthsense, id)
values
(?, ?, ?)
然后执行查询代码:
TOham oh = (TOham)session.load(TOham.class, new Integer(9));
//因为TOham默认为延迟加载,调用对象时才会执行查询数据库
System.out.println(oh.getName());
后台输出sql:
Hibernate:
select
toham0_.id as id0_1_,
toham0_.name as name0_1_,
toham0_.meditation as meditation0_1_,
toham0_.group_id as group4_0_1_,
tlulu1_.id as id1_0_,
tlulu1_.name as name1_0_,
tlulu1_.sixthsense as sixthsense1_0_
from
t_oham toham0_
left outer join
t_lulu tlulu1_
on toham0_.id=tlulu1_.id
where
toham0_.id=?
注意:若将TOham.hbm.xml的outer-join设置为“false”,重新执行查询代码,结果:
后台输出sql:
Hibernate:
select
toham0_.id as id0_0_,
toham0_.name as name0_0_,
toham0_.meditation as meditation0_0_,
toham0_.group_id as group4_0_0_
from
t_oham toham0_
where
toham0_.id=?
Hibernate:
select
tlulu0_.id as id1_0_,
tlulu0_.name as name1_0_,
tlulu0_.sixthsense as sixthsense1_0_
from
t_lulu tlulu0_
where
tlulu0_.id=?
这时,再把TLulu.hbm.xml的constrained设置为“false”,执行代码,结果:
Hibernate:
select
toham0_.id as id0_0_,
toham0_.name as name0_0_,
toham0_.meditation as meditation0_0_,
toham0_.group_id as group4_0_0_
from
t_oham toham0_
where
toham0_.id=?
Hibernate:
select
tlulu0_.id as id1_1_,
tlulu0_.name as name1_1_,
tlulu0_.sixthsense as sixthsense1_1_,
toham1_.id as id0_0_,
toham1_.name as name0_0_,
toham1_.meditation as meditation0_0_,
toham1_.group_id as group4_0_0_
from
t_lulu tlulu0_
left outer join
t_oham toham1_
on tlulu0_.id=toham1_.id
where
tlulu0_.id=?
说实话,在下不太明白constrained的具体作用,只知道当在采用主键关联的方式去做一对一关联时,从表(这里的例子t_lulu)的映射配置里id 的foreign类generator必须与constrained设置为true配对出现。
注意的一点是主从关系,我一般这样理解,持有外键的表是从表,是关联方,而被关联的表是主表,是主控方,被关联方,在hibernate的映射当中主从理清关系很重要,因为涉及级联操作,弄不好会造成逻辑混乱。
唯一外键关联
顾名思义,通过非主键的唯一外键来做一对一关联,现在举一例子:先前oham与lulu两阵型心灵单挑进行中,可惜两组总体水平相去甚远,现在oham组要求组内每人可以召唤帮托,但为兼顾公平,现在只允许每人召唤一个,并且只能是cancan类型的,还有不能召唤同一人。。。
TOham.java:
package learnHibernate.bean;
import java.io.Serializable;
public class TOham implements Serializable {
private static final long serialVersionUID = -3474820872619575460L;
private int id;
private String name;
private int groupId;
private String meditation;
private TLulu lu;
private TCancan can;
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 int getGroupId() {
return groupId;
}
public void setGroupId(int groupId) {
this.groupId = groupId;
}
public TLulu getLu() {
return lu;
}
public void setLu(TLulu lu) {
this.lu = lu;
}
public String getMeditation() {
return meditation;
}
public void setMeditation(String meditation) {
this.meditation = meditation;
}
public TCancan getCan() {
return can;
}
public void setCan(TCancan can) {
this.can = can;
}
}
TCancan.java:
package learnHibernate.bean;
public class TCancan {
private int id;
private String name;
private String think;
private TOham oh;
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 getThink() {
return think;
}
public void setThink(String think) {
this.think = think;
}
public TOham getOh() {
return oh;
}
public void setOh(TOham oh) {
this.oh = oh;
}
}
TOham.hbm.xml:
<?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">
<hibernate-mapping package="learnHibernate.bean">
<class name="TOham" table="t_oham">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name"
column="name"
type="java.lang.String" />
<property name="meditation"
column="meditation"
type="java.lang.String" />
<property name="groupId"
column="group_id"
type="java.lang.Integer" />
<one-to-one name="lu"
class="TLulu"
cascade="all"
outer-join="true">
</one-to-one>
<!-- 外键关联,一般是多对一的,唯一外键关联只是其中一个特例,
所以此处用了many-to-one,并设置unqiue为true,表明此外键唯一 -->
<many-to-one name="can"
class="TCancan"
column="friend_id"
unique="true"/>
</class>
</hibernate-mapping>
TCancan.hbm.xml:
<?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">
<hibernate-mapping package="learnHibernate.bean">
<class name="TCancan" table="t_cancan">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native" />
</id>
<!--<one-to-one name="oh"
class="TOham"
property-ref="can" />-->
<property name="name"
column="name"
type="java.lang.String" />
<property name="think"
column="think"
type="java.lang.String" />
</class>
</hibernate-mapping>
执行代码:
TOham oh = (TOham)session.load(TOham.class, new Integer(6));
System.out.println(oh.getCan().getName());
结果:
Hibernate:
select
toham0_.id as id0_1_,
toham0_.name as name0_1_,
toham0_.meditation as meditation0_1_,
toham0_.group_id as group4_0_1_,
toham0_.friend_id as friend5_0_1_,
tlulu1_.id as id1_0_,
tlulu1_.name as name1_0_,
tlulu1_.sixthsense as sixthsense1_0_
from
t_oham toham0_
left outer join
t_lulu tlulu1_
on toham0_.id=tlulu1_.id
where
toham0_.id=?
Hibernate:
select
tcancan0_.id as id2_0_,
tcancan0_.name as name2_0_,
tcancan0_.think as think2_0_
from
t_cancan tcancan0_
where
tcancan0_.id=?
此处要注意的是因为先前的例子我们做了主键一对一关联,所以此处left join了t_lulu表,若是用单纯的唯一外键映射例子做实验,此处的sql可能是left outer join的,可以去试试,因为many-to-one的outer-join默认为true。这是hibernate底层优化的结果。
另外 注意若不调用oh下面的can对象的话hibernate不会去执行查询t_canca的操作。
此处在hibernate的映射层面是个单向的关联,也就是说,执行下面代码:
TCancan can = (TCancan)session.load(TCancan.class, new Integer(1));
System.out.println(can.getOh());
结果是hibernate只会查询t_cancan表,不会去查t_oham表:
Hibernate:
select
tcancan0_.id as id2_0_,
tcancan0_.name as name2_0_,
tcancan0_.think as think2_0_
from
t_cancan tcancan0_
where
tcancan0_.id=?
若我们把TCancan.hbm.xml中的one-to-one节点的注释放开:
<one-to-one name="oh"
class="TOham"
property-ref="can" />
其中property-ref指定为TOham的属性名为can,意思是TOham的can就是关联着本类TCancan的。这样就实现了映射层面的双向关联。
在执行代码:
TCancan can = (TCancan)session.load(TCancan.class, new Integer(1));
System.out.println(can.getOh());
结果把t_cancan关联进来了:
Hibernate:
select
tcancan0_.id as id2_2_,
tcancan0_.name as name2_2_,
tcancan0_.think as think2_2_,
toham1_.id as id0_0_,
toham1_.name as name0_0_,
toham1_.meditation as meditation0_0_,
toham1_.group_id as group4_0_0_,
toham1_.friend_id as friend5_0_0_,
tlulu2_.id as id1_1_,
tlulu2_.name as name1_1_,
tlulu2_.sixthsense as sixthsense1_1_
from
t_cancan tcancan0_
left outer join
t_oham toham1_
on tcancan0_.id=toham1_.friend_id
left outer join
t_lulu tlulu2_
on toham1_.id=tlulu2_.id
where
tcancan0_.id=?
此处就引申了一个问题,若一个实体的映射关联多了,就不能总让其join来join去了,那么就要考虑如何设置映射配置,使得性能最优化。。。