您的位置:首页 > 其它

Hibernate关系映射:单向多对多映射

2013-01-15 15:25 381 查看


假设有角色(trole)和用户组(tgroup)两个表,是多对多的关系,即一个角色可以多个用户组拥有,一个用户组也可以拥有多个角色。这里需要增加一个角色-组的对应表tgroup_role,用来记录多对多的对应关系。

首先建立三个对应表格:

create table tgroup(
group_id int(11) not null auto_increment,
name varchar(16) not null default '',
primary key(group_id)
)engine=innodb default charset=gbk;
create table trole(
role_id int(11) not null auto_increment,
name varchar(16) not null default '',
primary key(role_id)
)engine=innodb default charset=gbk;
create table tgroup_role(
group_id int(11) not null,
role_id int(11) not null,
primary key(group_id)
)engine=innodb default charset=gbk;

建立角色的实体类Role.java:

package relation.unidirectional.manytomany;

import java.util.HashSet;
import java.util.Set;

public class Role {
	private int id;
	private String name;
	private Set groups = new HashSet();

	public Role() {}

	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 Set getGroups() {
		return groups;
	}

	public void setGroups(Set groups) {
		this.groups = groups;
	}
}

注解:控制方需要建立一个Set集合类型属性,用来保存多个不重复的Group对象。

Role是多对多的控制方,其映射文件Role.hbm.xml:



<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="relation.unidirectional.manytomany.Role" table="trole">
		<id name="id" column="role_id" unsaved-value="0">
			<generator class="native" />
		</id>
		<property name="name" type="string" />
		<!--配置set,指定中间表及其对应键-->
		<set name="groups" table="tgroup_role" cascade="save-update">
			<key column="role_id" />
			<!--单向多对多的主动方配置-->
			<many-to-many class="relation.unidirectional.manytomany.Group"
				column="group_id">
			</many-to-many>
		</set>
	</class>
</hibernate-mapping>

注解:在此映射文件中,配置set元素。set的name属性对应Role类的groups变量,table指明其对应的表;子标签key指定了集合中对象同Role主键关联的键值。

多对多关系通过many-to-many配置,many-to-many关联表映射为组合元素的集合(A mapping like this allows you to map extra columns of amany-to-many
association table to the composite element class.) 其属性 class(必须):关联类的名称,
column
(可选):这个元素的外键关键字段名;它也可以通过嵌套的
<column>
元素指定。要注意,毕竟是嵌套于<setname="groups"
table="tgroup_role"></set>中的标签,故其作用范围就限定在为实体类Role的Set集合类型属性groups关联服务。

这里的多对多是单向的,当反向添加Group对象时,对role并没有影响,这从本例可看出。

关于<set>标签中属性cascade="save-update",详见: Hibernate级联操作Cascade学之---save-update

关于<id>标签中属性unsaved-value="0",详见: unsaved-value的经典解释


Group是被控制方,Group.hbm.xml:



<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<!--多对多的被动一方配置-->
	<class name="relation.unidirectional.manytomany.Group" table="tgroup">
		<id name="id" column="group_id" unsaved-value="0">
			<generator class="native" />
		</id>
		<property name="name" type="string" />

	</class>
</hibernate-mapping>

将上面的两个映射文件加入到Hibernate配置文件中,建立一个测试类Test.java:

package relation.unidirectional.manytomany;

import java.util.Iterator;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class Test {
	public static void main(String[] args) {
		
		//创建对象
		Role role1 = new Role();
		role1.setName("Role1");
		Role role2 = new Role();
		role2.setName("Role2");
		Role role3 = new Role();
		role3.setName("Role3");
		Group group1 = new Group();
		group1.setName("group1");
		Group group2 = new Group();
		group2.setName("group2");
		Group group3 = new Group();
		group3.setName("group3");
		
		//相互赋值
		group1.getRoles().add(role1);
		group1.getRoles().add(role2);
		group2.getRoles().add(role2);
		group2.getRoles().add(role3);
		group3.getRoles().add(role1);
		group3.getRoles().add(role3);
		role1.getGroups().add(group1);
		role1.getGroups().add(group3);
		role2.getGroups().add(group1);
		role2.getGroups().add(group2);
		role3.getGroups().add(group2);
		role3.getGroups().add(group3);

		// 定义主键变量
		Integer pid;
		
		// Configuration管理Hibernate配置
		Configuration config = new Configuration().configure();
		// 根据Configuration建立 SessionFactory
		// SessionFactory用来建立Session
		SessionFactory sessionFactory = config.buildSessionFactory();
		
		// 正向添加数据
		Session session = sessionFactory.openSession();
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			pid=(Integer)session.save(role1);
			session.save(role2);
			session.save(role3);
			tx.commit();
		} catch (RuntimeException e) {
			if (tx != null)
				tx.rollback();
			throw e;
		} finally {
			session.close();
		}
		
		
		// 修改role1数据
		Group group4=new Group();
		group4.setName("group4");
		session = sessionFactory.openSession();
		tx = null;
		try {
			tx = session.beginTransaction();
			role1 = (Role)session.get(Role.class, pid);
			//role1增加group4
			role1.getGroups().add(group4);
			session.update(role1);
			tx.commit();
		} catch (RuntimeException e) {
			if (tx != null)
				tx.rollback();
			throw e;
		} finally {
			session.close();
		}
	
		// 查询数据
		session = sessionFactory.openSession();
		role1 = (Role)session.get(Role.class, pid);
		System.out.println("role name:" + role1.getName());
		System.out.println("groups:");
		Iterator iter = role1.getGroups().iterator();
		while (iter.hasNext()) {
			group1 = (Group) iter.next();
			System.out.println("group name:" +group1.getName());
		}
		session.close();
		
		
		// 删除role1数据
		session = sessionFactory.openSession();
		tx = null;
		try {
			tx = session.beginTransaction();
			role1 = (Role) session.get(Role.class, pid);
			session.delete(role1);
			tx.commit();
		} catch (RuntimeException e) {
			if (tx != null)
				tx.rollback();
			throw e;
		} finally {
			session.close();
		}
		
		// 反向添加数据,只增加了group数据,对role没有影响
		session = sessionFactory.openSession();
		tx = null;
		try {
			tx = session.beginTransaction();
			session.save(group1);
			session.save(group2);
			tx.commit();
		} catch (RuntimeException e) {
			if (tx != null)
				tx.rollback();
			throw e;
		} finally {
			session.close();
		}
		
		// 关闭sessionFactory
		sessionFactory.close();

	}
}

