Hibernte的多对多映射(十二)

简介: Hibernte的多对多映射(十二)

一. Hibernate的多对多映射


Hibernate与正常的数据库一样,有一对一的映射,也有一对多的映射,自然也有多对多的映射。这一章主要讲多对多的映射。 在生活中,多对多的例子很常见,如商品和订单。一个订单上面可以有多个商品,同一件商品也可以卖多次,即有多个订单。 学生与课程表。当然,也有最经典的用户和角色。 常见的角色有: 超级管理员,机构管理员,系统管理员,一般用户等。 这里就用用户和角色来说明多对多的关系。


二.多对多关系需要创建第三个表。


多对多,与数据库一样,需要第三个表来进行相关联。 Hibernate形式的多对多形式,会自动生成第三张表。 这第三张表只保留第一张表的主键和第二张表的主键。 第三张表是复合主键。 其实,多对多的关系,就是两个一对多的关系。 如User和Role 实体类。 一个用户可以有多个角色,一个角色也属于多个用户。


表现在代码上就是用户User类中有一个Set集合,存储角色。 一个角色Role类中有一个Set集合,存储用户。下面,具体操作一下。


三.搭建多对多实例


三.一 User实体类


package com.yjl.pojo;
import java.util.HashSet;
import java.util.Set;
/**
 @author:两个蝴蝶飞
 @date: 2019年3月2日 下午6:18:43
 @Description 用户组
*/
public class User {
  /**
   * @param id 用户编号
   * @param name 用户名称
   * @param sex 用户的性别
   */
  private Integer id;
  private String name;
  private String sex;
  public User() {
  }
  public User(String name, String sex) {
    this.name = name;
    this.sex = sex;
  }
  /**
   * @param role 角色。 是一对多的关系
   */
  private Set<Role> roles=new HashSet<Role>();
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getSex() {
    return sex;
  }
  public void setSex(String sex) {
    this.sex = sex;
  }
  public Set<Role> getRoles() {
    return roles;
  }
  public void setRoles(Set<Role> roles) {
    this.roles = roles;
  }
}


三.二 Role 实体类


package com.yjl.pojo;
import java.util.HashSet;
import java.util.Set;
/**
 @author:两个蝴蝶飞
 @date: 2019年3月2日 下午6:18:51
 @Description 角色组
*/
public class Role {
  /**
   * @param id 角色编号
   * @param name 用户的名称
   */
  private Integer id;
  private String name;
  public Role() {
  }
  public Role(String name) {
    this.name = name;
  }
  /**
   * @param users 用户   是一对多的关系
   */
  private Set<User> users=new HashSet<User>();
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Set<User> getUsers() {
    return users;
  }
  public void setUsers(Set<User> users) {
    this.users = users;
  }
}


三.三 User.hbm.xml配置文件


<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入相应的约束 -->
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.yjl.pojo">
  <class name="User">
    <id name="id" column="id">
      <generator class="native"></generator>
    </id>
    <property name="name" column="name"></property>
    <property name="sex"></property>
    <!-- 对角色的一对多设置 . 引入一个table,第三张表。-->
    <set name="roles" table="user_role">
      <key column="userId"></key>
      <!-- 用的标签是多对多,many-to-many. -->
      <many-to-many column="roleId" class="Role"> </many-to-many>
    </set>
  </class>
</hibernate-mapping>


三.四 Role.hbm.xml配置文件


<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入相应的约束 -->
<!DOCTYPE hibernate-mapping PUBLIC 
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.yjl.pojo">
  <class name="Role">
    <id name="id" column="id">
      <generator class="native"></generator>
    </id>
    <property name="name" column="name"></property>
    <!-- 对用户的一对多 -->
    <set name="users" table="user_role">
      <key column="roleId"></key>
      <many-to-many column="userId" class="User"></many-to-many>
    </set>
  </class>
</hibernate-mapping>


两个配置文件,要保证table的表名称是一致的。 key的值和另外的一个表的column值要保持一致。


