Hibernate关系映射:单向多对多映射
2013-01-15 15:25
447 查看
假设有角色(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 (?)
相关文章推荐
- hibernate单向无连接表1—N关系映射
- Hibernate之对象关系映射01一对一单向关联
- 【hibernate框架】关系映射之一对一单向外键关联(XML实现)
- Hibernate关系映射(一)一对一单向外键关联@OneToOne Annotation方式
- Hibernate关系映射(六)多对多单向关联@ManyToMany Annotation方式
- Hibernate 关系映射(7) 基于外键关联的单向1:N
- Hibernate 映射关系 ---Many2Many 单向关联
- Hibernate之对象关系映射03一对一单向外键关联
- Hibernate关系映射(三)多对一单向关联@ManyToOne Annotation方式
- Hibernate中的多对一单向映射关系
- Hibernate关系映射级别注解(一对多单向外键关联、一对多(多对一)双向外键关联)
- Hibernate 关系映射(6) 基于中间表关联的单向1:N
- Hibernate 关系映射 ——单向一对一
- Hibernate教程04_关系映射之一对一单向外键关联
- Hibernate关系映射级别注解(多对多单向外键关联、多对多双向外键关联)
- Hibernate关系映射(1)_一对一单向外键关联
- Hibernate 关系映射(五) 基于主键关联的单向1:1
- hibernate映射关系之一对一单向主键关联
- Hibernate关系映射 一对一双向外键关联@OneToOne Annotation方式 双向关联和单向关联的区别
- hibernate单向N-N关系映射