Hibernte的多对多映射(十二)

上一章简单介绍了Hibernate一对多关联的两个关键属性cascade和inverse(十一),如果没有看过,请观看上一章

一. 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表是这样的:
在这里插入图片描述
角色在前,用户在后。 不太习惯。 原因就是在引入的时候的顺序。 现在是将Role.hbm.xml放在前面,所以创建表和修改外键时,role均在前面。 换一下位置即可.
在这里插入图片描述
注意:在引入资源时,要注意顺序。

四.测试方法

四.一 插入方法

保存的时候,设置一下级联的保存。以用户为主。故User.hbm.xml中:
在这里插入图片描述
具体的测试代码是:

@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();
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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());
				}
			}
		}
		
	}

在这里插入图片描述
在这里插入图片描述
其中插入的顺序,即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();
	}

最后执行的结果是:
在这里插入图片描述

四.四 删除的测试

在用户User.hbm.xml中设置级联cascade=“delete” .查看此时的级联删除:
在这里插入图片描述

代码为:

@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引入资源

在这里插入图片描述

五.八 测试创建方法

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

运行创建的方法,达到正常的效果.
在这里插入图片描述

五.九 插入测试

@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();
	}

运行之后是:
在这里插入图片描述

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

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

两个蝴蝶飞

你的鼓励,是老蝴蝶更努力写作的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值