读书人

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

发布时间: 2013-01-19 11:41:36 作者: rapoo

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

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

假设有角色(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:

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

<?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:

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

<?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用来建立SessionSessionFactory 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增加group4role1.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();}// 关闭sessionFactorysessionFactory.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查询语句,故此处不用纠结。

数据库:

添加模块:

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

添加、修改模块:(修改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 (?, ?)

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

添加、删除模块:(删除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=?

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

添加、反向添加模块:(反向添加数据,只增加了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 (?)

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


读书人网 >编程

热点推荐