hibernate的OneToOne映射包括外键的单/双向映射,主键的单/双向映射,联合主键映射等。
关于OneToOne的映射,hibernate的annotation的API做了如下描述:
使用@OneToOne注解可以建立实体bean之间的一对一的关联. 一对一关联有三种情况: 一是关联的实体都共享同样的主键, 二是其中一个实体通过外键关联到另一个实体的主键 (注意要模拟一对一关联必须在外键列上添加唯一约束). 三是通过关联表来保存两个实体之间的连接关系 (注意要模拟一对一关联必须在每一个外键上添加唯一约束).
首先,我们通过共享主键来进行一对一关联映射:
@Entity public class Body { @Id public Long getId() { return id; } @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn public Heart getHeart() { return heart; } ... }
@Entity public class Heart { @Id public Long getId() { ...} }
上面的例子通过使用注解@PrimaryKeyJoinColumn定义了一对一关联.
下面这个例子使用外键列进行实体的关联.
@Entity public class Customer implements Serializable { @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name="passport_fk") public Passport getPassport() { ... } @Entity public class Passport implements Serializable { @OneToOne(mappedBy = "passport") public Customer getOwner() { ... }
上面这个例子中,Customer 通过Customer 表中名为的passport_fk 外键列和 Passport关联. @JoinColumn注解定义了联接列(join column). 该注解和@Column注解有点类似, 但是多了一个名为referencedColumnName的参数. 该参数定义了所关联目标实体中的联接列. 注意,当referencedColumnName关联到非主键列的时候, 关联的目标类必须实现Serializable, 还要注意的是所映射的属性对应单个列(否则映射无效).
一对一关联可能是双向的.在双向关联中, 有且仅有一端是作为主体(owner)端存在的:主体端负责维护联接列(即更新). 对于不需要维护这种关系的从表则通过mappedBy属性进行声明.mappedBy的值指向主体的关联属性. 在上面这个例子中,mappedBy的值为 passport. 最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.
如果在主体没有声明@JoinColumn,系统自动进行处理: 在主表(owner table)中将创建联接列, 列名为:主体的关联属性名+下划线+被关联端的主键列名. 在上面这个例子中是passport_id, 因为Customer中关联属性名为passport, Passport的主键是id.
The third possibility (using an association table) is very exotic.
第三种方式也许是最另类的(通过关联表).
@Entity public class Customer implements Serializable { @OneToOne(cascade = CascadeType.ALL) @JoinTable(name = "CustomerPassports", joinColumns = @JoinColumn(name="customer_fk"), inverseJoinColumns = @JoinColumn(name="passport_fk") ) public Passport getPassport() { ... } @Entity public class Passport implements Serializable { @OneToOne(mappedBy = "passport") public Customer getOwner() { ... }
Customer通过名为 CustomerPassports的关联表和 Passport关联; 该关联表拥有名为passport_fk的外键列,该 外键指向Passport表,该信息定义为inverseJoinColumn的属性值, 而customer_fk外键列指向Customer表, 该信息定义为 joinColumns的属性值.
这种关联可能是双向的.在双向关联中, 有且仅有一端是作为主体端存在的:主体端负责维护联接列(即更新). 对于不需要维护这种关系的从表则通过mappedBy属性进行声明. mappedBy的值指向主体的关联属性. 在上面这个例子中,mappedBy的值为 passport. 最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.
你必须明确定义关联表名和关联列名.
一.单向关联,包括外键和主键
基于外键关联的单向一对一关联和单向多对一关联几乎是一样的。唯一的不同就是单向一对一关联中的外键字段具有唯一性约束。
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
A unidirectional one-to-one association on a primary key usually uses a special id generator In this example, however, we have reversed the direction of the association:
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )二.使用链接表的单向关联
A unidirectional one-to-one association on a join table is possible, but extremely unusual.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )三.双向关联
A bidirectional one-to-one association on a foreign key is common:
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <many-to-one name="address" column="addressId" unique="true" not-null="true"/> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <one-to-one name="person" property-ref="address"/> </class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
A bidirectional one-to-one association on a primary key uses the special id generator:
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <one-to-one name="address"/> </class> <class name="Address"> <id name="id" column="personId"> <generator class="foreign"> <param name="property">person</param> </generator> </id> <one-to-one name="person" constrained="true"/> </class>
create table Person ( personId bigint not null primary key ) create table Address ( personId bigint not null primary key )四.使用链接表的双向关联
A bidirectional one-to-one association on a join table is possible, but extremely unusual.
<class name="Person"> <id name="id" column="personId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true"> <key column="personId" unique="true"/> <many-to-one name="address" column="addressId" not-null="true" unique="true"/> </join> </class> <class name="Address"> <id name="id" column="addressId"> <generator class="native"/> </id> <join table="PersonAddress" optional="true" inverse="true"> <key column="addressId" unique="true"/> <many-to-one name="person" column="personId" not-null="true" unique="true"/> </join> </class>
create table Person ( personId bigint not null primary key ) create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique ) create table Address ( addressId bigint not null primary key )
1.OneToOne的外键单向映射
先看annotation版本
本例使用Husband和Wife为例,需要在Husband里面引用Wife,并在getWife上使用@OneToOne@JoinColumn(name="wifeId")
Husband类
package com.baosight.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; @Entity public class Husband { private String id; private String name; private Wife wife; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne @JoinColumn(name="wifeId") public Wife getWife() { return wife; } public void setWife(Wife wife) { this.wife = wife; } }Wife类
package com.baosight.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Wife { private String id; private String name; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }使用JUnit进行测试
OrMappingTest类
package com.baosight.model; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.cfg.Configuration; import org.hibernate.tool.hbm2ddl.SchemaExport; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class OrMappingTest { private static SessionFactory sf = null; @BeforeClass public static void beforeClass(){ // 读取配置文件 Configuration cfg = new AnnotationConfiguration(); // 得到session工厂 sf = cfg.configure().buildSessionFactory(); } @Test public void testSchemaExport() { new SchemaExport(new AnnotationConfiguration().configure()).create(false, true); } @AfterClass public static void afterClass(){ // 关闭session工厂 sf.close(); } }运行结果为:
2.OneToOne的外键单向映射
看看xml版本
使用Student和StudentCard,.,需要在StudentCard里面引用Student,并在StudentCard.hbm.xml使用<many-to-one name="student" column="studentId" unique="true"></many-to-one>
Student类package com.baosight.model; public class Student { private String id; private String name; private int age; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }StudentCard类
package com.baosight.model; public class StudentCard { private String id; private String num; private Student student; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getNum() { return num; } public void setNum(String num) { this.num = num; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } }Student.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"> <hibernate-mapping package="com.baosight.model"> <class name="Student" dynamic-update="true"> <id name="id" > <generator class="native"></generator> </id> <property name="name"></property> <property name="age"></property> </class> </hibernate-mapping>StudentCard.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"> <hibernate-mapping package="com.baosight.model"> <class name="StudentCard" dynamic-update="true"> <id name="id" > <generator class="native"></generator> </id> <property name="num"></property> <many-to-one name="student" column="studentId" unique="true"></many-to-one> </class> </hibernate-mapping>使用JUnit测试以及结果见1中的描述
3.OneToOne的外键双向映射
看看annotation版本
本例使用Husband和Wife为例,需要在Husband里面引用Wife,并在getWife上使用@OneToOne@JoinColumn(name="wifeId"),并在Wife里面引用Husband,并在getHusband上使用@OneToOne(mappedBy="wife"),需要注意的是,双向关联的映射一般需要在其中一个类中使用mappedBy="wife",表示关联关系由Husband中的wife来维护。
Husband类与1中配置相同,这里不再赘述。
Wife类
package com.baosight.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity public class Wife { private String id; private String name; private Husband husband; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(mappedBy="wife") public Husband getHusband() { return husband; } public void setHusband(Husband husband) { this.husband = husband; } }
4.OneToOne的外键双向映射
看看xml版本
使用Student和StudentCard,.,需要在StudentCard里面引用Student,并在StudentCard.hbm.xml使用<many-to-one name="student" column="studentId" unique="true"></many-to-one>,另外需要在Student里面引用StudentCard,并在Student.hbm.xml使用<one-to-one name="studentCard" property-ref="student"></one-to-one>,其中property-ref="student"表示关联关系由StudentCard的student来维持,同样地,在双向关联中这种配置是比较常见的。
StudentCard和StudentCard.hbm.xml的配置与2中完全相同,这里不再赘述。
Student类
package com.baosight.model; public class Student { private String id; private String name; private int age; private StudentCard studentCard; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public StudentCard getStudentCard() { return studentCard; } public void setStudentCard(StudentCard studentCard) { this.studentCard = studentCard; } }Student.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"> <hibernate-mapping package="com.baosight.model"> <class name="Student" dynamic-update="true"> <id name="id" > <generator class="native"></generator> </id> <property name="name"></property> <property name="age"></property> <one-to-one name="studentCard" property-ref="student"></one-to-one> </class> </hibernate-mapping>仍然使用JUnit进行测试(详见1),结果为:
5.OneToOne的主键单向映射
先看annotation版本
本例使用Husband和Wife为例,需要在Husband里面引用Wife,并在getWife上使用@OneToOne@PrimaryKeyJoinColumn
Husband类
package com.baosight.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; @Entity public class Husband { private String id; private String name; private Wife wife; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne @PrimaryKeyJoinColumn public Wife getWife() { return wife; } public void setWife(Wife wife) { this.wife = wife; } }Wife类
package com.baosight.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Wife { private String id; private String name; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }仍然使用1中的JUnit进行测试,结果为:
6.OneToOne的主键单向映射
看看xml版本
使用Student和StudentCard,.,需要在StudentCard里面引用Student,并在StudentCard.hbm.xml使用<id name="id" >
<generator class="foreign">
<param name="property">student</param>
</generator>
</id>
和<one-to-one name="student" constrained="true"></one-to-one>,表示主键生成参考Student类色主键
StudentCard、Student和Student.hbm.xml的配置与2中完全相同,不再赘述
StudentCard.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"> <hibernate-mapping package="com.baosight.model"> <class name="StudentCard" dynamic-update="true"> <id name="id" > <generator class="foreign"> <param name="property">student</param> </generator> </id> <property name="num"></property> <one-to-one name="student" constrained="true"></one-to-one> </class> </hibernate-mapping>测试方法与5中相同,不再赘述,测试结果见5中
7.OneToOne的主键双向映射
看看annotation版本
本例使用Husband和Wife为例,需要在Husband里面引用Wife,并在getWife上使用@OneToOne@PrimaryKeyJoinColumn,并在Wife里面引用Husband,并在getHusband上使用@OneToOne@PrimaryKeyJoinColumn。
Husband类
package com.baosight.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; @Entity public class Husband { private String id; private String name; private Wife wife; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne @PrimaryKeyJoinColumn public Wife getWife() { return wife; } public void setWife(Wife wife) { this.wife = wife; } }Wife类
package com.baosight.model; import javax.persistence.Column; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.SequenceGenerator; import javax.persistence.TableGenerator; @Entity public class Wife { private String id; private String name; private Husband husband; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne @PrimaryKeyJoinColumn public Husband getHusband() { return husband; } public void setHusband(Husband husband) { this.husband = husband; } }JUnit测试同上,结果为:
8.OneToOne的主键双向映射
看看xml版本
使用Student和StudentCard,.,需要在StudentCard里面引用Student,并在StudentCard.hbm.xml使用
<generator class="foreign">
<param name="property">student</param>
</generator>
</id>
和<one-to-one name="student" constrained="true"></one-to-one>,表示主键生成参考Student类色主键
,另外需要在Student里面引用StudentCard,并在Student.hbm.xml使用<one-to-one name="studentCard" property-ref="student"></one-to-one>,其中property-ref="student"表示关联关系由StudentCard的student来维持,同样地,在双向关联中这种配置是比较常见的。StudentCard和StudentCard.hbm.xml的配置与6中完全相同,这里不再赘述。
Student类
package com.baosight.model; public class Student { private String id; private String name; private int age; private StudentCard studentCard; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public StudentCard getStudentCard() { return studentCard; } public void setStudentCard(StudentCard studentCard) { this.studentCard = studentCard; } }Student.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"> <hibernate-mapping package="com.baosight.model"> <class name="Student" dynamic-update="true"> <id name="id" > <generator class="native"></generator> </id> <property name="name"></property> <property name="age"></property> <one-to-one name="studentCard" property-ref="student"></one-to-one> </class> </hibernate-mapping>测试结果见7中
9.OneToOne的使用联合主键的类的外键映射
看看annotation版本
本例使用Husband和Wife为例,在Wife使用联合主键,创建联合住建磊WifePK,其需要implements Serializable和重写equals和hashCode方法,Wife中使用@IdClass(WifePK.class)和@Id。需要在Husband里面引用Wife,并在getWife上使用@OneToOne
@JoinColumns(
{
@JoinColumn(name="wifeId",referencedColumnName="id"),
@JoinColumn(name="wifeName",referencedColumnName="name")
}
)
WifePK类
package com.baosight.model; import java.io.Serializable; import javax.persistence.Embeddable; /** * 联合主键类 * <p>Title:WifePK </p> * <p>Description:TODO </p> * <p>Company: </p> * @author yuan * @date 2016-4-19 下午8:25:56 */ //@Embeddable public class WifePK implements Serializable{ private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub if(obj instanceof StudentPK){ StudentPK pk = (StudentPK)obj; if(this.id.equals(pk.getId())&&this.name.equals(pk.getName())){ return true; } } return false; } @Override public int hashCode() { // TODO Auto-generated method stub return this.id.hashCode(); } }Wife类
package com.baosight.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.IdClass; @Entity @IdClass(WifePK.class) public class Wife { private String id; private String name; private String age; @Id @GeneratedValue//auto public String getId() { return id; } public void setId(String id) { this.id = id; } @Id public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
10.OneToOne的使用联合主键的类的外键映射
看看xml版本
使用Student和StudentCard,.,在Student使用联合主键,创建联合住建磊StudentPK,其需要implements Serializable和重写equals和hashCode方法,需要在Student中引用StudentPK,并在Student.hbm.xml中使用<composite-id name="pk" class="StudentPK">
<key-property name="id" column="stu_id"></key-property>
<key-property name="name" column="stu_name"></key-property>
</composite-id>,不要再使用id了,需要注意。
需要在StudentCard里面引用Student,并在StudentCard.hbm.xml使用
<many-to-one name="student" class="Student" insert="false" update="false">
<column name="stu_id"></column>
<column name="stu_name"></column>
</many-to-one>
StudentPK
package com.baosight.model; import java.io.Serializable; /** * <p>Title:StudentPK </p> * <p>Description:TODO </p> * <p>Company: </p> * @author yuan * @date 2016-4-15 下午8:08:16*/ public class StudentPK implements Serializable{ private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub if(obj instanceof StudentPK){ StudentPK pk = (StudentPK)obj; if(this.id.equals(pk.getId())&&this.name.equals(pk.getName())){ return true; } } return false; } @Override public int hashCode() { // TODO Auto-generated method stub return this.id.hashCode(); } }Student
package com.baosight.model; import javax.persistence.Column; import javax.persistence.OneToOne; public class Student { // private String id; // private String name; private int age; private StudentPK pk; /* public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }*/ public int getAge() { return age; } public void setAge(int age) { this.age = age; } public StudentPK getPk() { return pk; } public void setPk(StudentPK pk) { this.pk = pk; } }StudentCard
package com.baosight.model; public class StudentCard { private String id; private String num; private Student student; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getNum() { return num; } public void setNum(String num) { this.num = num; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } }Student.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"> <hibernate-mapping package="com.baosight.model"> <class name="Student" dynamic-update="true"> <!-- <id name="id" > <generator class="native"></generator> </id> --> <composite-id name="pk" class="StudentPK"> <key-property name="id" column="stu_id"></key-property> <key-property name="name" column="stu_name"></key-property> </composite-id> <!-- <property name="name"></property> --> <property name="age"></property> </class> </hibernate-mapping>StudentCard.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"> <hibernate-mapping package="com.baosight.model"> <class name="StudentCard" dynamic-update="true"> <id name="id" > <generator class="native"></generator> </id> <property name="num"></property> <many-to-one name="student" class="Student" insert="false" update="false"> <column name="stu_id"></column> <column name="stu_name"></column> </many-to-one> </class> </hibernate-mapping>使用上面的JUnit进行测试,结果为:
以上即为OneToOne映射的内容,其实在实际使用中OneToOne用的不多,不过学习OneToOne对于学习后面的ManyToOne和ManyToMany有帮助,可以看作是它们的特殊情形。总之,上述各种映射关系需要在实际的使用中仔细体会。