SpringDataJpa多表查询 下(多对多)

2022-03-23 16:58:10 浏览数 (2)

关系分析

采用的示例为用户和角色。

用户:指的是咱们班的每一个同学。

角色:指的是咱们班同学的身份信息。

比如A同学,它是我的学生,其中有个身份就是学生,还是家里的孩子,那么他还有个身份是子女。

同时B同学,它也具有学生和子女的身份。

那么任何一个同学都可能具有多个身份。同时学生这个身份可以被多个同学所具有。

所以我们说,用户和角色之间的关系是多对多。

表关系建立

多对多的表关系建立靠的是中间表,其中用户表和中间表的关系是一对多,角色表和中间表的关系也是一对多

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

User实体

代码语言:javascript复制
@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;


    /**
     * 配置用户到角色的多对多
     *   配置多对多的映射关系
     *       1.声明表关系的配置
     *       @ManyToMany(targetEntity = Role.class)//声明多对多
     *       targetEntity:代表对方实体类字节码
     *       2.配置中间表(包含两个外键)
     *       @JoinTable
     *          name:中间表的名称
     *          joinColumns:配置当前对象在中间表的外键
     *               接收@inverseJoinColumns数组   name外键名  referencedColumnName:参照的主表主键名
     *          inverseJoinColumns:配置对方对象在中间表的外键
     *               接收@inverseJoinColumns数组   name外键名  referencedColumnName:参照的主表主键名
     * @return
     */
    @ManyToMany(targetEntity = Role.class)
    @JoinTable(name = "sys_user_role",
            //joinColumns:当前对象在中间表中的外键  referencedColumnName 来源
            joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
            //inverseJoinColumns:对方对象在中间表中的外键
            inverseJoinColumns ={@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} )
    private Set<Role> roles=new HashSet<Role>();

Role实体

代码语言:javascript复制
@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(mappedBy = "roles")//配置
   private Set<User> users=new HashSet<User>();

注解说明

代码语言:javascript复制
@ManyToMany
	作用:用于映射多对多关系
	属性:
		cascade:配置级联操作。
		fetch:配置是否采用延迟加载。
    	targetEntity:配置目标的实体类。映射多对多的时候不用写。

@JoinTable
    作用:针对中间表的配置
    属性:
    	nam:配置中间表的名称
    	joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段			  			
    	inverseJoinColumn:中间表的外键字段关联对方表的主键字段
    	
@JoinColumn
    作用:用于定义主键字段和外键字段的对应关系。
    属性:
    	name:指定外键字段的名称
    	referencedColumnName:指定引用主表的主键字段名称
    	unique:是否唯一。默认值不唯一
    	nullable:是否允许为空。默认值允许。
    	insertable:是否允许插入。默认值允许。
    	updatable:是否允许更新。默认值允许。
    	columnDefinition:列的定义信息。

测试方法

保存

代码语言:javascript复制
  @Autowired
    private UserDao userDao;
    @Autowired
    private RoleDao roleDao;

    /**
     * 保存一个用户保存一个角色
     *
     * 多对多放弃维护权:被动的一方放弃
     */
    @Test
   @Transactional
   @Rollback(value = false)
    public void testAdd(){
        User user=new User();
        user.setUserName("小张");

        Role role=new Role();
        role.setRoleName("程序猿");

        user.getRoles().add(role);//配置用户到角色关系,可以对中间表中的数据进行维护

        role.getUsers().add(user);//配置角色到用户的关系,可以对中间表的数据进行维护

        userDao.save(user);

        roleDao.save(role);
   }

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

代码语言:javascript复制
//配置多对多  放弃对中间表的维护权,解决保存中主键冲突的问题
    @ManyToMany(mappedBy = "roles")//配置
   private Set<User> users=new HashSet<User>();

级联操作

级联:操作一个对象的同时操作他的关联对象

代码语言:javascript复制
		级联操作:
			1.需要区分操作主体
			2.需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
			3.cascade(配置级联)

实体类上添加级联配置

代码语言:javascript复制
 //cascade:配置级联操作   All级联所有操作
    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)

级联保存

代码语言:javascript复制
 /**
     * 测试级联添加(保存一个用户的同时保存用户的关联角色 )
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void testCasCadeAdd(){
        User user=new User();
        user.setUserName("小张");

        Role role=new Role();
        role.setRoleName("程序猿");

        user.getRoles().add(role);//配置用户到角色关系,可以对中间表中的数据进行维护

        role.getUsers().add(user);//配置角色到用户的关系,可以对中间表的数据进行维护

        userDao.save(user);

    }

级联删除

代码语言:javascript复制
/**
     * 测试级联删除
     *   案例删除id唯一的客户同时删除他的关联对象
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void testCascadeRemove(){
        //查询一号用户
        User one = userDao.findOne(1l);
        //删除一号用户
        userDao.delete(one);
    }

多表的查询

对象导航查询 对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。例如:我们通过ID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。对象导航查询的使用要求是:两个对象之间必须存在关联关系。

需求:查询一个客户,获取该客户下的所有联系人

代码语言:javascript复制
   /**
     * 测试对象导航查询(查询一个对象的时候,通过此对象查询所有的关联对象)
     */
    @Test
    @Transactional  //解决java代码中的no session问题
    @Rollback(value = false)
    public void testQuery1() {
        //查询id为一的客户
        Customer customer = customerDao.getOne(1l);
        //对象导航查询,此客户下的所有联系人
        Set<LinkMan> linkMans = customer.getLinkMans();
        for (LinkMan linkMan : linkMans) {
            System.out.println(linkMan);
        }
    }

实体配置延迟加载与立即加载

代码语言:javascript复制
  *  fetch:配置关联对象的加载方式
     *       值:EAGER 立即加载  不推荐使用立即加载
     *           LAZY  延迟加载
      */
    @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    private Set<LinkMan> linkMans=new HashSet<LinkMan>();
代码语言:javascript复制
/**
     * 一对一
     * 从一的一方查询
     * 对象导航查询默认使用的是延迟加载的形式查询的
     * 调用get方法并不会立即发送查询,而是在使用关联对象的时候才会查询
     * 延迟加载
     * 不想用延迟加载 修改配置 将延迟加载改为立即加载即可
     * fetch,需要配置到多表映射关系的注解上面   虽然可以配置立即加载但并不推荐使用
     */
    @Test
    @Transactional  //解决java代码中的no session问题
    @Rollback(value = false)
    public void testQuery2() {
        //查询id为一的客户   getOne延迟加载  findOne立即加载
        Customer customer = customerDao.findOne(1l);
        //对象导航查询,此客户下的所有联系人
        Set<LinkMan> linkMans = customer.getLinkMans();
        for (LinkMan linkMan : linkMans) {
            System.out.println(linkMan);
        }
    }
代码语言:javascript复制
 /**
     *从一方查询多方
     *   默认使用延迟加载  (推荐延迟加载!!!)
     *从多方查询一方
     *    默认使用立即加载
     * 一对一
     * 对象导航查询 从多的一方查询
     *   默认使用立即加载
     *  延迟加载:
     */
    @Test
    @Transactional  //解决java代码中的no session问题
    @Rollback(value = false)
    public void testQuery3() {
        //查询id为2的联系人
        LinkMan linkMan = linkManDao.findOne(2l);
        //对象导航查询所属客户
        Customer customer = linkMan.getCustomer();

        System.out.println(customer);
    }
ole

0 人点赞