Hibernate 实体关联关系映射
Hibernate的映射关系很多,也比较复杂,也很容易忘记。这个基本上占据了Hibernate学习的七成时间。熟悉这些映射模型,需要大量的实践才能掌握。下面是我对Hibernate关联关系映射的一个总结,放到blog上一是自己查看方便,二来也可以和更多Hibernate开发人员交流分享。希望各位多多留言哦:)。
本文主要参考夏昕翻译的“Hibernate参考文档 V3.12”,也在附件中给出了。
本系列实例的开发环境:
Windows XP Professional 简体中文版
MySQL 5.0.45
Hibernate 3.12
Java SDK 1.5.06
IntelliJ IDEA 5.12
系列实例中所用的Hibernate配置文件如下:
<?xml version='1.0' encoding='gb2312'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
" http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--指定连接数据库驱动-->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<!--指定连接数据库的url,hibernate连接的数据库名-->
<property name="connection.url">jdbc:mysql://localhost:3306/hbstudy</property>
<!--指定连接数据库的用户名-->
<property name="connection.username">root</property>
<!--指定连接数据库的用户密码-->
<property name="connection.password">leizhimin</property>
<!--指定连接池的大小-->
<property name="connection.pool_size">5</property>
<!--指定数据库的方言-->
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<!--根据需要自动创建数据库,测试环境用-->
<property name="hbm2ddl.auto">create</property>
<!--在控制台显示执行的SQL语句-->
<property name="show_sql">true</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!--映射文件列表-->
<!--单向关联-->
<mapping resource="com/lavasoft/dx/_n_1_fk/Addressn1fk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_n_1_fk/Personn1fk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_n_1_tab/Addressn1tab.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_n_1_tab/Personn1tab.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_1_fk/Address11fk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_1_fk/Person11fk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_1_tab/Address11tab.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_1_tab/Person11tab.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_1_pk/Address11pk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_1_pk/Person11pk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_n_fk/Address1nfk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_n_fk/Person1nfk.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_n_tab/Address1ntab.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_1_n_tab/Person1ntab.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_n_n/Addressnn.hbm.xml"/>
<mapping resource="com/lavasoft/dx/_n_n/Personnn.hbm.xml"/>
<!--双向关联-->
<mapping resource="com/lavasoft/sx/_1_n_fk/Address1nfk_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_n_fk/Person1nfk_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_n_tab/Address1ntab_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_n_tab/Person1ntab_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_n_n/Addressnn_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_n_n/Personnn_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_1_fk/Address11fk_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_1_fk/Person11fk_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_1_pk/Address11pk_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_1_pk/Person11pk_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_1_tab/Address11tab_sx.hbm.xml"/>
<mapping resource="com/lavasoft/sx/_1_1_tab/Person11tab_sx.hbm.xml"/>
</session-factory>
</hibernate-configuration>
系列实例中所用到Session工厂是:
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("初始化SessionFactory失败!" + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session getCurrentSession() throws HibernateException {
Session s = (Session) session.get();
//当原Session为空或已关闭时,打开一个新的Session
if (s == null || !s.isOpen()) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = (Session) session.get();
session.set(null);
if (s != null) {
s.close();
}
}
}
------------------------------------------------------------------------------------------------------
http://lavasoft.blog.51cto.com/62575/39398
实体关联关系映射的种类:
单向关联
一对一外键单向关联
一对一主键单向关联
一对一连接表单向关联
一对多外键单向关联
一对多连接表单向关联
多对一外键单向关联
多对一连接表单向关联
多对多单向关联
双向关联
一对一外键双向关联
一对一主键双向关联
一对一连接表双向关联
一对多外键双向关联
一对多连接表双向关联
多对多双向关联
------------------------------------------------------------------------------------------------------
自我学习笔记:
多对一关联映射(many to one):
我们以用户和组为例来讲述,因为一个用户只能属于一个组,而一个组可以有多个用户,故用户是多,组是一。但是用户和组分别属于不同的实体,用户要找到组,就需要每个用户持有组的一个引用。这样才能构成他们之间的关系。
关联映射的本质:
* 将关联关系映射到数据库,所谓的关联关系是对象模型在内存中的一个或多个引用
<many-to-one>会在多的一端加入一个外键,指向一的一端,这个外键是由<many-to-one>
中的column属性定义的,如果忽略了这个属性那么默认的外键与实体的属性一致
<many-to-one>标签的定义示例:
* <many-to-one name="group" column="groupid"/>
第一步:建立用户和组的实体类:如下
User实体:
public class User {
private int id;
private String name;
private Group group;//用户里持有组的引用
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 Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
}
Group 实体:
public class Group {
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(String name) {
this.name = name;
}
}
第二步建立映射文件:
首先建立Group实体的映射文件,如下:
<hibernate-mapping>
<class name="com.bjsxt.hibernate.Group" table="t_group">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
</class>
</hibernate-mapping>
接着建立User实体的映射文件,如下:
<hibernate-mapping>
<class name="com.bjsxt.hibernate.User" table="t_user">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<!--
<many-to-one name="group" column="groupid" cascade="all"/>//设置级联属性,设置为all表示对删除,保存和修改都起作用
-->
<many-to-one name="group" column="groupid"/>
</class>
</hibernate-mapping>
第三部建立hibernate.cfg.xml文件:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate_many2one</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">bjsxt</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="com/bjsxt/hibernate/User.hbm.xml"/>
<mapping resource="com/bjsxt/hibernate/Group.hbm.xml"/>
</session-factory>
</hibernate-configuration>
第四部写测试代码,以存储为例:
public class Many2OneTest extends TestCase {
public void testSave1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Group group = new Group();
group.setName("尚学堂");
User user1 = new User();
user1.setName("菜10");
user1.setGroup(group);
User user2 = new User();
user2.setName("容祖儿");
user2.setGroup(group);
//不能成功保存,抛出TransientObjectException异常
//因为Group为Tran
sient状态,oid没有分配值
//persistent状态的对象是不能引用transient状态的对象的
session.save(user1);
session.save(user2);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testSave2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Group group = new Group();
group.setName("尚学堂");
session.save(group);
User user1 = new User();
user1.setName("菜10");
user1.setGroup(group);
User user2 = new User();
user2.setName("容祖儿");
user2.setGroup(group);
//可以正确存储
session.save(user1);
session.save(user2);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testSave3() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Group group = new Group();
group.setName("尚学堂");
User user1 = new User();
user1.setName("菜10");
user1.setGroup(group);
User user2 = new User();
user2.setName("容祖儿");
user2.setGroup(group);
//不会抛出异常,因为采用了cascade属性,所以它会先保存Group
//采用cascade属性是解决TransientObjectException异常的一种手段
//cascade属性只对删除,保存和修改时起作用
session.save(user1);
session.save(user2);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
User user = (User)session.load(User.class, 3);
System.out.println("user.name=" + user.getName());
System.out.println("user.group.name=" + user.getGroup().getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}
一对一关联关系映射(one to one):
一对一主键关联映射:让两个实体对象的id保持相同,这样可以避免多余的字段被创建。
两个对象之间的关系是一对一的关系,如用户和身份证的ID(Person 和 IdCard)
实现一对一关联关系映射有两种策略:
第一种是:主键关联。即让俩个对象具有相同的主键值,以表明他们之间的一对一的对应关系。这样数据库表不会有额外的字段来维护他们之间的关系,仅通过表的主 键来关联。
第二种是:唯一外键来关联。外键关联,本来是用于多对一的配置,但是加上唯一的限制之后,也可以用于表示一对一的关联关系。
下面我们以人和身份证俩个实体为例,来练习一对一的关联关系映射,我们采用主键关联:
第一步,分别建立人和身份证的实体类:如下
IdCaid的实体关系类为:
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(String cardNo) {
this.cardNo = cardNo;
}
}
Person的实体对象关系类为:
public class Person {
private int id;
private String name;
private 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(IdCard idCard) {
this.idCard = idCard;
}
}
第二步建立映射文件:
首先建立IdCard实体的映射文件,如下:
<hibernate-mapping>
<class name="com.bjsxt.hibernate.IdCard" table="t_idcard">
<id name="id">
<generator class="native"/>
</id>
<property name="cardNo"/>
</class>
</hibernate-mapping>
Person实体的映射文件,如下:
<hibernate-mapping>
<class name="com.bjsxt.hibernate.Person" table="t_person">
<id name="id">
<generator class="foreign">//因为要采用主键关联,这个主键要关联IdCard的主键,故这不能设为自增的,他的主键应该来源于IdCard,故 //为foreign,表示关联于外部。
<param name="property">idCard</param>//这个指定外部关联来源于谁,其中的name属性是固定的,都是property。
</generator>
</id>
<property name="name"/>
<one-to-one name="idCard" constrained="true"/>//这个表示hibernate怎么加载他的关联引用对象。name就是定义的属性。constrained这个属性表示加入了外键约束。需要设置为true,在一对一关系关联映射时需要添加这个属性,不加的话外键约束就不加上
</class>
</hibernate-mapping>
第三部建立hibernate.cfg.xml文件:如下:
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate_one2one_pk_1</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">bjsxt</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.show_sql">true</property>
<mapping resource="com/bjsxt/hibernate/IdCard.hbm.xml"/>
<mapping resource="com/bjsxt/hibernate/Person.hbm.xml"/>
</session-factory>
</hibernate-configuration>
第四部写测试代码,以存储为例:
public class One2OneTest extends TestCase {
public void testSave1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
IdCard idCard = new IdCard();
idCard.setCardNo("88888888888888");
Person person = new Person();
person.setName("菜10");
person.setIdCard(idCard);
//不会出现TransientObjectException异常
//因为一对一主键关联映射中,默认了cascade属性
session.save(person);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Person person = (Person)session.load(Person.class, 1);
System.out.println("person.name=" + person.getName());
System.out.println("idCard.cardNo=" + person.getIdCard().getCardNo());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
IdCard idCard = (IdCard)session.load(IdCard.class, 1);
System.out.println("idcard.cardNo=" + idCard.getCardNo());
System.out.println("idcard.person.name=" + idCard.getPerson().getName());
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
}