关于异常:Duplicate entry ‘1’ for key ‘PRIMARY’,即主键重复。

数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态,如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量,之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加解锁操作,这对性能产生了较大影响。

对于多对多连接表使用的异常:链接表的如果不是使用双主键,必须确定主键列不能重复,而Hibernate插入多对多时链接表的操作很可能会使主键重复,如本例有连接表tgroup_role连接表tgroup和trole,有两个字段group_id、role_id分别引用tgroup表和trole表,如果连接表将group_id设为主键(本例都没有设主键),当Hibernate插入group对象时,就会产生类似以上的异常,提示主键插入重复。故这时就有必要使用双主键了,以便使一列主键重复的同时,另一列主键可以作为辨别键,如将book_id、image_id都设为主键。当然对于本例都没有设置主键是正确的,或者两个都设置主键(复合主键)也正确,但只设置其中一个有主键运行时就会出现主键重复异常。

运行结果:

控制台:

23:14:56,080 DEBUG SQL:346 -insert into trole (name) values (?)
23:14:56,099 DEBUG SQL:346 -insert into tgroup (name) values (?)
23:14:56,100 DEBUG SQL:346 -insert into tgroup (name) values (?)
23:14:56,102 DEBUG SQL:346 -insert into trole (name) values (?)
23:14:56,105 DEBUG SQL:346 -insert into tgroup (name) values (?)
23:14:56,107 DEBUG SQL:346 -insert into trole (name) values (?)
23:14:56,114 DEBUG SQL:346 -insert into tgroup_role (role_id, group_id) values (?, ?)
23:14:56,115 DEBUG SQL:346 -insert into tgroup_role (role_id, group_id) values (?, ?)
23:14:56,116 DEBUG SQL:346 -insert into tgroup_role (role_id, group_id) values (?, ?)
23:14:56,116 DEBUG SQL:346 -insert into tgroup_role (role_id, group_id) values (?, ?)
23:14:56,116 DEBUG SQL:346 -insert into tgroup_role (role_id, group_id) values (?, ?)
23:14:56,117 DEBUG SQL:346 - insert intotgroup_role (role_id, group_id) values (?, ?)

注意:上面输出中tgroup_role(role_id,group_id)中”role_id”和”group_id”的顺序是反的,但这只是表示插入到对应的数据库表中去,并不表示严格的SQL查询语句,故此处不用纠结。

数据库:

添加模块:



添加、修改模块:(修改role1数据,role1增加group4)

控制台修改部分:

00:15:53,159 DEBUG SQL:346 -select role0_.role_id as role1_0_0_, role0_.name as name0_0_ from trole role0_where role0_.role_id=?
00:15:53,167 DEBUG SQL:346 -select groups0_.role_id as role1_1_, groups0_.group_id as group2_1_,group1_.group_id as group1_2_0_, group1_.name as name2_0_ from tgroup_rolegroups0_ left outer join tgroup group1_ on groups0_.group_id=group1_.group_idwhere
groups0_.role_id=?
00:15:53,170 DEBUG SQL:346 -insert into tgroup (name) values (?)
00:15:53,172 DEBUG SQL:346 - insert intotgroup_role (role_id, group_id) values (?, ?)



添加、删除模块:(删除role1数据)

控制台删除部分:

00:17:24,585 DEBUG SQL:346 -select role0_.role_id as role1_0_0_, role0_.name as name0_0_ from trole role0_where role0_.role_id=?
00:17:24,594 DEBUG SQL:346 -delete from tgroup_role where role_id=?
00:17:24,595 DEBUG SQL:346 - delete fromtrole where role_id=?



添加、反向添加模块:(反向添加数据,只增加了group数据,对role没有影响)

控制台反向添加部分:

00:19:37,423 DEBUG SQL:346 -insert into tgroup (name) values (?)
00:19:37,424 DEBUG SQL:346 - insert intotgroup (name) values (?)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: