首先构造一个场景,实体:国家(country),它具有以下属性,Id,名称,使用货币,首都。实体:首都(capital),它具有以下属性,Id,名称。一个国家有且仅有一个首都,一个首都只能属于一个国家,典型的一对一关系。由于Hibernate自动帮我们生成表了,所以表结构不直观,下面看PowerDesigner反向工程反向出来的表结构:
在country表中,有一个capital_capitalId属性作为外键指向capital表的capitalId,这就意味着要先存在一个capital才能有一个country,也就是说如果在做删除操作的时候先删除了capital,并且存在引用这个capital的话就会报错,正确的做法是,先解除两个实体之间的约束那么在进行删除操作。下面会详细介绍。
下面看如何通过注解的方式构造本例的实体:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity //该注解表示这是一个实体映射
@Table(name="country") //指定表名,若无该注解则表名默认和类名相同
public class Country {
private Integer countryId; //ID作为主键存在
private String countryName; //国家名称
private String currency; //使用货币
private Capital capital; //首都 Capital类型
public Country() {
super();
}
public Country(Integer countryId, String countryName, String currency,
Capital capital) {
super();
this.countryId = countryId;
this.countryName = countryName;
this.currency = currency;
this.capital = capital;
}
@Id
@GeneratedValue()
public Integer getCountryId() {
return countryId;
}
public void setCountryId(Integer countryId) {
this.countryId = countryId;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
@OneToOne //一对一注解
public Capital getCapital() {
return capital;
}
public void setCapital(Capital capital) {
this.capital = capital;
}
}
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="capital")
public class Capital {
private Integer capitalId;
private String capitalName;
public Capital() {
super();
}
public Capital(Integer capitalId, String capitalName) {
super();
this.capitalId = capitalId;
this.capitalName = capitalName;
}
@Id
@GeneratedValue()
public Integer getCapitalId() {
return capitalId;
}
public void setCapitalId(Integer capitalId) {
this.capitalId = capitalId;
}
public String getCapitalName() {
return capitalName;
}
public void setCapitalName(String capitalName) {
this.capitalName = capitalName;
}
}
通过在Country类中添加一个Capital类型的capital属性,并且打上@OneToOne注解,就将两个实体进行了一对一的关联。而有关fetchType(懒加载或者说加载机制)和Cascade(级联操作)通过后面的章节介绍。
最后在hibernate.cfg.xml配置文件中加上映射:
<mapping class="com.ht.entity.one2one.uni.fk.Country"/>
<mapping class="com.ht.entity.one2one.uni.fk.Capital"/>
接下来就要进行CRUD的测试了,测试采用JUnit进行:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import com.ht.entity.one2one.uni.fk.Capital;
import com.ht.entity.one2one.uni.fk.Country;
public class One2OneTest {
private static SessionFactory sessionFactory;
@BeforeClass
public static void beforeClass() {
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
}
@AfterClass
public static void afterClass() {
sessionFactory.close();
}
接下来看testCreate()方法,用于测试添加新数据:
@Test
public void testCreate(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
//new一个country对象
Country country = new Country();
//new一个capital对象
Capital capital = new Capital();
//设置country的属性
country.setCountryName("中国");
country.setCurrency("人民币");
//设置capital的属性
capital.setCapitalName("北京");
//设置他们之间的关联
country.setCapital(capital);
//保存
session.save(capital);
session.save(country);
//提交事务
session.getTransaction().commit();
}
下面是Hibernate生成的SQL语句:
Hibernate:
insert
into
capital
(capitalName)
values
(?)
Hibernate:
insert
into
country
(currency, capital_capitalId, countryName)
values
(?, ?, ?)
接下来继续测试,testRead()方法,用于测试读取数据:
@Test
public void testRead(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
//从country去读取capital
Country country = (Country) session.load(Country.class, 1);
System.out.println(country.getCountryName()+" "+country.getCurrency()+" "+country.getCapital().getCapitalName());
//从capital去读取country
Capital capital = (Capital) session.load(Capital.class, 1);
System.out.println(capital.getCapitalName());
session.getTransaction().commit();
}
原谅我偷懒,用了Sysout作为日志打印结果,下面是结果:
Hibernate:
select
country0_.countryId as countryId0_1_,
country0_.countryName as countryN2_0_1_,
country0_.currency as currency0_1_,
country0_.capital_capitalId as capital4_0_1_,
capital1_.capitalId as capitalId1_0_,
capital1_.capitalName as capitalN2_1_0_
from
country country0_
left outer join
capital capital1_
on country0_.capital_capitalId=capital1_.capitalId
where
country0_.countryId=?
中国 人民币 北京
北京
接下来是更新,testUpdate()方法:
@Test
public void testUpdate(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
//先把country读取出来
Country country = (Country) session.load(Country.class, 1);
//设置新的属性
country.setCountryName("China");
country.setCurrency("RMB");
Capital capital = (Capital) session.load(Capital.class, 1);
capital.setCapitalName("Beijing");
session.saveOrUpdate(country);
session.saveOrUpdate(capital);
session.getTransaction().commit();
}
运行结果截图如下(突然感觉在写大学的实验报告):
Hibernate:
update
capital
set
capitalName=?
where
capitalId=?
Hibernate:
update
country
set
countryName=?,
currency=?,
capital_capitalId=?
where
countryId=?
最后进行删除测试,testDelete()方法:
@Test
public void testDelete(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Country country = (Country) session.load(Country.class, 1);
Capital capital = (Capital) session.load(Capital.class, 1);
session.delete(country);
session.delete(capital);
session.getTransaction().commit();
}
Hibernate:
delete
from
country
where
countryId=?
Hibernate:
delete
from
capital
where
capitalId=?
成功删除!但是在现实需求中,在这样的关系中,如果需求是需要删除capital而不删除country,或者是只删除country而不删除capital呢?我们再试一试。
@Test
public void testDelete(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
//需求是删除capital,不删除country
Capital capital = (Capital) session.load(Capital.class, 2);
session.delete(capital);
session.getTransaction().commit();
}
这样做,就会报错,org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update,字面意思是违反了约束,也就是说country表中有一个外键是指向capital的,把capital删除了,就会报错,解决办法是先解除他们之间的约束,在进行删除。代码修改如下:
//需求是删除capital,不删除country
Country country = (Country) session.load(Country.class, 2);
Capital capital = (Capital) session.load(Capital.class, 2);
country.setCapital(null);
session.delete(capital);
希望能对你有所帮助,下一篇将介绍一对一双向外键关联