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的引用

Hibernate学习笔记(四)关系映射之一对一关联映射

3、 关系模型

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

Hibernate学习笔记(四)关系映射之一对一关联映射

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字段

Hibernate学习笔记(四)关系映射之一对一关联映射

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、 对象模型:

Hibernate学习笔记(四)关系映射之一对一关联映射

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、 对象模型

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

Hibernate学习笔记(四)关系映射之一对一关联映射

3、 关系模型

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

Hibernate学习笔记(四)关系映射之一对一关联映射

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"/>

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

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

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

另外映射文件也变化了。

Hibernate学习笔记(四)关系映射之一对一关联映射

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关联映射:

目前有两个类如下:

Hibernate学习笔记(四)关系映射之一对一关联映射

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

Hibernate学习笔记(四)关系映射之一对一关联映射

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

以上关系的映射称为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)

)

(七) 数据表结构:

Hibernate学习笔记(四)关系映射之一对一关联映射

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

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

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()中保存的对象是实体类。

上一篇:解决ubuntu开机进入grub界面的问题


下一篇:Oracle数据库导入与导出方法简述