随笔-126  评论-247  文章-5  trackbacks-0

一对一关联有三种情况:
 
一是关联的实体都共享同样的主键,二是其中一个实体通过外键关联到另一个实体的主键 ,三是通过关联表来保存两个实体之间的连接关系。

接下来要介绍的是,注解形式的一对一单向外键关联的情况。

环境 :  JDK 1.6,eclipse 3.6,maven 3.0.4,hibernate 3.3.2,junit 4.7,mysql 5.1

 pom.xml 核心部分清单 

<dependencies>

   <!-- Hibernate framework -->

    <dependency>

      <groupId>org.hibernate</groupId>

      <artifactId>hibernate-core</artifactId>

      <version>3.3.2.GA</version>

    </dependency>

    <!-- Hibernate Dependency Start -->

    <dependency>

      <groupId>cglib</groupId>

      <artifactId>cglib</artifactId>

      <version>2.2</version>

    </dependency>

    <dependency>    

      <groupId>javassist</groupId>    

      <artifactId>javassist</artifactId>    

      <version>3.9.0.GA</version>

    </dependency>

    <dependency>

      <groupId>org.hibernate</groupId>

      <artifactId>hibernate-annotations</artifactId>

      <version>3.4.0.GA</version>

    </dependency>

    <dependency>    

      <groupId>org.slf4j</groupId>    

      <artifactId>slf4j-log4j12</artifactId>    

      <version>1.5.8</version>

    </dependency>

    <!-- Hibernate Dependency End -->

    <!-- mysql driver -->

    <dependency>

      <groupId>mysql</groupId>

      <artifactId>mysql-connector-java</artifactId>

      <version>5.1.17</version>

    </dependency>

    <!-- junit -->

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.7</version>

      <scope>test</scope>

    </dependency>

 </dependencies>



注 : 此处配置 pom.xml 是使用 maven 来管理 jar 包,如果你没有使用 maven,则需手动导入相关 jar 包。

①一对一单向外键关联 :

 实体 bean 

package net.yeah.fancydeepin.unidirectional.po;

import java.io.Serializable;

import javax.persistence.CascadeType;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.FetchType;

import javax.persistence.GeneratedValue;

import javax.persistence.Id;

import javax.persistence.OneToOne;

import org.hibernate.annotations.GenericGenerator;

 

@Entity

public class Student implements Serializable{

   private static final long serialVersionUID = 1L;

   private String id;
  
private String name;
  
private StudentCard studentCard;

   @Id

   @GenericGenerator(name = "idGenerator", strategy = "uuid")

   @GeneratedValue(generator = "idGenerator")

   @Column(length = 32)

   public String getId() {

      return id;

   }

   @Column(length = 18, nullable = false)

   public String getName() {

      return name;

   }

   @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)

   public StudentCard getStudentCard() {

      return studentCard;

   }

   public void setId(String id) {

      this.id = id;

   }

   public void setName(String name) {

      this.name = name;

   }

   public void setStudentCard(StudentCard studentCard) {

      this.studentCard = studentCard;

   }

}


@OneToOne         建立实体 bean 之间的一对一的关联

cascade                  级联策略,即,当对主对象做某种操作时,是否对其相关联的子对象也做相对应的操作。它有5个值可选,分别是 :

             CascadeType.PERSIST : 级联新建

             CascadeType.REMOVE  :  级联删除

             CascadeType.REFRESH : 级联刷新

             CascadeType.MERGE   : 级联更新

             CascadeType.ALL     : 囊括以上四项


fetch                        抓取策略,它有2个值可选,分别是 :

             FetchType.LAZY   :   延迟抓取

             FetchType.EAGER  :   立即抓取

Tips :       延迟抓取数据能够保证应用只有在需要的时候才去数据库抓取相应的数据记录,这样能够避免过多,

             或过早的加载数据库表中的数据,从而减少应用内存的开销。

@JoinColumn     该注解与@Column 注解用法有点相似,可以通过name来指定联接列的名称,如果没有该注解没有被声明,

             默认的联接列名称是 : 关联的类的短类名(首字母小写,不带包名)_id。


 实体 bean 
package net.yeah.fancydeepin.unidirectional.po;

import java.io.Serializable;

import java.util.Date;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.Temporal;

