您的位置:首页 > 编程语言 > Java开发

Hibernate学习笔记之ORM实体间关系“OneToOne”详解

2016-08-10 16:17 651 查看
我们知道ORM是对象—关系映射的意思,是沟通对象和底层关系数据库之间的桥梁,可以简单的将对象和关系数据库中的表对应起来,在关系数据库中,不同的表之间存在不同的关系,比如一对一,一对多,多对多等,那怎样用Java语言在上层对底层关系进行实现呢,我们本文来详细讲述一下。

一对一的映射关系

一对一的映射关系应该是最简单最基础的一种映射关系了,我们现实生活中有很多一对一映射关系的例子,最典型的就是一个人对应一个身份证号码,这里我们就用这个例子作为我们的讲述基础。

注意:本文所有的实例都将采用两种实现方法:Annotation注释和Xml配置文件方式,以供自己和大家深入理解。

1.1 一对一单向外键(Annotation)

单向外键的意思就是一个实体通过外键关联到另一个实体的主键。注:一对一,则外键必须为唯一约束。也就是说,映射关系交给一方来维护。

我们这里新建两个实体类:Students和IdCard,并且Students通过外键关联到IdCard的主键。

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

@Entity
public class Students {
private int sid;
private String sname;
private IdCard cardId;

@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="pid",unique=true)//关联到IdCard的主键pid
public IdCard getCardId() {
return cardId;
}
public void setCardId(IdCard cardId) {
this.cardId = cardId;
}

@Id
@GeneratedValue
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
}


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.hibernate.annotations.GenericGenerator;

@Entity
public class IdCard {

private String pid;//身份证号码
private String province;//省份

@Id
@GeneratedValue(generator="pid")//generator指明主键生成器的名称
@GenericGenerator(name="pid",strategy="assigned")
//@GenericGenerator为hibernate特有的注解方式,使其可以使用主键生成策略assigned
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
}


然后执行SchemaExport():

@Test
public void testSchemaExport() {

SchemaExport se = new SchemaExport(new AnnotationConfiguration().configure());
se.create(true, true);
//第一个true就是把DDL语句输出到控制台,第二个true就是根据持久化类和映射文件先执行删除再执行创建操作
}


执行结果为:



可以看到:students表中插入了pid外键,而pid是idcard表的主键,这样两者就关联起来了。

现在,我们向两个表中插入数据:

@Test
public void testSave() {

Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();

try {

IdCard c = new IdCard();
c.setPid("8888888888888888888");
c.setProvince("guangdong");
Students s = new Students();
s.setSname("zhangsan");
s.setCardId(c);

session.save(c);
//先保存外键对象
session.save(s);
//再保存主对象

tx.commit();

} catch (Exception e) {
e.printStackTrace();
tx.rollback();
}
}


执行结果为:



1.2 一对一单向外键(XML)

前面已经使用Annotation注解的方式实现了OneToOne关联方式,现在来简单介绍以下怎样通过配置文件,也就是*.hbm.xml,来达到相同的目的。

Students.hbm.xml的配置:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-26 21:10:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="persistence_class_fk.Students" table="students">
<id name="sid" type="int">
<column name="sid"/>
<generator class="native"/>
</id>
<property generated="never" lazy="false" name="sname" type="string">
<column name="sname"/>
</property>

<!-- 一对一单向外键   主控方配置   name表示受控方对象  column表示关联的外键-->
<many-to-one name="cardId" column="pid" unique="true"/>

</class>
</hibernate-mapping>


IdCard.hbm.xml的配置:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-26 21:10:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="persistence_class_fk.IdCard" table="idcard">
<id name="pid" type="string">
<column name="pid"/>
<generator class="assigned"/>
</id>
<property generated="never" lazy="false" name="province" type="string">
<column name="province"/>
</property>
</class>
</hibernate-mapping>


关键的文件配置已经完成,其他操作和Annotation几乎相同,这里就不再赘述。

2.1 一对一双向外键关联(Annotation)

“双向一对一”顾名思义就是两者都可以持有对方的控制权,但是呢一次,也就是一次实现过程,一次数据持久化,控制权只能交给一方,不可能双方都设置外键保存关联关系,否则双方都无法保存,这一点要特别注意。

双向关联必须设置mappedBy属性(基于外键的双向关联):

在主控方设置:@OneToOne

在被控方设置:@OneToOne(mappedBy=”对方的关联属性名”)

(注:还有一种是基于主键的双向关联,我们这里就不涉及了,大家可以自己做做看,网上有相关资料。)

我们还是首先创建实体类IdCard和Students:

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

