【Hibernate步步为营】--多对多映射详解

上篇文章详细讨论了一对多映射,在一对多映射中单向的关联映射会有很多问题,所以不建议使用如果非要采用一对多的映射的话可以考虑使用双向关联来优化之间的关系,一对多的映射其实质上是在一的一端使用<many-to-one>标签来标明它们之间的关系,另外还需要在一的一端的对象中使用set标明集合映射。

一、单向多对多

仍然按照前几篇的文章格式来讨论,首先来看对象之间的关系,单向的多对多关系是两个对象之间发生的,比如在人和职位之间,一个人可以有多个职位,而且一个职位也可以由多人来负责,所以它们之间就形成了多对多的关系,另外这种单向性是指只能在一端来查询获取另一端的内容。另外因为是多对多之间的关系,所以在生成关系模型时会生成对象之间的关联表,实际它们之间的关系的是关联表,具体的对象模型如下:

上面已经说过多对多的关系会生成一个关联表,在关联表中来维护之间的关系,所以对应的关系模型中会有一个关系表,这个关系表中存放着两个关系表的主键,并且关系表的主键是另外两张表的主键的组合,如下图:

1.1、映射

上面的关系模型中会生成一个关系表,所以在映射中要编写对应的属性,因为是单向的关联关系所以主要的映射关系是在映射的原方向添加的,对应的上面的关系模型上就是在T_user中添加多对多映射的关系。

1.1.1 User.hbm.xml

文件中要使用<many-to-many>标签,并且在标签中添加上对应的列关系,因为你要让两个对象中都要清楚它们之间的映射是如何使用的,并且在生成的关系表中哪一列是对应的自己的外键,所以要在该标签中指明,另外在<set>标签中添加table属性会指明要生成新表,下面的示例中添加了t_user_role,所以会生成新的关联表。

<?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>
	<class name="com.src.hibernate.User" table="t_user">
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="name"/>
		<set name="roles" table="t_user_role">
			<key column="user_id"></key>
			<many-to-many class="com.src.hibernate.Role" column="role_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

1.1.2 Role.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>
	<class name="com.src.hibernate.Role" table="t_role">
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="name"/>
	</class>
</hibernate-mapping>

1.2、类文件

类文件中代码的编写要和映射文件中配置的相同,它们之间是相互对应的,在user中因为使用了<set>映射,所以在相应的类文件中也要添加Haseset来标明之间的映射关系。

1.2.1 User.java

类代码没有什么好讨论的了,里面的内容和前几篇文章的大致相同,除了基本的属性和方法外还需要添加对应的HashSet。

package com.src.hibernate;
import java.util.Set;

public class User {
	//ID号
	private int id;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}

	//名称
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	//角色集合
	private Set roles;
	public Set getRoles() {
		return roles;
	}
	public void setRoles(Set roles) {
		this.roles = roles;
	}
}

1.2.2 Role.java

基本的属性和方法,它的页面代码是非常简单基础的,不需要添加任何复杂的内容。

package com.src.hibernate;

public class Role {
	//id标示
	private int id;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}

	//名称
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

}

生成的表结构如下:

1.3、操作

1.3.1 插入操作

演示插入操作,新创建表后向表中写入数据,对应着关系模型,在关系模型中最复杂的是关联表部分,需要添加多个对应角色,并把角色分配到对应的表中,所以首先要创建关系并把关系保存到数据库中,然后创建用户Hash表,在Hash表中添加对应的关系,最后创建用户,然后将Hash表添加到用户上,代码如下:

public void testSave(){
	Session session=null;
	try{
		//创建session对象
		session=HibernateUtils.getSession();
		//开启事务
		session.beginTransaction();

		//创建角色1
		Role r1=new Role();
		r1.setName("Doctor");
		session.save(r1);

		//创建角色2
		Role r2=new Role();
		r2.setName("Teacher");
		session.save(r2);

		//创建角色3
		Role r3=new Role();
		r3.setName("Farmer");
		session.save(r3);

		//创建角色4
		Role r4=new Role();
		r4.setName("Woman");
		session.save(r4);

		//创建角色5
		Role r5=new Role();
		r5.setName("Father");
		session.save(r5);

		//创建用户1,并设置用户角色
		User user1=new User();
		user1.setName("Anne");
		Set roles1=new HashSet();
		roles1.add(r1);
		roles1.add(r5);
		user1.setRoles(roles1);
		session.save(user1);

		//创建用户2,并设置用户角色
		User user2=new User();
		user2.setName("Jack");
		Set roles2=new HashSet();
		roles2.add(r2);
		roles2.add(r4);
		user2.setRoles(roles2);
		session.save(user2);

		//创建用户3,并设置用户角色
		User user3=new User();
		user3.setName("Baby");
		Set roles3=new HashSet();
		roles3.add(r3);
		roles3.add(r2);
		user3.setRoles(roles3);
		session.save(user3);

		session.getTransaction().commit();
	}catch(Exception e){
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally{
		HibernateUtils.closeSession(session);
	}
}

执行上面的测试方法,将结构写入表:

对比上表,一个完整的写入测试方法编写完成,将数据写入到关系中其实相当的简单,主要是在写入时弄清楚写入的先后顺序,否则会出现很多null值,另外需要注意的是Hash表部分,首先需要添加对应的Hash表的内容,最后将Hash表写入到数据库中。

1.3.2 读取操作

读取操作相对于写入来说就很简单了,因为是单向的关系,所以在读取时只能通过一端来读取另一端的内容,也就是说通过User对象来读取Role的内容,如下代码:

public void testLoad1(){
	Session session=null;
	try{
		session=HibernateUtils.getSession();
		session.beginTransaction();

		User user=(User)session.load(User.class, 1);
		Set users=user.getRoles();
		for(Iterator iter=users.iterator();iter.hasNext();){
			Role role=(Role)iter.next();
			System.out.println("User.name= "+user.getName()+" and Role.name= "+role.getName());
		}

		session.getTransaction().commit();
	}catch(Exception e){
		e.printStackTrace();
		session.getTransaction().rollback();
	}finally{
		HibernateUtils.closeSession(session);
	}
}

执行测试方法,打印生成的内容如下:

Hibernate: select user0_.id as id0_0_, user0_.name as name0_0_ from t_user user0_ where user0_.id=?
Hibernate: select roles0_.user_id as user1_1_, roles0_.role_id as role2_1_, role1_.id as id2_0_, role1_.name as name2_0_ from t_user_role roles0_ left outer join t_role role1_ on roles0_.role_id=role1_.id where roles0_.user_id=?
User.name= Anne and Role.name= Father
User.name= Anne and Role.name= Doctor

二、双向多对多

双向的多对多映射可以看做是单向的一种扩展,它其实是为了设置在两端同时维护关系,从任何一端都能够加载到另一端的内容,在实现上和单向的起始端是相同的都要使用<many-to-many>标签。

同样以上面的User和Role来做示例,上面的示例中使用了单向的多对多,不同的是这里要使用双向关系,所以要在Role的一端添加同样的映射关系,并在相应的对象中添加集合映射,其中对应的User内的代码不会发生改变。

2.1 Role.hbm.xml

因为是双向的多对多所以要在对象的两端同时加入双向的集合映射,也就是在配置文件中添加<set>标签,并在标签中添加<many-to-many>标签,具体的配置方法类似于上文的User.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>
	<class name="com.src.hibernate.Role" table="t_role">
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="name"/>

		<!-- 添加集合映射,映射的表名应该同User.hbm.xml中配置的表名相同 -->
		<set name="users" table="t_user_role">
			<key column="role_id"/><!-- 添加映射的外键 -->
			<!-- 添加多对多的关系 -->
			<many-to-many class="com.src.hibernate.User" column="user_id"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

2.2 Role.java

同单向的多对多关系中的文件相同,不过需要在对象中添加集合映射Set,使用set来标明映射的集合,如下代码:

package com.src.hibernate;

import java.util.Set;

public class Role {
	//id标示
	private int id;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}

	//名称
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	//用户集合
	private Set users;
	public Set getUsers() {
		return users;
	}
	public void setUsers(Set users) {
		this.users = users;
	}

}

双向关联映射是在单向的关联映射基础上配置而来的,只需要在映射文件的两端同时配置<many-to-many>即可,也就是说User.hbm.xml和User.java代码和上文中的代码相同,不发生变化,所以不再重复添加了。

结语

完整的单向多对多讨论完整,需要注意的主要是user.hbm.xml中配置的方法,需要使用<many-to-many>标签并且需要生成关系表来维护多对多的关系,其它的内容都是很简单的。

【Hibernate步步为营】--多对多映射详解

时间: 2024-10-14 18:53:42

【Hibernate步步为营】--多对多映射详解的相关文章

【Hibernate步步为营】--继承映射详解

上篇文章讨论了多对多映射,在使用多对多映射时重点是使用<many-to-many>标签,并在标签的两端加入外键这样在生成关系时会创建两个关系之间的关系表,通过关系表来维护它们之间的关系,另外对于单向和双向的区别是在映射的哪一端添加标签的问题.在面向对象中很重要的一个特性就是继承,继承实现了代码的复用,而且Hibernate把基本上所有的对象模型进行了映射封装,其中就包括继承映射,接下来就详细讨论. 一.继承映射 继承是面向对象很重要的特性,它实现了代码的服用,在关系模型中同样也有继承关系,这种