三.五 hibernate.cfg.xml文件中引入资源


    <!-- 引入相应的约束文件  ctrl点击时可以正确进入-->
    <mapping resource="com/yjl/pojo/Role.hbm.xml"/>
    <mapping resource="com/yjl/pojo/User.hbm.xml"/>


三.六 进行相关的测试


@Test
  public void createTest(){
    Session session=HibernateUtil.getSession();
    session.close(); //一定不要忘记关闭。
  }


测试运行,执行顺序是:


  1. 创建Role表。
  2. 创建User表。
  3. 创建user_role表。
  4. 修改user_role的复合主键是Role的外键
  5. 修改user_role的复合主键是Role的外键


其中发现,生成的user_role表是这样的:


20190302174126752.png


角色在前,用户在后。 不太习惯。 原因就是在引入的时候的顺序。 现在是将Role.hbm.xml放在前面,所以创建表和修改外键时,role均在前面。 换一下位置即可.


20190302205638441.png


注意:在引入资源时,要注意顺序。


四.测试方法


四.一 插入方法


保存的时候,设置一下级联的保存。以用户为主。故User.hbm.xml中:


2019030221005925.png


具体的测试代码是:


@Test
  public void saveTest(){
    Session session=HibernateUtil.getSession();
    //不要忘记开启事务
    Transaction transaction=session.beginTransaction();
    transaction.begin();
    User user1=new User("两个蝴蝶飞","男");
    User user2=new User("风流少爷","男");
    User user3=new User("精灵妹","女");
    /*2. 设置多个角色*/
    Role role1=new Role("超级管理员");
    Role role2=new Role("系统管理员");
    Role role3=new Role("一般用户");
    /*Role role1=session.get(Role.class,1);
    Role role2=session.get(Role.class,2);
    Role role3=session.get(Role.class,3);*/
    /*3.设置之间的关系  需要多个设置设置即可*/
    user1.getRoles().add(role1);
    user1.getRoles().add(role3);
    user2.getRoles().add(role2);
    user2.getRoles().add(role3);
    user3.getRoles().add(role3);
    /*4.进行保存*/
    session.save(user1);
    session.save(user2);
    session.save(user3);
    transaction.commit();
  }


20190302210602683.png


20190302210627461.png


20190302210548514.png


在代码中,我设置的是单向的关联。 即只用用户去找角色。 是成功的。 我又设置了一下双向的关联。 即:


user1.getRoles().add(role1);
    user1.getRoles().add(role3);
    user2.getRoles().add(role2);
    user2.getRoles().add(role3);
    user3.getRoles().add(role3);
    role1.getUsers().add(user1);
    role2.getUsers().add(user2);
    role3.getUsers().add(user1);
    role3.getUsers().add(user2);
    role3.getUsers().add(user3);


这个时候,运行出来,却是错误的。 报的是重复的键。 也就是说,这种关系是设置了两遍。 查了一下,原因是这样的: 现在inverse=false, 默认的形式。 用户是这样的,角色也是这样的。 外键关系由双方进行关联。 这是不对的. 有两种解决的方式:


1.就是像代码中说的那样,只是从用户的角度去设置。 并不是从角色的角度去设置。 当然,也可以从角色的角度去设置。


2.在User.hbm.xml 中设置 inverse=“true” .即用户放弃外键维护。 这个时候,代码中要设置两种角度。 用户的角度和角色的角度。 即第二种代码的形式。 当然,也可以Role.hbm.xml中设置inverse=“true”. 即角色放弃外键维护。


其中,如果两个都设置inverse=true, 这个时候,双方都不维护外键,那么此时,无论代码中如何设置,都不会向第三张表插入值。 要特别注意这一点。


四.二 查询方法的测试


@Test
  public void searchTest(){
    Session session=HibernateUtil.getSession();
    User user=session.get(User.class,1);
    System.out.println("用户的名称:"+user.getName());
    //1.根据用户去查它下面的角色。
    System.out.println("该用户所拥有的角色是:");
    Set<Role> roles=user.getRoles();
    for (Role role : roles) {
      System.out.println(role.getName()+",");
    }
    //2.找到其中的一个角色,然后根据这个角色去查一下用户。
    for (Role role : roles) {
      if(role.getId()==1){
        System.out.println("该角色的所属用户是:");
        Set<User> users=role.getUsers();
        for (User user2 : users) {
          System.out.println(user2.getName());
        }
      }
    }
  }


