前言
在使用jpa持久化框架中,提供了@ManyToMany注解,用于级联解决关系表之中的多对多关系
注解说明
@ManyToMany注解用于标记表与表之间的多对多关系,jpa帮维护外键关系,比如用户和角色,教师和学生之间的关系
使用说明
表结构说明
(1)建立一个用户User表
@Data
@Entity
@Table(name = "t_user")
public class User implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "userName")
private String userName;
@ManyToMany
@JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")})
private List<Role> roleList = new ArrayList<>();
}
(2)建立一个角色Role表
@Data
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String roleName;
@ManyToMany
@JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "role_id")}, inverseJoinColumns = {@JoinColumn(name = "user_id")})
private List<User> userList;
}
其中:@ManyToMany注解提供几个字段
targetEntity:(可选)指定关联目标的实体类
cascade: (可选)指定级联到关联目标的操作
fetch:(可选)关联是应该延迟加载还是必须马上加载。EAGER 策略表示必须马上获取关联的实体,LAZY 策略表示用到关联对象时才去加载。默认值为 javax.persistence.FetchType.LAZY
mappedBy:拥有关系的字段,单向关系不需要指定改属性;双向关系必须指定改属性的值
备注: mappedBy会和@JoinTable冲突,两者同时使用会报错
@JoinTable 属性
@JoinTable提供几个属性字段,
其中
name: 用于连接表名称
joinColumns: 连接表主表外键
inverseJoinColumns: 连接表副表外键
等等,建立完关系之后,jpa会自动维护表与表之间的外键关系
前提
已经建立好User和Role中的dao层
(1)用户dao层
@Repository
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
(2)角色dao层
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
}
controller写法
@Slf4j
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@GetMapping("/save")
public User save() {
User user = new User();
user.setUserName("aaa");
userRepository.save(user);
return user;
}
@GetMapping("/saveRole")
public Role saveRole() {
Role role = new Role();
role.setRoleName("aaa");
roleRepository.save(role);
return role;
}
@GetMapping("/realtive")
public void realtive(@RequestParam("userId") Long userId, @RequestParam("roleId") Long roleId) {
userRepository.findById(userId).ifPresent(u -> {
roleRepository.findById(roleId).ifPresent(r -> {
List<Role> roleList = new ArrayList<>(10);
roleList.add(r);
u.setRoleList(roleList);
userRepository.save(u);
});
});
}
@GetMapping("/update")
public String update(@RequestParam Long id) {
userRepository.findById(id).ifPresent(e -> {
userRepository.save(e);
});
return "success";
}
@GetMapping("/findByUserName")
public void findByUserName(@RequestParam Long id) {
userRepository.deleteById(id);
}
@GetMapping("/findById")
public User findById(@RequestParam Long id) {
User user = userRepository.findById(id).orElse(null);
return user;
}
@GetMapping("/deleteByRoleId")
public void deleteByRoleId(@RequestParam Long id) {
roleRepository.deleteById(id);
}
}
(1)访问save接口,会保存一个user数据
(2)访问saveRole接口,会生成一个Role数据
(3)访问realtive接口,数据存在的话,会关联user和role之间的外键关系
(4)访问findByUserName会删除user数据,并且删除关系表
(5)访问deleteByRoleId,也会删除role数据,并且删除关系表
CascadeType字段
CascadeType类型如下
CascadeType.ALL:所有操作都会级联执行,包括保存(persist)、更新(merge)、删除(remove)等。
CascadeType.PERSIST:级联保存操作。
CascadeType.MERGE:级联更新操作。
CascadeType.REMOVE:级联删除操作。
CascadeType.REFRESH:级联刷新操作。
CascadeType.DETACH:级联脱管操作。
如果想级联表与表之间的关系,可以使用它
使用如下
(1)User表
@Data
@Entity
@Table(name = "t_user")
public class User implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(name = "userName")
private String userName;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")})
private List<Role> roleList = new ArrayList<>();
}
(2)Role表
@Data
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String roleName;
@ManyToMany
@JoinTable(name = "sys_user_role", joinColumns = {@JoinColumn(name = "role_id")}, inverseJoinColumns = {@JoinColumn(name = "user_id")})
private List<User> userList;
}
controller写法如下
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/user")
public class User1Controller {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
@GetMapping("/hello")
public User hello(@RequestParam Long id) {
User user = userRepository.findById(id).orElseThrow(RuntimeException::new);
return user;
}
@GetMapping("/save")
public User save() {
User user = new User();
user.setUserName("aaa");
List<Role> roleList = new ArrayList<>(10);
Role role = new Role();
role.setRoleName("aaa");
roleList.add(role);
user.setRoleList(roleList);
userRepository.save(user);
return user;
}
@GetMapping("/deleteById")
public void deleteById(@RequestParam("id") Long id) {
userRepository.deleteById(id);
}
@GetMapping("/deleteById1")
public void deleteById1(@RequestParam("id") Long id) {
roleRepository.deleteById(id);
}
@GetMapping("/hello1")
public User hello1(@RequestParam Long id) {
User user = userRepository.findById(id).orElseThrow(RuntimeException::new);
user.getRoleList().forEach(e -> {
roleRepository.deleteById(e.getId());
});
List<Role> roleList = new ArrayList<>(10);
Role role = new Role();
role.setRoleName("aaa");
roleList.add(role);
user.setRoleList(roleList);
userRepository.save(user);
return user;
}
}
(1)访问save接口,会自动保存user和role数据
这个时候,已经级联保存
(2)访问deleteById接口,由于使用了All属性,就是删除也会连着Role一起删除,
级联的数据一起被删除
(3)访问deleteById1接口,只会删除Role和关系表
总结
jpa中多对多帮维护了表与表之间的外键关系,使外键关系表,不用自己维护,但是使用不当,也很容易出现删除不掉问题,一般在开发过程中,因人而异,大部分不会采用外键级联表,这种根据自己技术决定