@Entity
public class Students {
private int sid;
private String sname;
private IdCard cardId;

@OneToOne(mappedBy="stu")
//只要是双向关联,就一定要指定mappedBy,这里将控制权交给IdCard
//@OneToOne(cascade=CascadeType.ALL)
//@JoinColumn(name="pid",unique=true)
public IdCard getCardId() {
return cardId;
}
public void setCardId(IdCard cardId) {
this.cardId = cardId;
}

@Id    //当主键为int类型时,主键生成策略默认为:native
@GeneratedValue
//@GeneratedValue(strategy=GenerationType.AUTO)
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
}


import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

import org.hibernate.annotations.GenericGenerator;

@Entity
public class IdCard {

private String pid;//身份证号码
private String province;//省份
private Students stu;

//@OneToOne(mappedBy="cardId")
//只要是双向关联,就一定要指定mappedBy,这里将控制权交给Students

@OneToOne(cascade=CascadeType.ALL)
//这种情况是IdCard作为主控方的测试
public Students getStu() {
return stu;
}
public void setStu(Students stu) {
this.stu = stu;
}

@Id
@GeneratedValue(generator="pid")
@GenericGenerator(name="pid",strategy="assigned")
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
}


然后执行SchemaExport():

@Test
public void testSchemaExport() {

SchemaExport se = new SchemaExport(new AnnotationConfiguration().configure());
se.create(true, true);
//第一个true就是把DDL语句输出到控制台,第二个true就是根据持久化类和映射文件先执行删除再执行创建操作
}


执行结果:



可以看到,控制权是在IdCard的手中。

现在,我们向两个表中插入数据:

@Test
public void testSave() {

Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();

try {

//此种情况为将Students的控制权交给IdCard
Students s = new Students();
s.setSname("lisi");
IdCard c = new IdCard();
c.setPid("6666666666666666");
c.setProvince("shanghai");
c.setStu(s);

session.save(s);
session.save(c);

tx.commit();

} catch (Exception e) {

e.printStackTrace();
tx.rollback();
}
}


执行结果为:



我们可以看到数据已经插入了。

2.2 一对一双向外键关联(XML)

这里我们的配置文件分别为:

Students.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-26 21:10:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="persistence_class_bfk.Students" table="students">
<id name="sid" type="int">
<column name="sid"/>
<generator class="native"/>
</id>
<property generated="never" lazy="false" name="sname" type="string">
<column name="sname"/>
</property>

<!--Students为受控方-->
<one-to-one name="cardId" property-ref="stu" />

</class>
</hibernate-mapping>


IdCard.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-26 21:10:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="persistence_class_bfk.IdCard" table="idcard">
<id name="pid" type="string">
<column name="pid"/>
<generator class="assigned"/>
</id>
<property generated="never" lazy="false" name="province" type="string">
<column name="province"/>
</property>

<!--IdCard为主控方:将控制权交给IdCard中的stu-->
<many-to-one name="stu" column="sid" unique="true"/>
</class>
</hibernate-mapping>


3.1 一对一单向外键联合主键(Annotation)

所谓主键就是可以唯一确定该行数据,由此可以知道,当一个字段不能决定该行的值时,就要考虑采用多个字段作为主键。比如,对于学校来说,班号可以决定班级,但是决定不了班级里的某个人,表示班级里的某个人就需要用班号+该学生在该班内的编号(当然也可以用唯一的学号来决定,这里只是举个例子)。

我们实际基于Hibernate实现联合主键的时候,要经历的步骤为:

1.创建主键类;

2.主键类必须实现serializable接口,重写hashcode()和equals()方法;

3.主键类要用@Embeddable标注,实体类使用@EmbeddedId标注。

我们首先创建主键类IdCardPK:

import java.io.Serializable;

import javax.persistence.Embeddable;

//身份证主键类

@Embeddable
public class IdCardPK implements Serializable{

/**
*
*/
private static final long serialVersionUID = 1L;
private String pid;//身份证号码
private String bloodType;//血型

public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getBloodType() {
return bloodType;
}
public void setBloodType(String bloodType) {
this.bloodType = bloodType;
}
}


再创建实体类IdCard:

import javax.persistence.CascadeType;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

import org.hibernate.annotations.GenericGenerator;

@Entity
public class IdCard {

private IdCardPK pk;
private String province;//省份
private Students stu;

@OneToOne(mappedBy="cardId")//只要是双向关联,就一定要指定mappedBy,这里将控制权交给Students
public Students getStu() {
return stu;
}
public void setStu(Students stu) {
this.stu = stu;
}

@EmbeddedId
public IdCardPK getPk() {
return pk;
}
public void setPk(IdCardPK pk) {
this.pk = pk;
}

public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}

@Override
public int hashCode() {
// TODO Auto-generated method stub
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super.equals(obj);
}
}


