zoukankan      html  css  js  c++  java
  • Hibernate学习笔记(四)关系映射之一对一关联映射

    一、 一对一关联映射

    ²        两个对象之间是一对一的关系,如Person-IdCard(人—身份证号)

    ²        有两种策略可以实现一对一的关联映射

    Ø        主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联。

    Ø        唯一外键关联:外键关联,本来是用于多对一的配置,但是如果加上唯一的限制之后,也可以用来表示一对一关联关系。

    对象模型

    实体类:

    /** 人-实体类 */

    public class Person {

        private int id;

        private String name;

        public int getId() {return id;  }

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

        public String getName() {return name;}

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

    }

    /**身份证-实体类*/

    public class IdCard {

        private int id;

        private String cardNo;

        public int getId() {return id;}

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

        public String getCardNo(){ return cardNo;}

        public void setCardNo(StringcardNo) {this.cardNo = cardNo;}

    }

    (一) 唯一外键关联-单向(unilateralism)

    1、 说明:

    人—-> 身份证号(PersonàIdCard),从IdCard看不到Person对象

    2、 对象模型

    需要在Person类中持有IdCard的一个引用idCard,则IdCard中没有Person的引用

    3、 关系模型

    关系模型目的:是实体类映射到关系模型(数据库中),是要求persion中添加一个外键指向idcard

    4、 实体类:

          注:IdCard是被引用对象,没有变化。

             /** 人-实体类 */

    public class Person {

        private int id;

        private String name;

    private IdCard idCard;//引用IdCard对象

        public int getId() {return id;  }

        public void setId(int 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(IdCardidCard) {this.idCard = idCard;}

    }

    5、 xml映射

    IdCard实体类的映射文件:

         因为IdCard是被引用的,所以没有什么特殊的映射

    <hibernate-mapping>

      <class name="com.wjt276.hibernate.IdCard" table="t_idcard">

          <id name="id" column="id">

              <generator class="native"/>

          </id>

          <property name="cardNo"/>

      </class>

    </hibernate-mapping>

    Person实体类的映射文件

             在映射时需要添加一个外键的映射,就是指定IdCard的引用的映射。这样映射到数据库时,就会自动添加一个字段并作用外键指向被引用的表

    <hibernate-mapping>

      <class name="com.wjt276.hibernate.Person" table="t_person">

          <id name="id" column="id">

              <generator class="native"/>

          </id>

          <property name="name"/> 

    <!-- <many-to-one>:在多的一端(当前Person一端),加入一个外键(当前为idCard)指向一的一端(当前IdCard),但多对一 关联映射字段是可以重复的,所以需要加入一个唯一条件unique="true",这样就可以此字段唯一了。-->

          <many-to-one name="idCard" unique="true"/>

      </class>

    </hibernate-mapping>

    注意:这里的<many-to-one>标签中的name属性值并不是数据库中的字段名,而是Person实体类中引用IdCard对象成员属性的getxxx方法后面的xxx(此处是getIdCard,所以是idCard),要求第一个字段小写。如果不指定column属性,则数据库中的字段名同name值

    6、 annotateon注解映射

    注意IdCard是被引用对象,除正常注解,无需要其它注解

    /**身份证*/

    @Entity

    public class IdCard {

      private int id;

      private String cardNo;

      @Id

      @GeneratedValue

      public int getId() {return id;}

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

      public String getCardNo(){return cardNo;}

      public void setCardNo(StringcardNo) {this.cardNo = cardNo;}

    }

    而引用对象的实体类需要使用@OneToOne进行注解,来表面是一对一的关系

    再使用@JoinColumn注解来为数据库表中这个外键指定个字段名称就可以了。如果省略@JoinColumn注解,则hibernate会自动为其生成一个字段名(好像是:被引用对象名称_被引用对象的主键ID)

    /** 人-实体类 */

    @Entity

    public class Person {

      private int id;

      private IdCard idCard;//引用IdCard对象  

      private String name;   

      @Id

      @GeneratedValue

      public int getId() {return id;}

      @OneToOne//表示一对一的关系

      @JoinColumn(name="idCard")//为数据中的外键指定个名称

      public IdCard getIdCard(){ return idCard;}

      public String getName() {return name;}

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

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

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

    }

    7、 生成的SQL语句:

        create tableIdCard (

            id integernot null auto_increment,

            cardNo varchar(255),

            primary key(id)

        )

        create tablePerson (

            id integernot null auto_increment,

            namevarchar(255),

            idCardinteger,//新添加的外键

            primary key(id)

        )

        alter tablePerson

            add indexFK8E488775BE010483 (idCard),

            addconstraint FK8E488775BE010483

            foreign key(idCard) //外键

            referencesIdCard (id)//引用IdCard的id字段

     

    8、 存储测试

    Session session = sf.getCurrentSession();      

    IdCard idCard = new IdCard();

    idCard.setCardNo("88888888888888888888888");       

    session.beginTransaction();

    // 如果先不保存idCard,则出抛出Transient异常,因为idCard不是持久化状态。

    session.save(idCard);      

    Person person = new Person();

    person.setName("菜10");

    person.setIdCard(idCard);

    session.save(person);

    session.getTransaction().commit();

    (二) 唯一外键关联-双向

    1、 说明:

    人<—-> 身份证号(Person<->IdCard)双向:互相持有对方的引用

    2、 对象模型:


    3、 关系模型:

    关系模型没有任务变化,同单向

    4、 实体类:

    实体类,只是相互持有对象的引用,并且要求getter和setter方法

    5、 xml映射

    Person实体类映射文件:同单向的没有变化

    IdCard实体类映射文件:如果使用同样的方法映射,这样就会在表中也添加一个外键指向对象,但对象已经有一个外键指向自己了,这样就造成了庸字段,因为不需要在表另外添加字段,而是让hibernate在加载这个对象时,会根据对象的ID到对方的表中查询外键等于这个ID的记录,这样就把对象加载上来了。也同样需要使用<one-to-one>标签来映射,但是需要使用property-ref属性来指定对象持有你自己的引用的成员属性名称(是gettxxxx后面的名称),这样在生成数据库表时,就不会再添加一个多于的字段了。数据加载时hibernate会根据这些配置自己加载数据

        <class name="com.wjt276.hibernate.IdCard" table="idcard">

            <id name="id" column="id">

                <generator class="native"/></id>

            <property name="cardNo"/>

            <!--<one-to-one>标签:告诉hibernate如何加载其关联对象

                property-ref属性:是根据哪个字段进行比较加载数据 -->

            <one-to-one name="person" property-ref="idCard"/>

        </class>

    一对一 唯一外键 关联映射 双向 需要在另一端(当前IdCard),添加<one-to-one>标签,指示hibernate如何加载其关联对象(或引用对象),默认根据主键加载(加载person),外键关联映射中,因为两个实体采用的是person的外键来维护的关系,所以不能指定主键加载person,而要根据person的外键加载,所以采用如下映射方式:

        <!--<one-to-one>标签:告诉hibernate如何加载其关联对象

                property-ref属性:是根据哪个字段进行比较加载数据 -->

            <one-to-one name="person" property-ref="idCard"/>

    6、 annotateon注解映射

    Person注解映射同单向一样

    IdCard注解映射如下:使用@OneToOne注解来一对一,但这样会在表中多加一个字段,因为需要使用对象的外键来加载数据,所以使用属性mappedBy属性在实现这个功能

    @Entity

    public class IdCard {

      private int id;

      private String cardNo;

      private Person person; 

      //mappedBy:在指定当前对象在被Person对象的idCard做了映射了

      //此值:当前对象持有引用对象中引用当前对象的成员属性名称(getXXX后的名称)

      //因为Person对象的持有IdCard对象的方法是getIdCard()因为需要小写,所以为idCard

      @OneToOne(mappedBy="idCard")

      public Person getPerson(){return person;}

      public void setPerson(Person person){this.person = person;}

      @Id

      @GeneratedValue

      public int getId() {return id;}

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

      public String getCardNo(){return cardNo;}

      public void setCardNo(StringcardNo) {this.cardNo = cardNo;}

    }

    7、 生成SQL语句

    因为关系模型没有变化,也就是数据库的结构没有变化,只是在数据加载时需要相互加载对方,这由hibernate来完成。因为生成的sql语句同单向一样

    8、 存储测试

    存储同单向一样

    9、 总结:

    规律:凡是双向关联,必设mappedBy

    (三) 主键关联-单向(不重要)

    主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联。

    1、 说明:

    人—-> 身份证号(PersonàIdCard),从IdCard看不到Person对象

    2、 对象模型

    站在人的角度来看,对象模型与唯一外键关联一个,只是关系模型不同

    3、 关系模型

    因为是person引用idcard,所以idcard要求先有值。而person的主键值不是自己生成的。而是参考idcard的值,person表中即是主键,同时也是外键

    4、 实体类:

    实体类同 一对一 唯一外键关联的实体类一个,在person对象中持有idcard对象的引用(代码见唯一外键关系)

    5、 xml映射

    IdCard映射文件,先生成ID

        <class name="com.wjt276.hibernate.IdCard" table="t_idcard">

            <id name="id"column="id">

                <generator class="native"/>

            </id>

            <property name="cardNo"/>

        </class>

    Person实体类映射文件,ID是根据IdCard主键值   

    <class name="com.wjt276.hibernate.Person"table="t_person">

            <id name="id"column="id">

    <!--因为主键不是自己生成的,而是作为一个外键(来源于其它值),所以使用foreign生成策略

    foreign:使用另外一个相关联的对象的标识符,通常和<one-to-one>联合起来使用。再使用元素<param>的属性值指定相关联对象(这里Person相关联的对象为idCard,则标识符为idCard的id)为了能够在加载person数据同时加载IdCard数据,所以需要使用一个标签<one-to-one>来设置这个功能。  -->

                <generator class="foreign">

                    <!-- 元素<param>属性name的值是固定为property -->

                    <param name="property">idCard</param>

                </generator>

            </id>

            <property name="name"/>

            <!-- <one-to-one>标签

            表示如何加载它的引用对象(这里引用对象就指idCard这里的name值是idCard),同时也说是一对一的关系。 默认方式是根据主键加载(把person中的主键取出再到IdCard中来取相关IdCard数据。) 我们也说过此主键也作为一个外键引用 了IdCard,所以需要加一个数据库限制(外键约束)constrained="true"     -->

            <one-to-one name="idCard"constrained="true"/> 

    6、 annotateon注解映射

    Person实体类注解

    方法:只需要使用@OneToOne注解一对一关系,再使用@PrimaryKeyJoinColumn来注解主键关系映射。

    @Entity

    public class Person {

        private int id;

        private IdCard idCard;//引用IdCard对象  

        private String name;   

        @Id

        public int getId() {return id;}

        @OneToOne//表示一对一的关系

        @PrimaryKeyJoinColumn//注解主键关联映射

        public IdCard getIdCard(){ return idCard;}

        public String getName() {return name;}

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

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

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

    }

    IdCard实体类,不需要持有对象的引用,正常注解就可以了。

    7、 生成SQL语句

    生成的两个表并没有多余的字段,因为是通过主键在关键的

        create tableIdCard (

            id integernot null auto_increment,

            cardNovarchar(255),

            primary key (id)

        )

        create tablePerson (

            id integernot null,

            namevarchar(255),

            primary key(id)

      )

    alter table person

    add index FK785BED805248EF3 (id),

    add constraint FK785BED805248EF3

    foreign key (id) references idcard (id)

        注意:annotation注解后,并没有映射出外键关键的关联,而xml可以映射,是主键关联不重要

    8、 存储测试

    session = HibernateUtils.getSession();

    tx = session.beginTransaction();

    IdCard idCard = new IdCard();

    idCard.setCardNo("88888888888888888888888");

               

    Person person = new Person();

    person.setName("菜10");

    person.setIdCard(idCard);

               

    //不会出现TransientObjectException异常

    //因为一对一主键关键映射中,默认了cascade属性。

    session.save(person);

    tx.commit();

    9、 总结

    让两个实体对象的ID保持相同,这样可以避免多余的字段被创建

    <id name="id"column="id">

          <!—person的主键来源idcard,也就是共享idCard的主键-->

              <generator class="foreign">

                  <param name="property">idCard</param>

              </generator>

          </id>

          <property name="name"/>

    <!—one-to-one标签的含义:指示hibernate怎么加载它的关联对象,默认根据主键加载

      constrained="true",表面当前主键上存在一个约束:person的主键作为外键参照了idCard-->

          <one-to-one name="idCard" constrained="true"/>

    (四) 主键关联-双向(不重要)

    主键关联:即让两个对象具有相同的主键值,以表明它们之间的一一对应的关系;数据库表不会有额外的字段来维护它们之间的关系,仅通过表的主键来关联。

    主键关联映射,实际是数据库的存储结构并没有变化,只是要求双方都可以持有对象引用,也就是说实体模型变化,实体类都相互持有对方引用。

    另外映射文件也变化了。

    1、 xml映射

    Person实体类映射文件不变,

    IdCard如下:

    <class name="com.wjt276.hibernate.IdCard" table="t_idcard">

            <id name="id" column="id">

                <generator class="native"/> </id>

            <property name="cardNo"/>

    <!—one-to-one标签的含义:指示hibernate怎么加载它的关联对象(这里的关联对象为person),默认根据主键加载-->

            <one-to-one name="person"/>

        </class>

    2、 annotateon注解映射:

    Person的注解不变,同主键单向注解

     

    IdCard注解,只需要在持有对象引用的getXXX前加上

            @OneToOne(mappedBy="idCard") 如下:

    @Entity

    public class IdCard {

        private int id;

        private String cardNo;

        private Person person; 

        @OneToOne(mappedBy="idCard")

        public Person getPerson(){

            return person;

        }}

    (五) 联合主键关联(Annotation方式)

    实现上联合主键的原理同唯一外键关联-单向一样,只是使用的是@JoinColumns,而不是@JoinColumn,实体类注解如下:

        @OneToOne

        @JoinColumns(

            {

                @JoinColumn(name="wifeId", referencedColumnName="id"),

                @JoinColumn(name="wifeName", referencedColumnName="name")

            }

        )

        public WifegetWife() {

            return wife;

        }

            注意:@oinColumns注解联合主键一对一联系,然后再使用@JoinColumn来注解当前表中的外键字段名,并指定关联哪个字段,使用referencedColumnName指定哪个字段的名称

     

    二、 component(组件)关联映射

    (一) Component关联映射:

    目前有两个类如下:

             大家发现用户与员工存在很多相同的字段,但是两者有不可以是同一个类中,这样在实体类中每次都要输入很多信息,现在把联系信息抽取出来成为一个类,然后在用户、员工对象中引用就可以,如下:

    值对象没有标识,而实体对象具有标识,值对象属于某一个实体,使用它重复使用率提升,而且更清析。

    以上关系的映射称为component(组件)关联映射

    在hibernate中,component是某个实体的逻辑组成部分,它与实体的根本区别是没有oid,component可以成为是值对象(DDD)。

    采用component映射的好处:它实现了对象模型的细粒度划分,层次会更加分明,复用率会更高。

    (二) User实体类:

    public class User {

    private int id;

      private String name;   

      private Contact contact;//值对象的引用   

      public int getId() {return id;}

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

      public String getName() {   return name;}

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

      public ContactgetContact() {   return contact;}

      public void setContact(Contactcontact) {   this.contact = contact;}   

    }

    (三) Contact值对象:

    public class Contact {

      private String email;  

      private String address;

      private String zipCode;

      private String contactTel;

      public String getEmail(){  return email;}

      public void setEmail(Stringemail) {    this.email = email; }

      public StringgetAddress() {return address;}

      public void setAddress(Stringaddress) {this.address = address;}

      public StringgetZipCode() {return zipCode;}

      public void setZipCode(StringzipCode) {this.zipCode = zipCode;}

      public StringgetContactTel() { return contactTel;}

      public voidsetContactTel(String contactTel){this.contactTel = contactTel;}

    }

    (四) xml--User映射文件(组件映射):

    <hibernate-mapping>

      <class name="com.wjt276.hibernate.User" table="t_user">

          <id name="id" column="id">

              <generator class="native"/>

          </id>

          <property name="name" column="name"/>

          <!-- <component>标签用于映射Component(组件)关系

              其内部属性正常映射。

           -->

          <component name="contact">

              <property name="email"/>

              <property name="address"/>

              <property name="zipCode"/>

              <property name="contactTel"/>          

          </component>

      </class>

    </hibernate-mapping>

    (五) annotateon注解

    使用@Embedded用于注解组件映射,表示嵌入对象的映射

    @Entity

    public class User {

        private int id;

        private String name;   

        private Contact contact;//值对象的引用   

        @Id

        @GeneratedValue

        public int getId() {    return id;}

     

        @Embedded//用于注解组件映射,表示嵌入对象的映射

        public ContactgetContact() {return contact;}

        public void setContact(Contactcontact) {this.contact = contact;}  

    Contact类是值对象,不是实体对象,是属于实体类的某一部分,因此没有映射文件

    (六) 导出数据库输出SQL语句:

        create table User (

            id integer not null auto_increment,

            address varchar(255),

            contactTel varchar(255),

            email varchar(255),

            zipCode varchar(255),

            name varchar(255),

            primary key (id)

    )

    (七) 数据表结构:


    注:虽然实体类没有基本联系信息,只是有一个引用,但在映射数据库时全部都映射进来了。以后值对象可以重复使用,只要在相应的实体类中加入一个引用即可。

    (八) 组件映射数据保存:

               session =HibernateUtils.getSession();

               tx =session.beginTransaction();

     

               User user= new User();

               user.setName("10");

              

               Contactcontact = new Contact();

               contact.setEmail("wjt276");

               contact.setAddress("aksdfj");

               contact.setZipCode("230051");

               contact.setContactTel("3464661");

              

               user.setContact(contact);

               session.save(user);

              

               tx.commit();

    实体类中引用值对象时,不用先保存值对象,因为它不是实体类,它只是一个附属类,而session.save()中保存的对象是实体类。

  • 相关阅读:
    OpenCV中 常用 函数 的作用
    OpenCV中Mat的使用
    awk --- 常用技巧
    Specify 的含义 ------ 转载
    关于CPU CACHE工作机制的学习
    关于CPU Cache -- 程序猿需要知道的那些事
    ARM920T的Cache
    Learn Git and GitHub
    朴素贝叶斯分类器(MNIST数据集)
    k-近邻算法(KNN)识别手写数字
  • 原文地址:https://www.cnblogs.com/hustfly/p/3442094.html
Copyright © 2011-2022 走看看