码迷,mamicode.com
首页 > Web开发 > 详细

Hibernate一对一关联关系

时间:2016-01-21 15:47:35      阅读:219      评论:0      收藏:0      [点我收藏+]

标签:

  一对一的关联关系有一下几类:

  1. 单向一对一

    a). 基于主键的

    b). 基于外键的

  2. 双向一对一

    a). 基于外键的

好, 下面咱们主要说一下怎么来设置这种关系, 主要是说怎么用, 其中也说一下一些地方为什么这么配置.

OneByOne

  一: 基于主键的 单向一对一

    我们使用 Person类和 IdCard类来做测试, 一个Person 只能对应一个 IdCard. Just So.

    Person类:

技术分享
package com.single.one2one_primary;

public class Person {

    private Integer id;
    private String name;
    
    private IdCard idCard;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public IdCard getIdCard() {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

}
View Code

     IdCard类:

技术分享
package com.single.one2one_primary;

public class IdCard {

    private Integer id;
    private String card;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getCard() {
        return card;
    }

    public void setCard(String card) {
        this.card = card;
    }

}
View Code

     IdCard类的映射文件:

<?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="com.test.one2one_primary.IdCard" table="IDCARDS">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="card" type="java.lang.String">
            <column name="CARD" />
        </property>
        
    </class>
</hibernate-mapping>

 

    Person的映射文件:

<?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="com.test.one2one_primary.Person" table="PERSONS">
    
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <!--
                使用 foregin策略: 
                    指使用"对方"的主键来生成自己的主键, 自己并不能独立生成主键.
                
                <param name="property">idCard</param>: 
                    指定使用当前持久化类中的那个属性作为"对方"
                    name="property": 固定写法
                
                使用该策略生成主键, 还需要增加一个 <one-to-one> 标签
                并且 <one-to-one> 标签属性还应增加 constrained="true" 属性
                
                constrained="true":
                    指为当前持久化类对应的数据表的主键增加一个外键约束
                    引用被关联的对象("对方")所使用的数据库主键
             -->
            <generator class="foreign" >
                <param name="property">idCard</param>
            </generator>
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <one-to-one name="idCard" constrained="true"></one-to-one>
        
    </class>
</hibernate-mapping>

 

    Hibernate主配置文件:

技术分享
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC 
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        
        <!-- 配置连接数据库的信息 -->
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">oracle</property>
        <property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        
        <!-- 配置数据库方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL57InnoDBDialect</property>
        
        <!-- 设置数据表生成策略 -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        
        <!-- 是否格式化SQL -->
        <property name="hibernate.format_sql">true</property>
        
        <!-- 是否显示SQL -->
        <property name="hibernate.show_sql">true</property>
        
        <mapping resource="com/single/one2one_primary/IdCard.hbm.xml"/>
        <mapping resource="com/single/one2one_primary/Person.hbm.xml"/>
        <!-- 
        <mapping resource="com/single/one2one_foreign/IdCard.hbm.xml"/>
        <mapping resource="com/single/one2one_foreign/Person.hbm.xml"/>
        <mapping resource="com/doubles/one2one_foreign/IdCard.hbm.xml"/>
        <mapping resource="com/doubles/one2one_foreign/Person.hbm.xml"/>
        -->
        
    </session-factory>
</hibernate-configuration>
View Code

 