import javax.persistence.TemporalType;

@Entity

public class StudentCard implements Serializable{

   private static final long serialVersionUID = 1L;

   private Long id;

   private Date date;

   @Id

   public Long getId() {

      return id;

   }

   @Column(nullable = false)

   @Temporal(TemporalType.DATE)

   public Date getDate() {

      return date;

   }

   public void setId(Long id) {

      this.id = id;

   }

   public void setDate(Date date) {

      this.date = date;

   }

}

                                 

 hibernate.cfg.xml 清单   

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->

        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>

        <property name="connection.url">jdbc:mysql://localhost:3306/temp</property>

        <property name="connection.username">username</property>

        <property name="connection.password">password</property>

        <!-- SQL dialect -->

        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>


       
<!-- Enable Hibernate's automatic session context management -->

        <property name="current_session_context_class">thread</property>

        <!-- Echo all executed SQL to stdout -->

        <property name="show_sql">true</property>

        <property name="format_sql">true</property>

        <!-- OneToOne 单向 -->

        <mapping class="net.yeah.fancydeepin.unidirectional.po.Student" />

        <mapping class="net.yeah.fancydeepin.unidirectional.po.StudentCard" />

        <!-- OneToOne 双向 -->

        <!--

        <mapping class="net.yeah.fancydeepin.bidirectional.po.Student" />

        <mapping class="net.yeah.fancydeepin.bidirectional.po.StudentCard" />

        -->

    </session-factory>

</hibernate-configuration>




 Junit Test 

package junit.test;

import java.util.Date;

import net.yeah.fancydeepin.unidirectional.po.Student;

import net.yeah.fancydeepin.unidirectional.po.StudentCard;

import org.hibernate.Session;

import org.hibernate.cfg.AnnotationConfiguration;

import org.hibernate.tool.hbm2ddl.SchemaExport;

import org.junit.BeforeClass;

import org.junit.Test;

public class TestApp {

   private static Session session;

   private static final String ID = "402881f13a5480c2013a5480c3d00001";

   @BeforeClass

   public static void beforeClass(){

      session = new AnnotationConfiguration().configure().buildSessionFactory().getCurrentSession();

   }

   @Test

   public void createTable(){

      new SchemaExport(new AnnotationConfiguration().configure()).create(true, true);

   }

   @Test

   public void insert(){

      Student student = new Student();

      student.setName("fancy");

      StudentCard studentCard = new StudentCard();

      studentCard.setId(3110005981L);

      studentCard.setDate(new Date());

      student.setStudentCard(studentCard);

      session.beginTransaction();

      session.save(student);

      session.getTransaction().commit();

   }

   @Test

   public void query(){

      session.beginTransaction();

      Student student = (Student)session.get(Student.class, ID);

      System.out.println(student.getName());

      //StudentCard studentCard = student.getStudentCard();

 

      //System.out.println(studentCard.getDate());

   }

   @Test

   public void update(){

      session.beginTransaction();

      Student student = (Student)session.get(Student.class, ID);

      student.setName("fancydeepin");

   // StudentCard studentCard = student.getStudentCard();

   // studentCard.setDate(new Date());

   // student.setStudentCard(studentCard);

      session.update(student);

      session.getTransaction().commit();

   }

   @Test

   public void delete(){

      session.beginTransaction();

      Student student = (Student)session.get(Student.class, ID);

   // StudentCard studentCard = student.getStudentCard();

   // session.delete(studentCard);

      session.delete(student);

      session.getTransaction().commit();

   }

}

1. 建表

    在 Junit 测试类中执行建表方法 createTable,数据库中生成表结构 : 

    

    


2. 插入数据 ( 级联插入 )

    在 Junit 测试类中执行 insert 方法,后台发出两条插入的 SQL 语句,数据库中产生的数据 :

        

    在这里,student 是主表,studentcard 是从表,Student 类级联 ( CascadeType.ALL ) 了 StudentCard 类,当 Student 的实例对象被持久化时,

    若 Student 对象的 StudentCard 实例对象不为 null,则该 StudentCard 对象也将被持久化到数据库,若为 null,则不会被持久化。