【Hibernate步步为营】--组合映射详解

上篇文章详细讨论了复合主键的映射原理,对于复合主键映射需要使用<composite-id>标签来标明映射的类属性,并在该标签中添加<key-property>子标签,并且主键列需要实现序列化接口,使用很简单只要学会如何进行映射就可以实现复合映射.接下来讨论组合对象映射,组合映射关系其实是把两个对象的公共部分抽象出来形成一个对象,两个子对象会包含另一个主对像,在配置映射文件时需要使用<component>标签来标明映射关系. 一.组合映射 组合是关联关系的一种特殊情况,是

【Hibernate步步为营】--锁机制详解

上篇文章详细讨论了hql的各种查询方法,在讨论过程中写了代码示例,hql的查询方法类似于sql,查询的方法比较简单,有sql基础的开发人员在使用hql时就会变得相当的简单.Hibernate在操作数据库的同时也提供了对数据库操作的限制方法,这种方法被称为锁机制,Hibernate提供的锁分为两种一种是乐观锁,另外一种是悲观锁.通过使用锁能够控制数据库的并发性操作,限制用户对数据库的并发性的操作. 一.锁简介 锁能控制数据库的并发操作,通过使用锁来控制数据库的并发操作,Hibernate提供了两种

mybatis懒加载特性详解,以及多对多映射详解

注意讲解都在代码中 准备数据库,测试数据,各位自己添加,因为是多对多,所以使用中间表: 用到的实体: 学生类: public class Student { private Integer sid; private String name; private Integer age; //一个学生有多个老师,一个老师有多个学生 private List<Teacher> teachers=new ArrayList<Teacher>(); setter.. getter.... to

【Hibernate步步为营】--双向关联一对一映射详解(二)

很不好意思,有两天时间没有更新博客文章了,不写文章的日子还真是感觉很空洞啊,养成了写文章的恶习想改也改不掉啊.说点题外话,前两天收到一位朋友的私信,邀请笔者写一篇有关OWS的文章,用来研究图标工具的一种技术,很荣幸收到这位朋友的邀请,但是因为这几天开发的项目着急上线所以暂时没有时间去研究,只能等这周末了,利用周末的时间来研究然后更新类似的技术文章. 回到文章的正题,上篇文章讨论了双向主键关联,它其实是一对一主键关联的一种特殊情况,想要实现双向的关联就必须在映射文件的两端同时配置<one-to-o

【Hibernate步步为营】--双向关联一对一映射详解(一)

摘要:本文以我自己设计植物大战僵尸碰撞检测算法为例进行分析,浅谈游戏开发中碰撞检测算法.本文所提及的碰撞算法与数据结构也可应用于跑酷等游戏中. 关键优化技术1:只检测逻辑上可能存在碰撞关系的物体.比如植物大战僵尸中,植物发射的子弹只能打击其所在行的僵尸,因此子弹只需遍历检测本行的僵尸,而不是存在场景中的全部僵尸.对于地铁跑酷,奔牛跑酷,吃金币的碰撞检测算法亦是如此. 关键优化技术2:子弹检测碰撞僵尸,或是僵尸检测碰撞子弹,谁检测谁碰撞谁的问题,这个可能跟具体项目息息相关.可以从算法效率,以及设计

【Hibernate步步为营】--复合主键映射详解

本题是最基本的分段树操作了.或者一般叫线段树,不过好像和线段没什么关系,只是分段了. 不使用lazy标志,更新只是更新单点. 如果不使用分段树,那么更新时间效率只需要O(1),使用分段树更新效率就需要O(lgn)了. 但是不是用分段树,那么查询的时间效率是O(n),而分段树查询效率是O(lgn) 这就是amortize分摊了时间,而且lgn真的很快,数据不是非常巨大的时候,接近常数了. 故此本题需要使用分段树. #include <cstdio> class EnemyInfo { stati

hibernate enum映射详解

hibernate enum映射详解 在这里介绍注解的形式,如果想要了解XML配置的方式,可以自行查找相关资料. 例如以下Entity @Entity @Table(name = "t_user") public class User implements Serializable { /** * */ private static final long serialVersionUID = -9042615274714038279L; @Id @GeneratedValue(stra

【Hibernate步步为营】--关联映射之多对一

上篇文章讨论了Hibernate的基本映射,一个实体类对应着一张表,在相应的Hibernate Mapping文件中使用<class>标签映射.并且实体类中的普通属性对应着表字段,使用<property>标签映射.另外在构造实体类时应注意:在实体类中应实现无参的默认的构造函数,提供一个标示,建议不要使用final修饰实体类,为实体类生成getter和setter方法,最后介绍了几种主要的主键生成策略,接下来讨论多对一映射. 一.关联映射之多对一 对于多对一关联映射其实很容易理解,在