201903022125356.png


20190302212609581.png


20190302212717510.png


其中插入的顺序,即role表的值:


并不是我们代码中设置的顺序。 原因是因为Set是无序的。


四.三 修改的测试


@Test
  public void updateTest(){
    Session session=HibernateUtil.getSession();
    Transaction transaction=session.getTransaction();
    transaction.begin();
    User user=session.get(User.class,1);
    System.out.println("用户的名称:"+user.getName());
    Set<Role> roles=user.getRoles();
    Iterator<Role> iterator=roles.iterator();
    while(iterator.hasNext()){
      Role role=iterator.next();
      //1. 去除第一个角色是1的值,即去除普通用户这个角色。
      if(role.getId()==1){
        iterator.remove();
      }else if (role.getId()==2){  //是超级管理员的角色的话
        //将精灵妹放入到到超级管理员里面
        role.getUsers().add(session.get(User.class,3));
      }
    }
    //已经设置了是用户级联了。
    session.update(user);
    transaction.commit();
  }


最后执行的结果是:


20190302213653689.png


四.四 删除的测试


在用户User.hbm.xml中设置级联cascade=“delete” .查看此时的级联删除:


20190302213850806.png


代码为:


@Test
  public void deleteTest(){
    Session session=HibernateUtil.getSession();
    Transaction transaction=session.getTransaction();
    transaction.begin();
    User user=session.get(User.class,1);
    session.delete(user);
    transaction.commit();
  }


主要的执行顺序是:


1.根据员工编号去找那个员工的信息

2.将user表与user_role表进行关联,根据员工的编号查询出他所拥有的角色编号

3.从user_role 表中删除那个用户的信息

4.从user_role表中删除那个角色的信息

5.从role表中删除那个角色

6.从user表中删除那个用户。


本来是想删除那个用户,没有想到,把那个角色和在user_role表中那个角色所对应的记录给删除了。 这就是级联删除的危害。 所以,要慎用这一个级联 cascade=“delete” 。


注意一点: 并不会由这个角色即系统管理员去找那些人,然后删除那些人,删除之前找那些人所拥有的角色,去删除那些角色,删除角色之前,再找人。 并不会下去。 不然,很可能到最近,user,role,user_role表中都没有任何记录。 只是到删除用户角色表中关于那个角色的配置用户的记录而已。


五. 替换多对多


一般来说,上面那个多对多,并不太好。 因为第三张表,几乎只能有那两个字段。并不会添加其他字段之类的。 如,插入的操作人,插入的时间等。 一般来说,第三张表是单独设置的,然后分别引用第一张表和第二张表的外键而已。 即,创建一个实实在在的User_Role表和实体。 这个时候,用户与角色将不会存在直接的关系了。 全在User_Role实体中。这个实体与用户是多对一,这个实体与角色是多对一。


五.一 UserRole实体类


package com.yjl.pojo;
import java.io.Serializable;
import java.util.Date;
/**
 @author:两个蝴蝶飞
 @date: 2019年3月2日 下午9:52:31
 @Description 用户角色表,真实存在
*/
public class UserRole implements Serializable{
  private static final long serialVersionUID = -1109238867051166408L;
  /**
   * @param id 编号
   * @param user 与用户表是多对一的关系
   * @param role 与用户表是多对一的关系
   * @param operateDate 操作日期
   * @param operateUser 操作人
   */
  private Integer id;
  private User user;
  private Role role;
  private Date operateDate;
  private String operateUser;
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public User getUser() {
    return user;
  }
  public void setUser(User user) {
    this.user = user;
  }
  public Role getRole() {
    return role;
  }
  public void setRole(Role role) {
    this.role = role;
  }
  public Date getOperateDate() {
    return operateDate;
  }
  public void setOperateDate(Date operateDate) {
    this.operateDate = operateDate;
  }
  public String getOperateUser() {
    return operateUser;
  }
  public void setOperateUser(String operateUser) {
    this.operateUser = operateUser;
  }
}


五.二 User实体类


package com.yjl.pojo;
import java.util.HashSet;
import java.util.Set;
/**
 @author:两个蝴蝶飞
 @date: 2019年3月2日 下午6:18:43
 @Description 用户组
*/
public class User {
  /**
   * @param id 用户编号
   * @param name 用户名称
   * @param sex 用户的性别
   */
  private Integer id;
  private String name;
  private String sex;
  public User() {
  }
  public User(String name, String sex) {
    this.name = name;
    this.sex = sex;
  }
  /**
   * @param userRoles1 角色。 是一对多的关系
   */
  private Set<UserRole> userRoles1=new HashSet<UserRole>();
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getSex() {
    return sex;
  }
  public void setSex(String sex) {
    this.sex = sex;
  }
  public Set<UserRole> getUserRoles1() {
    return userRoles1;
  }
  public void setUserRoles1(Set<UserRole> userRoles1) {
    this.userRoles1 = userRoles1;
  }
}


五.三 Role实体类


package com.yjl.pojo;
import java.util.HashSet;
import java.util.Set;
/**
 @author:两个蝴蝶飞
 @date: 2019年3月2日 下午6:18:51
 @Description 角色组
*/
public class Role {
  /**
   * @param id 角色编号
   * @param name 用户的名称
   */
  private Integer id;
  private String name;
  public Role() {
  }
  public Role(String name) {
    this.name = name;
  }
  /**
   * @param userRoles 用户   是一对多的关系
   */
  private Set<UserRole> userRoles=new HashSet<UserRole>();
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Set<UserRole> getUserRoles() {
    return userRoles;
  }
  public void setUserRoles(Set<UserRole> userRoles) {
    this.userRoles = userRoles;
  }
}


五.四 User.hbm.xml文件


<hibernate-mapping>
  <class name="com.yjl.pojo.User">
    <id name="id" column="id">
      <generator class="native"></generator>
    </id>
    <property name="name" column="name"></property>
    <property name="sex"></property>
    <!-- 对角色的一对多设置 .-->
    <set name="userRoles1"  cascade="save-update" inverse="true">
      <key column="userId"></key>
      <!-- 用的标签是多对多,many-to-many. -->
      <one-to-many class="com.yjl.pojo.UserRole"/>
    </set>
  </class>
</hibernate-mapping>


五.五 Role.hbm.xml配置文件


<hibernate-mapping>
  <class name="com.yjl.pojo.Role">
    <id name="id" column="id">
      <generator class="native"></generator>
    </id>
    <property name="name" column="name"></property>
    <!-- 对用户的一对多 -->
    <!-- 对角色的一对多设置 .-->
    <set name="userRoles" cascade="save-update" inverse="true">
      <key column="roleId"></key>
      <!-- 用的标签是多对多,many-to-many. -->
      <one-to-many class="com.yjl.pojo.UserRole"/>
    </set>
  </class>
</hibernate-mapping>


五.六 UserRole.hbm.xml配置文件


<hibernate-mapping >
  <class name="com.yjl.pojo.UserRole">
    <id name="id">
      <generator class="native"></generator>
    </id>
    <many-to-one name="user" column="userId" class="com.yjl.pojo.User"></many-to-one>
    <many-to-one name="role" column="roleId" class="com.yjl.pojo.Role"></many-to-one>
    <property name="operateDate" column="operateDate" type="timestamp"></property>
    <property name="operateUser" column="operateUser" type="java.lang.String"></property>
  </class>
</hibernate-mapping>


五.七 hibernate.cfg.xml引入资源


20190302223823417.png


五.八 测试创建方法


@Test
  public void createTest(){
    Session session=HibernateUtil.getSession();
    session.close();
  }


运行创建的方法,达到正常的效果.


20190302223922797.png


五.九 插入测试


