Hibernate关系映射(一)一对一单向外键关联@OneToOne Annotation方式

首先构造一个场景,实体:国家(country),它具有以下属性,Id,名称,使用货币,首都。实体:首都(capital),它具有以下属性,Id,名称。一个国家有且仅有一个首都,一个首都只能属于一个国家,典型的一对一关系。由于Hibernate自动帮我们生成表了,所以表结构不直观,下面看PowerDesigner反向工程反向出来的表结构:
Hibernate关系映射(一)一对一单向外键关联@OneToOne Annotation方式_第1张图片
在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
        (?, ?, ?)

结果如图:
这里写图片描述
Hibernate关系映射(一)一对一单向外键关联@OneToOne Annotation方式_第2张图片


接下来继续测试,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);

截图:
Hibernate关系映射(一)一对一单向外键关联@OneToOne Annotation方式_第3张图片

这里写图片描述


希望能对你有所帮助,下一篇将介绍一对一双向外键关联

你可能感兴趣的:(hibernate)