上篇文章具体讨论了一对多映射,在一对多映射中单向的关联映射会有非常多问题,所以不建议使用假设非要採用一对多的映射的话能够考虑使用双向关联来优化之间的关系,一对多的映射事实上质上是在一的一端使用<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
由于是单向的关系,所以在该映射文件里就不须要加入多余的标签来维护关系了,它的内部代码也会非常easy,相应的映射代码例如以下:
<?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 插入操作
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); } }
1.3.2 读取操作
读取操作相对于写入来说就非常easy了。由于是单向的关系,所以在读取时仅仅能通过一端来读取还有一端的内容。也就是说通过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>标签而且须要生成关系表来维护多对多的关系。其他的内容都是非常easy的。