Hibernate学习笔记之ORM实体间关系“OneToOne”详解
2016-08-10 16:17
651 查看
我们知道ORM是对象—关系映射的意思,是沟通对象和底层关系数据库之间的桥梁,可以简单的将对象和关系数据库中的表对应起来,在关系数据库中,不同的表之间存在不同的关系,比如一对一,一对多,多对多等,那怎样用Java语言在上层对底层关系进行实现呢,我们本文来详细讲述一下。
一对一的映射关系
一对一的映射关系应该是最简单最基础的一种映射关系了,我们现实生活中有很多一对一映射关系的例子,最典型的就是一个人对应一个身份证号码,这里我们就用这个例子作为我们的讲述基础。
注意:本文所有的实例都将采用两种实现方法:Annotation注释和Xml配置文件方式,以供自己和大家深入理解。
1.1 一对一单向外键(Annotation)
单向外键的意思就是一个实体通过外键关联到另一个实体的主键。注:一对一,则外键必须为唯一约束。也就是说,映射关系交给一方来维护。
我们这里新建两个实体类:Students和IdCard,并且Students通过外键关联到IdCard的主键。
然后执行SchemaExport():
执行结果为:
可以看到:students表中插入了pid外键,而pid是idcard表的主键,这样两者就关联起来了。
现在,我们向两个表中插入数据:
执行结果为:
1.2 一对一单向外键(XML)
前面已经使用Annotation注解的方式实现了OneToOne关联方式,现在来简单介绍以下怎样通过配置文件,也就是*.hbm.xml,来达到相同的目的。
Students.hbm.xml的配置:
IdCard.hbm.xml的配置:
关键的文件配置已经完成,其他操作和Annotation几乎相同,这里就不再赘述。
2.1 一对一双向外键关联(Annotation)
“双向一对一”顾名思义就是两者都可以持有对方的控制权,但是呢一次,也就是一次实现过程,一次数据持久化,控制权只能交给一方,不可能双方都设置外键保存关联关系,否则双方都无法保存,这一点要特别注意。
双向关联必须设置mappedBy属性(基于外键的双向关联):
在主控方设置:@OneToOne
在被控方设置:@OneToOne(mappedBy=”对方的关联属性名”)
(注:还有一种是基于主键的双向关联,我们这里就不涉及了,大家可以自己做做看,网上有相关资料。)
我们还是首先创建实体类IdCard和Students:
然后执行SchemaExport():
执行结果:
可以看到,控制权是在IdCard的手中。
现在,我们向两个表中插入数据:
执行结果为:
我们可以看到数据已经插入了。
2.2 一对一双向外键关联(XML)
这里我们的配置文件分别为:
Students.hbm.xml
IdCard.hbm.xml
3.1 一对一单向外键联合主键(Annotation)
所谓主键就是可以唯一确定该行数据,由此可以知道,当一个字段不能决定该行的值时,就要考虑采用多个字段作为主键。比如,对于学校来说,班号可以决定班级,但是决定不了班级里的某个人,表示班级里的某个人就需要用班号+该学生在该班内的编号(当然也可以用唯一的学号来决定,这里只是举个例子)。
我们实际基于Hibernate实现联合主键的时候,要经历的步骤为:
1.创建主键类;
2.主键类必须实现serializable接口,重写hashcode()和equals()方法;
3.主键类要用@Embeddable标注,实体类使用@EmbeddedId标注。
我们首先创建主键类IdCardPK:
再创建实体类IdCard:
再创建实体类Students:
执行SchemaExport():
执行结果为:
可以看到,idcard表中有两个主键bloodType和pid作为联合主键,而在students表中使用联合主键作为外键。
我们执行插入数据的操作:
执行结果为:
这就和我们预期的是一样的了。
3.2 一对一单向外键联合主键(XML)
IdCard.hbm.xml
Students.hbm.xml
4.1 一对一组件关联(Annotation)
组件类就是一个POJO类,被嵌入在一个实体类中,是一个实体类的组件部分。实体类需要使用@Embedded标注。
在Hibernate中,component是某个实体的逻辑组成部分,它与实体的根本区别是没有oid,component可以成为是值对象(DDD)。
采用component映射的好处:它实现了对象模型的细粒度划分,层次会更加分明,复用率会更高。
首先创建组件类IdCard:
创建实体类Students:
执行SchemaExport():
执行结果为:
我们继续执行插入数据的操作:
执行结果为:
4.2 一对一组件关联(XML)
这里用配置实体类Students的xml文件就行了:
执行结果和使用注解方式相同。
以上就是ORM的一对一主键关联关系,后续我会将一对多,多对多的详解写出来,供大家共同学习!
一对一的映射关系
一对一的映射关系应该是最简单最基础的一种映射关系了,我们现实生活中有很多一对一映射关系的例子,最典型的就是一个人对应一个身份证号码,这里我们就用这个例子作为我们的讲述基础。
注意:本文所有的实例都将采用两种实现方法: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的一对一主键关联关系,后续我会将一对多,多对多的详解写出来,供大家共同学习!
相关文章推荐
- hibernate学习笔记one-to-one
- Hibernate实体关系映射(OneToMany单边)——完整实例
- Hibernate学习笔记(3)Hibernate映射文件之set key one-to-many
- Hibernate学习笔记:一对一的关联关系(one-to-one)
- NHibernate学习笔记(二):one-to-one关系映射
- Hibernate实体关系映射(OneToMany、ManyToOne双边)——完整实例
- Rhythmk 学习 Hibernate 06 - Hibernate 表间关系 [One To One]
- Rhythmk 学习 Hibernate 05 - Hibernate 表间关系 [ManyToOne,OneToMany]
- Hibernate学习笔记--第二篇 关联关系映射 many –to –one
- 学习笔记之什么是持久化和对象关系映射ORM技术
- Hibernate 学习笔记(二)-- 实体映射
- Hibernate中one-to-one的深入学习
- Hibernate 表关系描述之OneToOne
- OGRE 学习笔记(启动详解 + 实体的加载与显示)
- Hibernate 表关系描述之OneToOne
- Hibernate 表关系描述之OneToMany
- 『收集』学习笔记之什么是持久化和对象关系映射ORM技术
- Hibernate 学习笔记(四)-- 实体层设计之Table per subclass
- 学习笔记之什么是持久化和对象关系映射ORM技术
- Hibernate one to one详解