@Test
  public void saveTest(){
    Session session=HibernateUtil.getSession();
    Transaction transaction=session.beginTransaction();
    /*1.设置多个用户*/
    User user1=new User("两个蝴蝶飞","男");
    User user2=new User("风流少爷","男");
    User user3=new User("精灵妹","女");
    /*2. 设置多个角色*/
    Role role1=new Role("超级管理员");
    Role role2=new Role("系统管理员");
    Role role3=new Role("一般用户");
    /*3.进行设置关系. 以多的那一方进行设置了. 均是多的一方为UserRole.
    所以,要以UserRole 为主角*/
    UserRole ur1=new UserRole();
    ur1.setUser(user1);
    ur1.setRole(role1);
    ur1.setOperateDate(new Date());
    ur1.setOperateUser("110");
    /*上面就是设置了,第一条的记录。 接下来,设置第二条,第三条的记录。*/
    UserRole ur2=new UserRole();
    ur2.setUser(user1);
    ur2.setRole(role3);
    ur2.setOperateDate(new Date());
    ur2.setOperateUser("111");
    UserRole ur3=new UserRole();
    ur3.setUser(user2);
    ur3.setRole(role1);
    ur3.setOperateDate(new Date());
    ur3.setOperateUser("112");
    /*4.进行保存*/
    session.save(user1);
    session.save(user2);
    session.save(user3);
    session.save(role1);
    session.save(role2);
    session.save(role3);
    session.save(ur1); //必须要添加上。
    session.save(ur2);
    session.save(ur3);
    transaction.commit();
  }


运行之后是:


20190302224959772.png


此时,虽然设置了级联保存,但是userrole这张表并不是设置的唯一外键。 不但要用user表,还要用role表。 所以,级联是不好用的。 要单独进行save()方法进行插入数据。 有个疑问,要不要设置成复合主键? 将userRole类中的id去除掉? 后来,想想,还是算了。 添加一个标识id必须好一些。


谢谢!!!

相关文章
|
11月前
|
SQL XML 存储
Hibernate框架【五】——基本映射——多对多映射
Hibernate框架【五】——基本映射——多对多映射
139 0
|
1月前
|
机器学习/深度学习 SQL Java
JPA - 双向多对多映射
JPA - 双向多对多映射
37 2
|
6月前
|
SQL Java 数据库连接
MyBatis 的关联关系配置 一对多,一对一,多对多 关系的映射处理
MyBatis 的关联关系配置 一对多,一对一,多对多 关系的映射处理
|
9月前
|
SQL XML Java
【MyBatis】映射一对多和多对多关系配置
resultMap 允许我们定义复杂的映射规则,将结果集中的多个字段映射到一个对象中。因为我们是一对多的所以我们再编写vo类的时候,里面是使用list集合。建立我们给的sql文件,运行它用这里面的表,让我们更好的示例操作。在我们的配置文件里面配置我们需要的几个表,自动生成所需文件。当然我们要先建立这个包里面的类才能更好的下一步。在原本的基础的sql上我们增加一个一对多的sql。在上面我们已经写好了sql,我们生成对应的。在我们的里面添加一个sql的方法编写。在生成的接口类里面编写对应的接口方法。
|
11月前
|
SQL XML Java
Hibernate框架【四】——基本映射——多对一和一对多映射
Hibernate框架【四】——基本映射——多对一和一对多映射
115 0
|
11月前
|
uml
UML关系与代码的映射
UML关系与代码的映射
|
Java 数据库连接 网络安全
【SSH快速进阶】——Hibernate 多对一映射 和 一对多映射
上两篇文章说了一对一映射,这里说一下多对一 和 一对多的映射情况。
【SSH快速进阶】——Hibernate 多对一映射 和 一对多映射
举一个多对多关联的例子,并说明如何实现多对多关联映射
例如:商品和订单、学生和课程都是典型的多对多关系。可以在实体类上通过@ManyToMany注解配置多对多关联或者通过映射文件中的和标签配置多对多关联,但是实际项目开发中,很多时候都是将多对多关联映射转换成两个多对一关联映射来实现的。
1462 0
|
Java 数据库连接 数据库