一对一关系分以下几种:
- 单向主键一对一
- 双向主键一对一
- 单向外键一对一
- 双向外键一对一
在此先介绍统一用的实体类:
package entity;
import java.util.Date;
/**
* 单向one to one,User指向Group
* 主键关联
* @author arkulo
*
*/
public class User {
private int id;
private String userName;
private String passWd;
private Date addtime;
private IdCard ic;
public IdCard getIc() {
return ic;
}
public void setIc(IdCard ic) {
this.ic = ic;
}
public Date getAddtime() {
return addtime;
}
public void setAddtime(Date addtime) {
this.addtime = addtime;
}
public User(){}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWd() {
return passWd;
}
public void setPassWd(String passWd) {
this.passWd = passWd;
}
}
package entity;
/**
* 身份证,不需要指向User
* @author arkulo
*
*/
public class IdCard {
private int id;
private String idCard;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
}
一、单向主键一对一
User.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="entity.User" >
<id name="id">
<!-- 单向主键关联,class应该设置成foreign -->
<generator class="foreign">
<!-- User类中的ic这个属性 -->
<param name="property">ic</param>
</generator>
</id>
<property name="userName" />
<property name="passWd" />
<property name="addtime" type="time" />
<!-- constrained表示一个外键约束,默认为false,true的时候数据库表会加外键 -->
<one-to-one name="ic" constrained="true" cascade="delete"></one-to-one>
</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="entity.IdCard" >
<id name="id">
<generator class="native" />
</id>
<property name="idCard" />
</class>
</hibernate-mapping>
单元测试:
package entity;
import java.util.Date;
import org.hibernate.classic.Session;
import junit.framework.TestCase;
import util.hibernateUtil;
/**
* 一对一关联分为两大类:主键关联,唯一外键关联
* 一对一关联还有方向之分:单向和双向
*
* 本项目是一对一单向主键关联方式
*
* @author arkulo
*
*/
public class TestOneToOne extends TestCase {
/**
* 单向主键one to one,添加数据,注意主键是否需要提前生成
* 因为是一对一,主键外键关联是做在user表上的,因此,要测试是否提前生成idcard的主键
*/
public void test1() {
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
IdCard idcard = new IdCard();
idcard.setIdCard("123456789");
// 这里不用saveidcard,user保存的时候会一起save
User user = new User();
user.setUserName("王蕊");
user.setPassWd("123456");
user.setAddtime(new Date());
user.setIc(idcard);
session.save(user);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
hibernateUtil.closeSession(session);
}
// 读取关联数据,get函数能不能在读取User的时候,把idCard也读取出来,用不同的session,避免一级缓存
try {
session = hibernateUtil.getSession();
session.beginTransaction();
User user = (User) session.get(User.class, 1);
System.out.println("用户名:" + user.getUserName());
// 上面user的查询是直接发出一条sql语句,但是并没有关联查询idcard,当执行到下面这句话的时候,因为可以从user中
// 获得idcard的主键id,因此又发出了一条sql,条件是id的去查询idcard
System.out.println("身份证:" + user.getIc().getIdCard());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
hibernateUtil.closeSession(session);
}
}
/**
* 如果要删除idcard数据,user数据会怎么样?
* 请先用test1函数做数据初始化,初始化后运行此函数,请将hibernate.hbm2ddl.auto改为update
*/
public void test2() {
// 在这里调用一下初始化数据的test1函数
test1();
System.out.println("----------------------------------");
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 因为有外键约束存在,如果构造直接构造一个瞬时状态的IdCard对象,然后直接去删除,系统会报错,提示
// 有外键约束,不能删除
// IdCard ic = new IdCard();
// ic.setId(1);
// session.delete(ic);
// 这里我们模拟构造一个顺势状态的User,看是否能够直接删除
// 这里没有构造idcard对象,单独删除user成功,idcard记录没有删除
// User user = new User();
// user.setId(1);
// session.delete(user);
// 这里我们再来试一下,构造了idcard,看能不能关联删除
// 这里也没有删除idcard
// IdCard ic = new IdCard();
// ic.setId(1);
//
// User user = new User();
// user.setId(1);
// user.setIc(ic);
// session.delete(user);
// 这里我们设置一下映射文件中的级联方式,让他的级联方式变成delete,看看能不能删除idcard
// 只有user拥有了id,idcard也拥有了id,加上级联关系,然后删除user的时候,才会连锁删除idcard
IdCard ic = new IdCard();
ic.setId(1);
User user = new User();
user.setId(1);
user.setIc(ic);
session.delete(user);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
hibernateUtil.closeSession(session);
}
}
}
二、双向主键一对一
<?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="entity.User" >
<id name="id">
<!-- 单向主键关联,class应该设置成foreign -->
<generator class="foreign">
<!-- User类中的ic这个属性 -->
<param name="property">ic</param>
</generator>
</id>
<property name="userName" />
<property name="passWd" />
<property name="addtime" type="time" />
<!-- constrained表示一个外键约束,默认为false -->
<one-to-one name="ic" constrained="true" cascade="delete"></one-to-one>
</class>
</hibernate-mapping>
<?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="entity.IdCard" >
<id name="id">
<generator class="native" />
</id>
<property name="idCard" />
<!-- 这里不需要设置constrained属性?给idcard也加上这个属性,在数据库中就生成了由idcard表到user表的外键约束-->
<!-- 不能在一对一关系中,两边都加上constrained属性,会报错的 -->
<!-- 就是今天,就是今天,我有了人生第一个机械键盘,爽的不行不行的! -->
<one-to-one name="user"></one-to-one>
</class>
</hibernate-mapping>
单元测试:
package entity;
import java.util.Date;
import org.hibernate.classic.Session;
import junit.framework.TestCase;
import util.hibernateUtil;
/**
* 一对一关联分为两大类:主键关联,唯一外键关联
* 一对一关联还有方向之分:单向和双向
*
* 本项目是双向主键一对一关联,测试目的如下:
* 1. 双向关联插入?如果是在idcard中添加user对象,是不是保存idcard对象,就算是保存了user对象
* 2. 不管是读取那个对象的数据,都会带出关联的数据,不需要单独去查询
* 3. 删除,是否可以反向删除,例如删除idcard,然后会不会也删除user
* @author arkulo
*
*/
public class TestOneToOne extends TestCase {
// 1. 双向数据插入的测试
public void test1() {
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
IdCard ic = new IdCard();
ic.setIdCard("123456789");
User user = new User();
user.setUserName("王蕊");
user.setAddtime(new Date());
user.setPassWd("123323");
user.setIc(ic);
ic.setUser(user);
// 单独保存idcard不会级联保存user数据,只会单独保存idcard的数据
// session.save(ic);
// 如果单独保存user对象,会连天idcard对象一起保存
session.save(user);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
hibernateUtil.closeSession(session);
}
}
/**
* 2. 读取数据,由user是否能关联查处
*/
public void test2() {
// 这里需要调用一下数据初始化函数test
test1();
System.out.println("-----------------------------------------");
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 从user角度读取数据,先发出一条sql语句,查询user,然后打印username,查询idcard的sql语句这时候并没有发出
User user = (User) session.get(User.class, 1);
System.out.println("用户名:" + user.getUserName());
// 在这里要用到idcard的数据的时候,才发出了查询idcard的sql语句(left out join)
System.out.println("身份证号码:" + user.getIc().getIdCard());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
hibernateUtil.closeSession(session);
}
// 这里通过查询idcard,看能不能级联查处user数据,在不同的session里面测试,避免一级缓存
try {
session = hibernateUtil.getSession();
session.beginTransaction();
IdCard ic = (IdCard) session.get(IdCard.class, 1);
// 这种情况下,一次性采用连表查询left out join将两张表一起取出来了
System.out.println("用户名:" + ic.getUser().getUserName());
System.out.println("身份证编号:" + ic.getIdCard());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
hibernateUtil.closeSession(session);
}
}
/**
* 3. 删除的时候会不会有级联效果
*/
public void test3() {
test1();
System.out.println("-----------------------------");
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 构造一个user瞬时对象,然后直接删除
IdCard ic = new IdCard();
ic.setId(1);
User user = new User();
user.setId(1);
user.setIc(ic);
ic.setUser(user);
// 如果单独删除user,系统会自动级联删除idcard
session.delete(user);
// 如果单独删除idcard,系统报错,提示有外键约束,不能删除
// session.delete(ic);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
hibernateUtil.closeSession(session);
}
}
}
三、单向外键一对一
<?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="entity.User" >
<id name="id">
<generator class="native" />
</id>
<property name="userName" />
<property name="passWd" />
<property name="addtime" type="time" />
<!-- 单向一对一外键关联,其实就是多对一关联设置为唯一 -->
<many-to-one name="ic" unique="true" cascade="delete"></many-to-one>
</class>
</hibernate-mapping>
<?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="entity.IdCard" >
<id name="id">
<generator class="native" />
</id>
<property name="idCard" />
</class>
</hibernate-mapping>
单元测试:
package entity;
import java.util.Date;
import org.hibernate.classic.Session;
import junit.framework.TestCase;
import util.hibernateUtil;
/**
* 单向外键一对一关联
*
* 1. 添加数据,级联保存,保存user的时候,是否能同时保存idcard
* 2. 查询数据,查询user的时候,是否能查处idcard
* 3. 删除数据,删除user,是否能级联删除idcard,如果把级联规则改掉,删除user,是否可以不删除idcard
* @author arkulo
*
*/
public class TestOneToOne extends TestCase {
// 添加数据
public void test1()
{
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 外间单向关联,首先需要保存idcard,如果只是保存user的时候,会提示错误,user中的外间关联字段会没有值
// 也就是说,单独保存user的时候,不能一并保存idcard对象
IdCard ic = new IdCard();
ic.setIdCard("1234567");
session.save(ic);
User user = new User();
user.setUserName("王蕊");
user.setAddtime(new Date());
user.setIc(ic);
session.save(user);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
hibernateUtil.closeSession(session);
}
}
// 查询数据,是否能实现查询user的时候,一次性关联查询出idcard
public void test2()
{
// 初始化数据
test1();
System.out.println("-------------------------------------");
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 采用load查询方式
User user = (User)session.load(User.class, 1);
// 这里单独的发出一条sql语句,去查询user表,并没有查询idcard
System.out.println("用户名:"+user.getUserName());
// 这里单独的发出了一条sql语句,where条件是id,去查询idcard
System.out.println("身份证编号:"+user.getIc().getIdCard());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
hibernateUtil.closeSession(session);
}
}
// 删除操作
public void test3()
{
test1();
System.out.println("-----------------------------");
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 自定义一个idcard瞬时对象
IdCard ic = new IdCard();
ic.setId(1);
// 自定义一个顺势对象user
User user = new User();
user.setId(1);
user.setIc(ic);
// 在cascade默认情况下,单独删除user,不会级联删除idcard的!!
// 在cascade设为delete情况下,单独删除user, 会级联删除idcard
session.delete(user);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
hibernateUtil.closeSession(session);
}
}
}
四、双向外键一对一
<?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="entity.User" >
<id name="id">
<generator class="native" />
</id>
<property name="userName" />
<property name="passWd" />
<property name="addtime" type="time" />
<!-- 单向一对一外键关联,其实就是多对一关联设置为唯一 -->
<many-to-one name="ic" unique="true" cascade="delete"></many-to-one>
</class>
</hibernate-mapping>
<?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="entity.IdCard" >
<id name="id">
<generator class="native" />
</id>
<property name="idCard" />
<!--
先抛出问题:
one to one默认是两个对象的主键进行关联,而我们现在是要用外键关联,也就是说User的一个属性(非主键)要和idcard进行关联,
这个如何设置?
解决办法:
property-ref这个选项可以设定User对象中哪个属性和idcard表的主键进行关联,实际代码中指定是user对象的ic属性。
-->
<one-to-one name="user" property-ref="ic"></one-to-one>
</class>
</hibernate-mapping>
单元测试:
package entity;
import java.util.Date;
import org.hibernate.classic.Session;
import junit.framework.TestCase;
import util.hibernateUtil;
/**
* 双向一对一外键关联
* 1. 在设置映射的时候,idcard的one to one映射加上property-ref的作用
* 2. 新增,新增之后查看idcard和user之间的关联关系
* 3. 查询,双向关联是基于对象之间的,不是数据库表之间的,数据库表里还是user表中加字段,关联idcard
* 4. 删除,双向关联,删除idcard会删除user吗?
* @author arkulo
*
*/
public class TestOneToOne extends TestCase {
// 新增数据
public void test1(){
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
IdCard ic = new IdCard();
ic.setIdCard("12345678");
User user = new User();
user.setUserName("王蕊");
user.setPassWd("242424");
user.setAddtime(new Date());
user.setIc(ic);
ic.setUser(user);
// 这里我们来尝试一下,如果我们要保存idcard,能否一起保存use对象
// 单独保存idcard对象,只能保存idcard,不会级联保存user
// 如果在idcard的映射文件中,设置cascade属性,然后单独保存idcard,系统会报错
// session.save(ic);
// 单独保存user对象也是不行的,因为这时候idcard还没有保存,没有主键可以关联
// 因此需要先保存idcard,然后再保存user
session.save(ic);
session.save(user);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
hibernateUtil.closeSession(session);
}
}
/**
* 读取数据:
* 1. 是否能从user级联查询出idcard
* 2. 是否能从idcard级联查询出user
*/
public void test2(){
test1();
System.out.println("-------------------------------------------");
Session session = null;
// 第一种情况
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 这里也是两条语句,在get函数的时候立即发出一条sql语句,查询user表
// 在打印身份证编号的时候发出第二条sql语句,关联查询user表和idcard表,得出idcar的数据
User user = (User)session.get(User.class, 1);
System.out.println("用户名:"+user.getUserName());
System.out.println("身份证编号:"+user.getIc().getIdCard());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
hibernateUtil.closeSession(session);
}
System.out.println("------------------------------");
// 第二种情况
try {
session = hibernateUtil.getSession();
session.beginTransaction();
// 这里一条sql语句,把user表和idcard一起查询出来
IdCard ic = (IdCard)session.get(IdCard.class, 1);
System.out.println("用户名:"+ic.getUser().getUserName());
System.out.println("身份证编号:"+ic.getIdCard());
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
hibernateUtil.closeSession(session);
}
}
/**
* 删除测试
* 1. 在不设置cascade的情况下,删除user,会不会级联删除idcard
* 2. 在设置了级联操作下,阐述idcard,会不会删除user
*/
public void test3(){
test1();
System.out.println("------------------------------");
Session session = null;
try {
session = hibernateUtil.getSession();
session.beginTransaction();
IdCard ic = new IdCard();
ic.setId(1);
User user = new User();
user.setId(1);
user.setIc(ic);
ic.setUser(user);
// 1. 在默认不设置cascade的情况下,删除user是不会级联删除idcard的
// 2. 在默认不设置cascade的情况下,删除idcard会报错,提示外键关联,不能随意删除
// 3. cascade设置为delete情况下,删除user会级联删除idcard
// 4. cascade设置为delete情况下,删除idcard会报错,提示外键关联,不能随意删除
session.delete(user);
// session.delete(ic);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally{
hibernateUtil.closeSession(session);
}
}
}