1.JPA中的多对多

多对多的表关系建立靠的是中间表,

其中用户表和中间表的关系是一对多,角色表和中间表的关系也是一对多,如下图所示:

 

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

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

配置多对多的映射关系User

@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:配置当前对象在中间表的外键
     *                      @JoinColumn的数组
     *                          name:外键名
     *                          referencedColumnName:参照的主表的主键名
     *                  inverseJoinColumns:配置对方对象在中间表的外键
     */
    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @JoinTable(name = "sys_user_role",
            //joinColumns,当前对象在中间表中的外键
            joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
            //inverseJoinColumns,对方对象在中间表的外键
            inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}
    )

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

@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<>();

3.映射的注解说明

@ManyToMany

         作用:用于映射多对多关系

         属性:

                  cascade:配置级联操作。

                  fetch:配置是否采用延迟加载。

             targetEntity:配置目标的实体类。映射多对多的时候不用写。

 

@JoinTable

    作用:针对中间表的配置

    属性:

             nam:配置中间表的名称

             joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段                                              

             inverseJoinColumn:中间表的外键字段关联对方表的主键字段

            

@JoinColumn

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

    属性:

             name:指定外键字段的名称

             referencedColumnName:指定引用主表的主键字段名称

             unique:是否唯一。默认值不唯一

             nullable:是否允许为空。默认值允许。

             insertable:是否允许插入。默认值允许。

             updatable:是否允许更新。默认值允许。

             columnDefinition:列的定义信息

4. 多对多的操作

  保存

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

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

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

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

        userDao.save(user);
        roleDao.save(role);
    }

在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的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);
    }

6.Spring Data JPA中的多表查询

对象导航查询

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

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

 @Autowired
    private CustomerDao customerDao;
    @Test
    //由于是在java代码中测试,为了解决no session问题,将操作配置到同一个事务中
    @Transactional
    public void testFind() {
        Customer customer = customerDao.findOne(5l);
        Set<LinkMan> linkMans = customer.getLinkMans();//对象导航查询
        for(LinkMan linkMan : linkMans) {
           System.out.println(linkMan);
        }
    }

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

    @Autowired
    private LinkManDao linkManDao;
    @Test
    public void testFind() {
       LinkMan linkMan = linkManDao.findOne(4l);
        Customer customer = linkMan.getCustomer(); //对象导航查询
        System.out.println(customer);
    }

对象导航查询的问题分析

问题1:我们查询客户时,要不要把联系人查询出来?

分析:如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的,不使用时又会白白的浪费了服务器内存。

解决:采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。

配置方式:

    /**
     * 在客户对象的@OneToMany注解中添加fetch属性
     *      FetchType.EAGER :立即加载
     *      FetchType.LAZY  :延迟加载
     */
    @OneToMany(mappedBy="customer",fetch=FetchType.EAGER)
    private Set<LinkMan> linkMans = new HashSet<>(0);

问题2:我们查询联系人时,要不要把客户查询出来?

分析:例如:查询联系人详情时,肯定会看看该联系人的所属客户。如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的话,一个对象不会消耗太多的内存。而且多数情况下我们都是要使用的。

解决: 采用立即加载的思想。通过配置的方式来设定,只要查询从表实体,就把主表实体对象同时查出来

配置方式

    /**
     * 在联系人对象的@ManyToOne注解中添加fetch属性
     *      FetchType.EAGER :立即加载
     *      FetchType.LAZY  :延迟加载
     */
    @ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER)
    @JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id")
    private Customer customer;

7.使用Specification查询

 /**
     * Specification的多表查询
     */
    @Test
    public void testFind() {
        Specification<LinkMan> spec = new Specification<LinkMan>() {
            public Predicate toPredicate(Root<LinkMan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //Join代表链接查询,通过root对象获取
                //创建的过程中,第一个参数为关联对象的属性名称,第二个参数为连接查询的方式(left,inner,right)
                //JoinType.LEFT : 左外连接,JoinType.INNER:内连接,JoinType.RIGHT:右外连接
                Join<LinkMan, Customer> join = root.join("customer",JoinType.INNER);
                return cb.like(join.get("custName").as(String.class),"hdh");
            }
        };
        List<LinkMan> list = linkManDao.findAll(spec);
        for (LinkMan linkMan : list) {
            System.out.println(linkMan);

        }

    }

                                                                                                                                                                               ————你是我自罚三杯也不肯开口的秘密

 

 

 

posted on 2019-11-07 20:54  忆夏KhaZix  阅读(449)  评论(0)    收藏  举报