一对一关联有两种关联方式,即:主键关联和外键关联。
下面将分别对这两种情况进行演示和讨论。在讨论前,首先给出这次用到得POJO,和相应的映射文件的主要片段。
Member类:
public class Member {
private String id;
private String name;
private Integer age;
private IdCard card;
.......
}
IdCard类:
public class IdCard {
private String id;
private String num;
private Member member;
........
}
先对这两个类解释一下,Member类代表人员,IdCard类代表了身份证,这在现实中也是一对一的关系。
1、主键关联
Member. hbm.xml的主要片段如下:
<hibernate-mapping package="org.louis.domain">
<class name="Member" table="TEST_MEMBER">
<id name="id" column="ID">
<generator class="uuid.hex"></generator>
</id>
<property name="age" column="AGE"></property>
<property name="name" column="NAME"></property>
<one-to-one name="card" class="IdCard" cascade="all"></one-to-one>
.........
</hibernate-mapping>
IdCard.hbm.xml的配置如下:
<hibernate-mapping package="org.louis.domain">
<class name="Order" table="TEST_ORDER">
<id name="id" column="ID">
<generator class="foreign"><!--使用主键关联,引用Member对应表的主键作为自己的主键-->
<param name="property">member</param><!--此处的member必须和下面配置的one-to-one的name 属性一样 -->
</generator>
</id>
<property name="num" column="NUM"></property>
<one-to-one name="member" class="Member" constrained="true"></one-to-one>
</class>
</hibernate-mapping>
至此,所有的配置工作已经完成。
测试进行测试:
a、新增数据
public void insert(){
Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
session.beginTransaction();
Member m = new Member();
m.setAge(24);
m.setName("Louis");
IdCard card = new IdCard();
card.setNum("123456789");
card.setMember(m);//设置Member和IdCard关系,以便IdCard可以从Member取得主键值
m.setCard(card);//设置Member和IdCard关系
session.save(m);
session.getTransaction().commit();
}
后台Hibernate执行的SqlServer语句是:
Hibernate: insert into TEST_MEMBER (AGE, NAME, ID) values (?, ?, ?)
Hibernate: insert into TEST_IDCARD (NUM, ID) values (?, ?)
保存IdCard时使用IdCard对象中member对象的Id来为自己的Id赋值的。你现在可以查看数据库,里面两个表的主键值相同。
b、加载数据
public void getMemberById(String id){
Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
session.beginTransaction();
Member m = (Member)session.load(Member.class, id);
System.out.println(m);
session.getTransaction().commit();
}
后台Hibernate执行的Sql语句是:
select
member0_.ID as ID5_1_,
member0_.AGE as AGE5_1_,
member0_.NAME as NAME5_1_,
idcard1_.ID as ID4_0_,
idcard1_.NUM as NUM4_0_
from
TEST_MEMBER member0_
left outer join
TEST_IDCARD idcard1_
on member0_.ID=idcard1_.ID
where
member0_.ID=?
可以看出默认采用的左外连接 的查询。
如果把Member.hbm.xml中做些改动如:
<one-to-one name="card" class="IdCard" fetch="select" cascade="all"></one-to-one>
再次执行查询Sql语句如下:
Hibernate:
select
member0_.ID as ID5_0_,
member0_.AGE as AGE5_0_,
member0_.NAME as NAME5_0_
from
TEST_MEMBER member0_
where
member0_.ID=?
Hibernate:
select
idcard0_.ID as ID4_0_,
idcard0_.NUM as NUM4_0_
from
TEST_IDCARD idcard0_
where
idcard0_.ID=?
可以看出生成了两条SQL语句,明显要比一条的性能低一些。
c、删除操作
Hibernate:
select
member0_.ID as ID5_0_,
member0_.AGE as AGE5_0_,
member0_.NAME as NAME5_0_
from
TEST_MEMBER member0_
where
member0_.ID=?
Hibernate:
select
idcard0_.ID as ID4_0_,
idcard0_.NUM as NUM4_0_
from
TEST_IDCARD idcard0_
where
idcard0_.ID=?
Hibernate:
delete
from
TEST_IDCARD
where
ID=?
Hibernate:
delete
from
TEST_MEMBER
where
ID=?
前两条Sql语句是加载Member和关联的IdCard对象。由于在测试加载数据时改变了抓取数据方式为fetch="select"所以生成了两条,如果改为默认(fetch="join")则只生成一条,并且采用的是左外连接方式。在删除代码中,我只是删除了Member,但是查看数据库和后台的SQL语句看出也把IdCard数据删除了,这是因为我在Member.hbm.xml中的<one-to-one>设置了cascade="all"。如果不设置(默认为“none”),则只会删除Member对象,而不会级联删除IdCard对象。
注意:在这个例子中,一定要注意添加数据、删除数据的先后顺序。添加数据必须先添加Member(因为IdCard的主键由Member而来),删除数据必须先删除IdCard(因为在IdCard.hbm.xml中设置了constrained="true"而使得两个表之间有了约束)。
2、外键关联
使用外键关联,我们还是使用这两个POJO类,只是其映射文件有所调整。调整后的映射文件如下:
Member.hbm.xml :
<hibernate-mapping package="org.louis.domain">
<class name="Member" table="TEST_MEMBER">
<id name="id" column="ID">
<generator class="uuid.hex"></generator>
</id>
<property name="age" column="AGE"></property>
<property name="name" column="NAME"></property>
<one-to-one name="card" class="IdCard" fetch="join" cascade="all" property-ref="member"></one-to-one>
......
</hibernate-mapping>
IdCard.hbm.xml :
<hibernate-mapping package="org.louis.domain">
<class name="IdCard" table="TEST_IDCARD">
<id name="id" column="ID">
<generator class="uuid.hex">
</generator>
</id>
<property name="num" column="NUM"></property>
<many-to-one name="member" class="Member" unique="true" column="MEMBER_ID"></many-to-one>
</class>
</hibernate-mapping>
依然采用上面的测试代码进行测试:
a、插入数据
后台Hibernate执行的SQL语句
Hibernate:
insert
into
TEST_MEMBER
(AGE, NAME, ID)
values
(?, ?, ?)
Hibernate:
insert
into
TEST_IDCARD
(NUM, MEMBER_ID, ID)
values
(?, ?, ?)
IdCard的外键来自Member的主键。
b、加载数据
同上
c、删除数据
同上
至此,一对一的关联关系基本上说的差不多了,下一章将讨论一对多的关联关系。