JPA(12)之多对多操作

JPA(12)之多对多操作

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

示例分析

我们采用的示例为用户和角色。
用户:指的是咱们班的每一个同学。
角色:指的是咱们班同学的身份信息。
比如A同学,它是我的学生,其中有个身份就是学生,还是家里的孩子,那么他还有个身份是子女。
同时B同学,它也具有学生和子女的身份。
那么任何一个同学都可能具有多个身份。同时学生这个身份可以被多个同学所具有。
所以我们说,用户和角色之间的关系是多对多。

实体类关系建立以及映射配置

一个用户可以具有多个角色,所以在用户实体类中应该包含多个角色的信息,代码如下:

package xyz.zze.study.springdatajpa.bean;

import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * 用户的数据模型
 */
@Entity
@Table(name="sys_user")
public class SysUser implements Serializable {
	
	@Id
	@GeneratedValue(strategy= GenerationType.IDENTITY)
	@Column(name="user_id")
	private Long userId;
	@Column(name="user_code")
	private String userCode;
	@Column(name="user_name")
	private String userName;
	@Column(name="user_password")
	private String userPassword;
	@Column(name="user_state")
	private String userState;
	
	//多对多关系映射
	@ManyToMany(mappedBy="users")
	private Set<SysRole> roles = new HashSet<SysRole>(0);
	
	public Long getUserId() {
		return userId;
	}
	public void setUserId(Long userId) {
		this.userId = userId;
	}
	public String getUserCode() {
		return userCode;
	}
	public void setUserCode(String userCode) {
		this.userCode = userCode;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getUserPassword() {
		return userPassword;
	}
	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}
	public String getUserState() {
		return userState;
	}
	public void setUserState(String userState) {
		this.userState = userState;
	}
	public Set<SysRole> getRoles() {
		return roles;
	}
	public void setRoles(Set<SysRole> roles) {
		this.roles = roles;
	}
	@Override
	public String toString() {
		return "SysUser [userId=" + userId + ", userCode=" + userCode + ", userName=" + userName + ", userPassword="
				+ userPassword + ", userState=" + userState + "]";
	}
}

一个角色可以赋予多个用户,所以在角色实体类中应该包含多个用户的信息,代码如下:

package xyz.zze.study.springdatajpa.bean;

import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

/**
 * 角色的数据模型
 */
@Entity
@Table(name="sys_role")
public class SysRole implements Serializable {
	
	@Id
	@GeneratedValue(strategy= GenerationType.IDENTITY)
	@Column(name="role_id")
	private Long roleId;
	@Column(name="role_name")
	private String roleName;
	@Column(name="role_memo")
	private String roleMemo;
	
	//多对多关系映射
	@ManyToMany
	@JoinTable(name="user_role_rel",//中间表的名称
			  //中间表user_role_rel字段关联sys_role表的主键字段role_id
			  joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")},
			  //中间表user_role_rel的字段关联sys_user表的主键user_id
			  inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")}
	)
	private Set<SysUser> users = new HashSet<SysUser>(0);
	
	
	public Long getRoleId() {
		return roleId;
	}
	public void setRoleId(Long roleId) {
		this.roleId = roleId;
	}
	public String getRoleName() {
		return roleName;
	}
	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}
	public String getRoleMemo() {
		return roleMemo;
	}
	public void setRoleMemo(String roleMemo) {
		this.roleMemo = roleMemo;
	}
	public Set<SysUser> getUsers() {
		return users;
	}
	public void setUsers(Set<SysUser> users) {
		this.users = users;
	}
	@Override
	public String toString() {
		return "SysRole [roleId=" + roleId + ", roleName=" + roleName + ", roleMemo=" + roleMemo + "]";
	}
}

映射的注解说明

@ManyToMany

作用:用于映射多对多关系。
属性:

  • cascade:配置级联操作;
  • fetch:配置是否采用延迟加载;
  • targetEntity:配置目标的实体类。映射多对多的时候不用写;

@JoinTable

作用:针对中间表的配置。
属性:

  • name:配置中间表的名称;
  • joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段;
  • inverseJoinColumn:中间表的外键字段关联对方表的主键字段;

@JoinColumn

作用:用于定义主键字段和外键字段的对应关系。
属性:

  • name:指定外键字段的名称;
  • referencedColumnName:指定引用主表的主键字段名称;
  • unique:是否唯一,默认值不唯一;
  • nullable:是否允许为空,默认值允许;
  • insertable:是否允许插入,默认值允许;
  • updatable:是否允许更新,默认值允许;
  • columnDefinition:列的定义信息;

多对多的操作

保存

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.zze.study.springdatajpa.bean.SysRole;
import xyz.zze.study.springdatajpa.bean.SysUser;
import xyz.zze.study.springdatajpa.dao.RoleDao;
import xyz.zze.study.springdatajpa.dao.UserDao;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class ManyToManyTest {
    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleDao roleDao;
    /**
     * 需求:
     * 	保存用户和角色
     * 要求:
     * 	创建2个用户和3个角色
     * 	让1号用户具有1号和2号角色(双向的)
     * 	让2号用户具有2号和3号角色(双向的)
     *  保存用户和角色
     * 问题:
     *  在保存时,会出现主键重复的错误,因为都是要往中间表中保存数据造成的。
     * 解决办法:
     * 	让任意一方放弃维护关联关系的权利
     */
    @Test
    @Transactional  //开启事务
    @Rollback(false)//设置为不回滚
    public void test1(){
        //创建对象
        SysUser u1 = new SysUser();
        u1.setUserName("用户1");
        SysRole r1 = new SysRole();
        r1.setRoleName("角色1");
        //建立关联关系
        u1.getRoles().add(r1);
        r1.getUsers().add(u1);
        //保存
        roleDao.save(r1);
        userDao.save(u1);
    }
}

在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,所以报错,主键重复,解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃,配置如下:

//放弃对中间表的维护权,解决保存中主键冲突的问题
@ManyToMany(mappedBy="roles")
private Set<SysUser> users = new HashSet<SysUser>(0);

删除

@Autowired
private UserDao userDao;
/**
 * 删除操作
 *  在多对多的删除时,双向级联删除根本不能配置
 * 禁用
 *  如果配了的话,如果数据之间有相互引用关系,可能会清空所有数据
 */
@Test
@Transactional
@Rollback(false)//设置为不回滚
public void testDelete() {
    userDao.delete(1l);
}

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/jpa12.html

Buy me a cup of coffee ☕.