    建立单元测试类:

技术分享
ackage com.single.one2one_foreign;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class TestHibernate {
    
    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;
    
    @Before
    public void init(){
        
        sessionFactory = new Configuration().configure().buildSessionFactory();
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    }
    
    @After
    public void distory(){
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}
View Code

 

    测试保存数据:

    @Test
    public void testInsert(){
        
        Person person = new Person();
        person.setName("Jerry");
        
        IdCard idCard = new IdCard();
        idCard.setCard("100003");
        
        // 设置关联关系
        person.setIdCard(idCard);
        
        // 不管保存顺序怎么样, 总是先保存 Order表, 然后再保存 Person表.
        session.save(person);
        session.save(idCard);
        
    }

 

    测试查询数据:

    @Test
    public void testQuery(){
        
        // 默认使用懒加载
        // 可以在 <one-to-one> 总加上 lazy="false" 关闭懒加载
        Person person = session.get(Person.class, 1);
        System.out.println(person.getName());
        
        // 这是一个代理对象
        System.out.println(person.getIdCard().getClass().getName());
        
    }

    测试删除数据:

    @Test
    public void testDelete(){
        
        IdCard idCard = session.get(IdCard.class, 1);
        
        // 删除不了的, 因为有外键约束!
        // 如何解决呢? 我添加了 cascade="all" 属性, 但是不管用. 不知道怎么解决了.
        session.delete(idCard);
        
    }

    测试更新数据:

    @Test
    public void testUpdate(){
        
        IdCard idCard = session.get(IdCard.class, 1);
        idCard.setCard("200002");
        
        // 我感觉更新数据一般不会出现什么问题, 主要是保存数据和删除数据的时候容易出问题.
        session.update(idCard);
        
    }

 

 

  二: 基于外键的单向一对一

    还是利用上面的Person类和IdCard类, 咱们主要是修改配置文件

    使用外键, 然后由于是单向的, 所以配置比较简单, 主要来修改 Person.hbm.xml 文件, eg:

<?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 package="com.single.one2one_foreign">
    <class name="Person" table="PERSONS">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <!--
            使用  unique="true" 来确定是唯一的, 要不然会出现一对多现象.
        -->
        <many-to-one name="idCard" unique="true" 
            class="IdCard" column="IDCARD_ID"></many-to-one>
        
    </class>
</hibernate-mapping>

 

    这个外键会出现在 Person 表中, 下面咱们还要说双向一对一, <many-to-one>标签出现在哪个表中, 外键就会在哪个表中. 

    测试数据的保存:

    @Test
    public void testInsert(){
        
        Person person = new Person();
        person.setName("Tom");
        
        IdCard idCard = new IdCard();
        idCard.setIdCard("10002");
        
        // 设置关联关系
        person.setIdCard(idCard);
        
        // 先保存没有外键的一端, 然后再保存有外键的一端
        // 否则会多出一条 update 语句
        session.save(idCard);
        session.save(person);
        
    }

    测试查询数据(同上面一样, 默认使用懒加载):

技术分享
    @Test
    public void testQuery(){
        
        // 默认使用懒加载
        // 可以在 <one-to-one> 总加上 lazy="false" 关闭懒加载
        Person person = session.get(Person.class, 1);
        System.out.println(person.getName());
        
        // 这是一个代理对象
        System.out.println(person.getIdCard().getClass().getName());
        
    }
View Code

 

    测试删除数据(同上面一样, 级联删除还是不行):

技术分享
    @Test
    public void testDelete(){
        
        IdCard idCard = session.get(IdCard.class, 1);
        
        // 删除不了的, 因为有外键约束!
        // 如何解决呢? 我添加了 cascade="all" 属性, 但是不管用. 不知道怎么解决了.
        session.delete(idCard);
        
    }
View Code

    测试更新数据(同上面一样):

技术分享
    @Test
    public void testUpdate(){
        
        IdCard idCard = session.get(IdCard.class, 1);
        idCard.setIdCard("200002");
        
        // 我感觉更新数据一般不会出现什么问题, 主要是保存数据和删除数据的时候容易出问题.
        session.update(idCard);
        
    }
View Code

 

 

  三: 基于外检的双向一对一

    这里跟基于外键的单向一对一差不多, 无非就是在另一个持久化类中添加了一个属性,

    这个地方, 我们需要在 IdCard 类中添加 Person 类对象, 并添加上 getter/setter 方法. 然后对它们的映射文件进行配置:

    IdCard的映射文件:

<?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 package="com.doubles.one2one_foreign">
    <class name="IdCard" table="IDCARD">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="idCard" type="java.lang.String">
            <column name="IDCARD" />
        </property>
        
        <!--
            一定要加上 property-ref="idCard"
            否则, Hibernate查询的时候, 会出现SQL连接逻辑出错 on idcard0_.ID=person1_.ID
            正常的应该是: on idcard0_.ID=person1_.IDCARD_ID
            如果改为 property-ref="name"(这个name是Person类中的name)
            那么, 左外连接语句就变成了 on idcard0_.ID=person1_.NAME
            可以发现我们配置的 property-ref 的值在对应类中的属性对应的列
            就是左外连接 on idcard0_.ID=person1_.? 处, ? 所代表的的值. 
            如果不配置, property-ref 的取值就是本类的主键属性对应的数据列(ID)
            配置成 name 就是 Person类中 name 属性对应的数据列(NAME)
            配置成 idCard 就是 Person 类中 idCard 属性对应的数据列(IDCARD_ID)
        -->
        <one-to-one name="person" class="Person" property-ref="idCard"></one-to-one>
        
    </class>
</hibernate-mapping>

 

    Person类的映射文件:

<?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 package="com.doubles.one2one_foreign">
    <class name="Person" table="PERSON">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <!--
            使用唯一约束(unique="true")保证是一对一
            column="IDCARD_ID": 外键的名字 
        -->
        <many-to-one name="idCard" class="IdCard" 
            column="IDCARD_ID" unique="true">
        </many-to-one>
        
    </class>
</hibernate-mapping>

 

 

    测试保存数据:

    @Test
    public void testInsert(){
        
        Person person = new Person();
        person.setName("Mike");
        
        IdCard idCard = new IdCard();
        idCard.setIdCard("10001");
        
        person.setIdCard(idCard);
        idCard.setPerson(person);
        
        // 先保存没有外键的一端, 然后再保存有外键的一端
        // 否则会多出一条 update 语句
        session.save(idCard);
        session.save(person);
        
    }

    测试查询数据:

    @Test
    public void testQuery(){
        
        // 在双向一对一的情况下
        // 1. 查询没有外键的数据表时, 默认使用左外连接(left outer join)进行查询
        IdCard idCard = session.get(IdCard.class, 1);
        System.out.println(idCard.getIdCard());
        System.out.println(idCard.getPerson().getClass().getName());
        
        // 2. 查询有外键的数据表时, 默认使用懒加载
        Person person = session.get(Person.class, 1);
        System.out.println(person.getName());
        System.out.println(person.getIdCard().getClass().getName());
        
    }

 

    测试删除数据(同上):

    只要有外键关联, 没有设置级联操作, 而且外键还有数据表在引用, 就不能删除. 不论是本篇博文中的那种关系.

    测试更新数据(同上):

    最容易出错的就是查询数据中的懒加载和删除数据中的级联操作, 至于更新数据, 没什么问题.

 

    你很有可能问, 为什么没有基于主键的双向一对一?

    很好理解, 因为基于主键的单向一对一: 一方的主键使用另一方的主键来生成自己的主键, 它自己不能生成主键, 只能依赖于对方, 如果是双向的话, 这样, 一方依赖另一方生成主键, 另一方又依赖这一方生成主键, 这样就乱了.

  

Hibernate一对一关联关系

标签:

原文地址:http://www.cnblogs.com/wuqinglong/p/5148372.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!