再创建实体类Students:

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.OneToOne;

@Entity
public class Students {
private int sid;
private String sname;
private IdCard cardId;

@OneToOne(cascade=CascadeType.ALL)
@JoinColumns({
@JoinColumn(name="pid",referencedColumnName="pid"),
@JoinColumn(name="bloodType",referencedColumnName="bloodtype")
})
public IdCard getCardId() {
return cardId;
}
public void setCardId(IdCard cardId) {
this.cardId = cardId;
}

@Id    //当主键为int类型时,主键生成策略默认为:native
@GeneratedValue
//@GeneratedValue(strategy=GenerationType.AUTO)
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
}


执行SchemaExport():

@Test
public void testSchemaExport() {

SchemaExport se = new SchemaExport(new AnnotationConfiguration().configure());
se.create(true, true);
//第一个true就是把DDL语句输出到控制台,第二个true就是根据持久化类和映射文件先执行删除再执行创建操作
}


执行结果为:



可以看到,idcard表中有两个主键bloodType和pid作为联合主键,而在students表中使用联合主键作为外键。

我们执行插入数据的操作:

@Test
public void testSave() {

Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();

try {

IdCardPK pk = new IdCardPK();
pk.setBloodType("A");
pk.setPid("5555555555555555");

IdCard c = new IdCard();
c.setPk(pk);
c.setProvince("beijing");

Students s = new Students();
s.setSname("zhaoliu");
s.setCardId(c);

session.save(c);
session.save(s);

tx.commit();

} catch (Exception e) {

e.printStackTrace();
tx.rollback();
}
}


执行结果为:



这就和我们预期的是一样的了。

3.2 一对一单向外键联合主键(XML)

IdCard.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-26 21:10:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="persistence_class_ufk.IdCard" table="idcard">

<!--关键语句,composite为混合,综合的意思,这里指代联合主键-->
<composite-id name="pk" class="persistence_class_ufk.IdCardPK">
<key-property name="pid" column="pid" type="string" />
<key-property name="bloodType"  type="string" />
<generator class="assigned"></generator>
</composite-id>

<property name="province" column="province" type="string" />

</class>
</hibernate-mapping>


Students.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-26 21:10:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="persistence_class_ufk.Students" table="students">
<id name="sid" type="int">
<column name="sid"/>
<generator class="native"/>
</id>
<property generated="never" lazy="false" name="sname" type="string">
<column name="sname"/>
</property>

<!--关键语句-->
<many-to-one name="cardId">
<column name="pid" unique="true" />
<column name="bloodid" />
</many-to-one>

</class>
</hibernate-mapping>


4.1 一对一组件关联(Annotation)

组件类就是一个POJO类,被嵌入在一个实体类中,是一个实体类的组件部分。实体类需要使用@Embedded标注。

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

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

首先创建组件类IdCard:

//身份证类,就是一个POJO类
public class IdCard {

private String pid;//身份证号码
private String province;//省份

public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
}


创建实体类Students:

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Students {

private int sid;
private String sname;
private IdCard cardId;

@Id
@GeneratedValue
public int getSid() {
return sid;
}
public void setSid(int sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}

@Embedded
public IdCard getCardId() {
return cardId;
}
public void setCardId(IdCard cardId) {
this.cardId = cardId;
}
}


执行SchemaExport():

@Test
public void testSchemaExport() {

SchemaExport se = new SchemaExport(new AnnotationConfiguration().configure());
se.create(true, true);
//第一个true就是把DDL语句输出到控制台,第二个true就是根据持久化类和映射文件先执行删除再执行创建操作
}


执行结果为:



我们继续执行插入数据的操作:

@Test
public void testSave() {

Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();

try {

IdCard c = new IdCard();
c.setPid("11111111111111111111");
c.setProvince("henan");
Students s = new Students();
s.setSname("aaron");
s.setCardId(c);

session.save(s);

tx.commit();

} catch (Exception e) {

e.printStackTrace();
tx.rollback();
}
}


执行结果为:



4.2 一对一组件关联(XML)

这里用配置实体类Students的xml文件就行了:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2016-7-26 21:10:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="persistence_class_component.Students" table="students">
<id name="sid" type="int">
<column name="sid"/>
<generator class="native"/>
</id>
<property generated="never" lazy="false" name="sname" type="string">
<column name="sname"/>
</property>

<component name="cardId" class="persistence_class_component.IdCard">
<property name="pid" column="pid" type="string"></property>
<property name="province" type="string"></property>
</component>

</class>
</hibernate-mapping>


执行结果和使用注解方式相同。

以上就是ORM的一对一主键关联关系,后续我会将一对多,多对多的详解写出来,供大家共同学习!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息