SpringDataJpa(六) 多表操作-多对多
1.实例分析 用户:角色
用户:角色===> n : n
一个用户可以有多个角色,一个角色可以赋予多个用户
eg: 用户:李四 既是教师又是java工程师,张三也是教师
- 李四有教师,java工程师两个角色
- 教师这个角色同时赋予了,张三和李四
用户和角色是多对多的关系
2.表关系建立
3.实体类与数据库表关系映射
用户表: sys_user
角色表: sys_role
中间表:sys_user_role
User.java
用户实体类
/**
* 用户实体类
*/
Entity
Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Long userId;
@Column(name = "user_name")
private String userName;
@Column(name = "age")
private Integer age;
/**
* 配置多对多映射
* 配置中间表
*/
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
@JoinTable(name = "sys_user_role", //中间表名称
// 当前对象在中间表中的外键
joinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")},
// 对方对象在中间表中的外键
inverseJoinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "role_id")}
)
private Set<Role> roles = new HashSet<Role>();
}
Role.java 角色实体类
/**
* 角色实体类
*/
@Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
/**
* 创建外键关联
*/
/* @ManyToMany(targetEntity = User.class)
@JoinTable(name = "sys_user_role", //中间表名称
// 当前对象在中间表中的外键
joinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "role_id")},
// 对方对象在中间表中的外键
inverseJoinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")}
)*/
@ManyToMany(mappedBy = "roles") //放弃外键维护
private Set<User> users = new HashSet<User>();
}
UserDao 用户持久层
/**
* 用户持久层
*/
public interface UserDao extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
RoleDao 角色持久层
/**
* 角色持久层
*/
public interface RoleDao extends JpaRepository<Role, Long>, JpaSpecificationExecutor<Role> {
}
4.映射注解说明
@ManyToMany
- 作用:用于映射多对多关系
- 属性:
- mapperBy:放弃外键维护权力,由关联表1维护外键
- cascade:配置级联操作。
- fetch:配置是否采用延迟加载。
- targetEntity:配置目标的实体类
@JoinTable
- 作用:中间表配置
- 属性:
- name:中间表名称
- joinColumns:
- name:当前实体类在中间表中对应的外键字段
- referenceColumnName:当前实体类与数据库表对应的主键
- inverserJoinColumn:
- name:对方实体类在中间表中对应的外键字段
- referenceColumnName:对方实体类与数据库表对应的主键
@JoinColumn
- 作用:用于定义主键字段和外键字段的对应关系。
- 属性:
- name:指定外键字段的名称
- referencedColumnName:指定引用主表的主键字段名称
- unique:是否唯一。默认值不唯一
- nullable:是否允许为空。默认值允许。
- insertable:是否允许插入。默认值允许。
- updatable:是否允许更新。默认值允许。
- columnDefinition:列的定义信息。
5.测试
保存
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:applicationContext.xml")
public class ManyToManyTest {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
@Test
@Transactional
@Rollback(false)
public void test_save(){
User user = new User();
user.setUserName("张三");
Role role = new Role();
role.setRoleName("ADMIN");
//建立联系
user.getRoles().add(role);
role.getUsers().add(user);
userDao.save(user);
}
}
问题:
多对多保存时,如果设置双向关系
,意味着双方都有外键维护权,保存时都会向中间表插入数据,会造成同一条记录插入两次,主键重复
,会报错。
解决:让被动的一方放弃外键维护权力
Role.java
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<User>();
删除
cascade:配置级联操作
- CascadeType.MERGE 级联更新
- CascadeType.PERSIST 级联保存
- CascadeType.REFRESH 级联刷新
- CascadeType.REMOVE 级联删除
- CascadeType.ALL 级联所有操作
User.java
/***
* CascadeType.ALL 级联所有操作
*/
@ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
private Set<Role> roles = new HashSet<Role>();
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath*:applicationContext.xml")
public class ManyToManyTest {
/**
* 级联删除--- 只能单项关系级联
* 删除用户级联删除角色
* 双向级联删除根本不能配置
* 禁用
* 如果配了的话,如果数据之间有相互引用关系,可能会清空所有数据
*/
@Test
public void test_del(){
User user = userDao.findOne(1l);
userDao.delete(user);
}
}