Springboot中jpa中多对多

876 阅读4分钟

前言

在使用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注解提供几个字段

image.png

targetEntity:(可选)指定关联目标的实体类
cascade: (可选)指定级联到关联目标的操作
fetch:(可选)关联是应该延迟加载还是必须马上加载。EAGER 策略表示必须马上获取关联的实体,LAZY 策略表示用到关联对象时才去加载。默认值为 javax.persistence.FetchType.LAZY 
mappedBy:拥有关系的字段,单向关系不需要指定改属性;双向关系必须指定改属性的值

备注: mappedBy会和@JoinTable冲突,两者同时使用会报错

image.png

@JoinTable 属性

@JoinTable提供几个属性字段,

image.png 其中

name: 用于连接表名称
joinColumns: 连接表主表外键
inverseJoinColumns: 连接表副表外键

等等,建立完关系之后,jpa会自动维护表与表之间的外键关系

image.png

前提

已经建立好UserRole中的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数据

image.png

image.png

(2)访问saveRole接口,会生成一个Role数据

image.png

image.png

(3)访问realtive接口,数据存在的话,会关联user和role之间的外键关系

image.png

image.png

(4)访问findByUserName会删除user数据,并且删除关系表

image.png (5)访问deleteByRoleId,也会删除role数据,并且删除关系表

image.png

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数据

image.png 这个时候,已经级联保存 (2)访问deleteById接口,由于使用了All属性,就是删除也会连着Role一起删除,

image.png

级联的数据一起被删除

(3)访问deleteById1接口,只会删除Role和关系表

image.png

总结

jpa中多对多帮维护了表与表之间的外键关系,使外键关系表,不用自己维护,但是使用不当,也很容易出现删除不掉问题,一般在开发过程中,因人而异,大部分不会采用外键级联表,这种根据自己技术决定