3. 查询数据 ( 延迟加载 )

    在 Junit 测试类中执行 query 方法,后台发出 Student 的 select SQL 语句 : 
    
    Hibernate: 
        select
            student0_.id as id0_0_,
            student0_.name as name0_0_,
            student0_.studentCard_id as studentC3_0_0_ 
        from
            Student student0_ 
        where
            student0_.id=?

    若将 query 方法里被注释的行去掉,后台除了会发出 Student 的 select SQL 语句之外,还会发出 StudentCard 的 select SQL : 

    Hibernate: 
        select
            student0_.id as id0_0_,
            student0_.name as name0_0_,
            student0_.studentCard_id as studentC3_0_0_ 
        from
            Student student0_ 
        where
            student0_.id=?
    Hibernate: 
        select
            studentcar0_.id as id1_0_,
            studentcar0_.date as date1_0_ 
        from
            StudentCard studentcar0_ 
        where
            studentcar0_.id=?

4. 更新数据 ( 级联更新 )
 
    在 Junit 测试类中执行 update 方法,后台发出 Student 的 update SQL 语句 : 

    Hibernate: 
        update
            Student 
        set
            name=?,
            studentCard_id=? 
        where
            id=?

    若将 update 方法中的注释行去掉,后台除了会发出 Student 的 update SQL 语句之外,还会发出 StudentCard 的 update SQL : 

    Hibernate: 
        update
            Student 
        set
            name=?,
            studentCard_id=? 
        where
            id=?
    Hibernate: 
        update
            StudentCard 
        set
            date=? 
        where
            id=?

    注 :  只有当 Student 对象的属性值发生变化时,才会发出 Student 的 update SQL,如果 Student 对象中的属性值没有发生过改变,

            则不会发出 Student 的 update SQL ; StudentCard 也是一样的。


5. 删除数据 ( 级联删除 )

    在 Junit 测试类中执行 delete 方法,后台发出 Student 和 StudentCard 的 delete SQL 语句 : 

    Hibernate: 
        delete 
        from
            Student 
        where
            id=?
    Hibernate: 
        delete 
        from
            StudentCard 
        where
            id=?


    由于是 CascadeType.ALL 的级联策略,当从表中的记录被删除时,主表中被关联的记录也将会被删除掉。

    若是将 delete 方法中的注释行去掉,将最后注释行的下一行注释掉,也就是如果将 session.delete(student); 这行 注释起来的话,
 
    后台将抛出   org.hibernate.ObjectDeletedException 的异常,这是由于从表 student 关联了主表 studentcard,因此不能直接去删除

    studentcard 表中被 student 表参考的记录,换句话说就是,在记录关联情况下,只有从表中的记录先被删除,主表中的记录才有可能被删除。


②一对一双向外键关联 : 

    如果,StudentCard 中的 Student 是必须的,也就是说,在 Student 中保存了一个 StudentCard 的引用,在 StudentCard 中也保存了一个 Student 的引用,

    这就成了一对一的双向关联,StudentCard 的代码修改成 :

 实体 bean 
package net.yeah.fancydeepin.bidirectional.po;

import java.io.Serializable;

import java.util.Date;

import javax.persistence.Column;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.OneToOne;

import javax.persistence.Table;

import javax.persistence.Temporal;

import javax.persistence.TemporalType;

@Entity

@Table(name = "_studentcard")

public class StudentCard implements Serializable{

   private static final long serialVersionUID = 1L;

   private Long id;

   private Date date;

   private Student student;

   @Id

   public Long getId() {

      return id;

   }

   @Column(nullable = false)

   @Temporal(TemporalType.DATE)

   public Date getDate() {

      return date;

   }

   @OneToOne(mappedBy = "studentCard")

   public Student getStudent() {

      return student;

   }

   public void setId(Long id) {

      this.id = id;

   }

   public void setDate(Date date) {

      this.date = date;

   }

   public void setStudent(Student student) {

      this.student = student;

   }

}


    
    mappedBy : 

    相当于 xml 方式配置的 inverse = "true",表示将表间关联的这种关系的维护交给对方,对方才是关系的真正维护者,拥有主导权。 

    在双向关联中,有且仅有一端是作为主体( owner )端存在,主体端负责维护联接列。对于不需要维护这种关系的从表则通过 mappedBy 属性进行声明。

    最后不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.








  
posted on 2012-10-13 13:20 fancydeepin 阅读(7798) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: