Specifications动态查询

* 继承了JpaSpecificationExecutor<T>中的方法
    //根据条件查询一个对象
     T findOne(Specification<T> spec);    
       //根据条件查询集合
     List<T> findAll(Specification<T> spec);
       //根据条件分页查询
     Page<T> findAll(Specification<T> spec, Pageable pageable);
       //排序查询查询
     List<T> findAll(Specification<T> spec, Sort sort);
       //统计查询
     long count(Specification<T> spec);

* Specification:查询条件
    自定义我们自己的Specification实现类
    实现
        1. root:查询的根对象(查询的任何属性都可以从根对象中获取)
        2. CriteriaQuery:顶层查询对象,自定义查询方式(了解即可,一般不使用)
        3. CriteriaBuilder:查询构造器,封装了很多的查询条件
        Predicate toPredicate(Root<T> root,CriteriaQuery<?> query,CriteriaBuilder cb); 

* 代码
     @Test
    public void testEquals(){
        Customer c = dao.findOne(new Specification<Customer>() {
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path<Object> custId = root.get("custId");
                Predicate predicate = criteriaBuilder.equal(custId, 1l);
                return predicate;
            }
        });

        System.out.println(c);
    }

    /**
     * testNoAsString
     * 在path写上String就不用再写as.(String.class)
     *
     */
    @Test
    public void testNoAsString() throws Exception{
        Customer customer = dao.findOne(new Specification<Customer>() {
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path<String> custName = root.get("custName");
                Predicate predicate = criteriaBuilder.like(custName, "ha%");

                return predicate;
            }
        });

        System.out.println(customer);
    }

    /**
     * 测试分页查询
     * Pageable
     */
    @Test
    public void testPageSelect()throws Exception{
        Page<Customer> page = dao.findAll(new PageRequest(0, 5));
        //返回的总页数
        System.out.println(page.getTotalPages());
        //返回的总记录数
        System.out.println(page.getTotalElements());
        //返回的内容集合
        System.out.println(page.getContent());
    }

    /**
     * 排序查询
     */

    @Test
    public void testSortSelect() throws Exception{
        //倒序查询,根据custId属性倒序
        List<Customer> list = dao.findAll(new Sort(Sort.Direction.DESC,"custId"));
        for (Customer customer : list) {
            System.out.println(customer);
        }
    }

多表之间的关系和操作多表的操作步骤

表关系:
    一对一
        主键映射(两个表的主键相同),外键映射(在一个表中设置外键来对应另一张表的主键)
    一对多
        一的一方:主表
        多的一方:从表
        外键:需要在从表上新建一列作为外键,取值来源于主表的主键
    多对多
        中间表:至少有两个字段组成

实体类中的关系:
    包含关系:可以通过实体类中的包含关系描述表关系
    继承关系:

步骤:
    1. 明确表关系
    2. 确定表关系(描述:外键|中间表)
    3. 编写实体类,在实体类中描述表关系(包含|继承)
    4. 配置映射关系

多表操作练习

* 一般我们不会进行双向关联,只会保存可以维护中间表/外键的一方
1. 一对多:

    1. 建立表之间的关联关系(实体类之间的关系)
    Customer(一的一方)
    //配置一对多的关系
    //@OneToMany(targetEntity = LinkMan.class)
    //@JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")

    //放弃维护外键(一般一的一方会放弃外键的维护)
    @OneToMany(mappedBy = "customer")
    private Set<LinkMan> linkmans = new HashSet<LinkMan>();

    linkman(多的一方)
    //配置一对多关系
    /@ManyToOne(targetEntity = Customer.class)
    //配置级联操作,一般级联不会配置到一的一方,太危险,会把所有对应的多都删除
    @ManyToOne(targetEntity = Customer.class,cascade = CascadeType.ALL)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;

    2. 测试类

    @Autowired
    private ICustomerDao customerDao;
    @Autowired
    private ILinkmanDao linkmanDao;
    //保存
    @Test
    public void testAdd() throws Exception{

        Customer customer = new Customer();
        customer.setCustName("test222");
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("testl222");
        linkMan.setCustomer(customer);
        //只需要在维护外键的一方告诉关系即可保存维护外键
        customerDao.save(customer);
        linkmanDao.save(linkMan);

    }

    /**
     * 级联删除
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void testCascadeDelete()throws Exception{
        linkmanDao.delete(4l);
    }
2. 多对多
    1.  建立表之间的关联关系(实体类之间的关系)
    SysUser
    //配置多对多关系
    @ManyToMany(targetEntity = SysRole.class,cascade = CascadeType.ALL)
    @JoinTable(name = "user_role_rel",
            joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")}
    )
    private Set<SysRole> roles = new HashSet<>();

    SysRole
    //配置多对多关系,并放弃外键/表的维护
    @ManyToMany(mappedBy = "roles")
    private Set<SysUser> users = new HashSet<>();

    2. 测试类

    @Autowired
    private ISysRoleDao roleDao;
    @Autowired
    private ISysUserDao userDao;
    //保存
    @Test
    public void testAdd() throws Exception{
        SysUser user = new SysUser();
        user.setUserName("haha");
        SysRole role = new SysRole();
        role.setRoleName("hehe");
        //在一方进行外键/表维护
        user.getRoles().add(role);
        userDao.save(user);
        roleDao.save(role);

    }

    //删除,没有级联
    @Test
    @Transactional
    @Rollback(value = false)
    public void testDelete() throws Exception{
        userDao.delete(2l);
    }
3. 关于删除
    * 有从表数据
          1、在默认情况下,它会把外键字段置为null,然后删除主表数据。如果在数据库的表结构上,外键字段有非空约束,默认情况就会报错了。
          2、如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null,没有关系)因为在删除时,它根本不会去更新从表的外键字段了。
          3、如果还想删除,使用级联删除引用
    * 没有从表数据引用:随便删
    * 在多对多时候,级联删除最好不要配置,如果配了的话,如果数据之间有相互引用关系,可能会清空所有数据

对象导航查询

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

简单理解:就是查询对象后通过.getXxx来调用有关联关系的属性
//对象导航查询
@Test
@Transactional
public void testObjectmap() throws Exception{

    SysUser user = userDao.findOne(5l);
    Set<SysRole> roles = user.getRoles();
    System.out.println(roles);
}

Specification的多表查询

//创建的过程中,第一个参数为关联对象的属性名称,第二个参数为连接查询的方式(left,inner,right)
//JoinType.LEFT : 左外连接,JoinType.INNER:内连接,JoinType.RIGHT:右外连接

//使用Specification的多表查询
@Test
@Transactional
public void testSpec(){
    SysUser user = userDao.findOne(new Specification<SysUser>() {
        @Override
        public Predicate toPredicate(Root<SysUser> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            Join<SysUser, SysRole> join = root.join("roles", JoinType.LEFT);

            return criteriaBuilder.like(join.get("roleName").as(String.class), "he%");
        }
    });
    Set<SysRole> roles = user.getRoles();
    System.out.println(roles);
}

* roles,表示查询中当前类中的外键属性,后面的条件是对外连接表中条件的查询
* 也可以继续写root.xx,并对root条件限制,root代表的是本类的查询条件,join是连接表的插叙条件

spring data jpa多对多查询 spring data jpa多表查询_Customer

Jpql的多表查询

在接口中重写方法,并使用@query注解
如:
@Query("select c from Customer c left outer join c.linkMans m where m.lkmId = 3")
List<Customer> getCustomerByLink();