美文网首页SpringBoot
Spring Boot Jpa 我已知的两种分页多查询条件的查询

Spring Boot Jpa 我已知的两种分页多查询条件的查询

作者: maxzhao_ | 来源:发表于2019-05-19 22:14 被阅读70次

    前言

    对于 SpringBoot 自带的 Spring JPA 方式的分页多条件查询, 目前我只掌握了两种,一种貌似还不支持条件的嵌套,下面就开始说明。

    表结构得表现一下吧

    SQL太长了,放到最后。

    JPA 的 repo 操作类

    /**
     * JpaSpecificationExecutor 这是为了实现第二种查询方式
     * @author maxzhao
     * @date 2019-05-06
     */
    @Repository(value = "appTableRepository")
    public interface AppTableRepository extends JpaRepository<AppTable, String>, JpaSpecificationExecutor<AppTable> {
    
    }
    

    测试代码

    备注:把application.*的文件拷贝到 test目录的 resources下。

    /**
     * 测试
     * @author maxzhao
     * @date 2019-06-06
     */
    @RunWith(SpringRunner.class)
    // BasicApplication 是启动类
    @SpringBootTest(classes = BasicApplication.class)
    public class AppColumnServiceTest {
        // 如果没有日志可以删掉
        private final static Logger logger = LoggerFactory.getLogger(AppTablesRepositoryTest.class);
        @Resource(name = "appTableRepository")
        private AppTableRepository appTableRepository;
    
        @Before
        public void startInit() {
            logger.info("---------------- {} -------------------", "AppColumnServiceTest");
        }
    
        @After
        public void endDestory() {    }
    
        @Test
        public void listTableTest() {
            //Sort.Direction是个枚举有ASC(升序)和DESC(降序)
            Sort.Direction sort = Sort.Direction.ASC;
            //PageRequest继承于AbstractPageRequest并且实现了Pageable
            //获取PageRequest对象 index:页码 从0开始  size每页容量 sort排序方式 "tab_name"->properties 以谁为准排序
            // 这里的tabName是实体的属性,不是数据库字段的名称
            Pageable pageable = PageRequest.of(0, 5, sort, "tabName");
            
            //region 查询条件设置方式一:
            //要匹配的实例对象
            AppTable appTable = new AppTable();
            appTable.setTabName("app_");
            //定义匹配规则 匹配"vendorId"这个属性 exact 精准匹配
            // 这里的tabName是实体的属性,不是数据库字段的名称
            ExampleMatcher exampleMatcher = ExampleMatcher
                    .matching()
                    .withMatcher("tabName", ExampleMatcher.GenericPropertyMatchers.contains());
            Example<AppTable> example = Example.of(appTable, exampleMatcher);
            //下面加断点,查询结果集
            Page<AppTable> page = appTableRepository.findAll(example, pageable);
            //获取总页数
            page.getTotalPages();
            //获取总元素个数
            page.getTotalElements();
            //获取该分页的列表
            page.getContent();
            page.getSize();
            //endregion
            
            //查询条件设置方式二:
            Specification<AppTable> specification = (Specification<AppTable>) (root, query, criteriaBuilder) -> {
                // 这里的tabName是实体的属性,不是数据库字段的名称
                Path<String> name = root.get("tabName");
                //Path<String> alias = root.get("alias");
                //查询条件1
                Predicate p1 = criteriaBuilder.like(name, "%" + "app_" + "%");
                //查询条件2
            //Predicate p2 = criteriaBuilder.lt(alias param.getAge());
                //查询条件1和2的关系
            //Predicate p = criteriaBuilder.and(p1, p2);
                return p1;
            };
            //下面加断点,查询结果集
            Page<AppTable> page2 = appTableRepository.findAll(specification,pageable);
            //endregion
        } 
    }
    

    代码中需要加断点的地方数据是一样的,如果不一样,设置了排序规则就没有如果。

    上面注释也写的很清楚了,这两种查询方式第一种应该是没有嵌套查询的(我没有翻到源码,网上也搜不到案例),第二种也不麻烦,就多继承一个接口,可以实现嵌套查询。

    查询方式二的第二种查询方式(单表多条件)

    (网上找的案例,不记得地址了,我也只是 cp了这部分代码,上面的测试一下,这个就懂了 )

    // Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb
    List<Predicate> predicates = new ArrayList<>();
    if (user.getUserId() != null && !user.getUserId().equals("")) {
        predicates.add(cb.like(root.get("userId").as(String.class), "%" + user.getUserId() + "%"));
    }
    if (user.getUserName() != null && !user.getUserName().equals("")) {
        predicates.add(cb.like(root.get("userName").as(String.class), "%" + user.getUserName() + "%"));
    }
    if (user.getGender() != null && !user.getGender().equals("")) {
        predicates.add(cb.like(root.get("gender").as(String.class), "%" + user.getGender() + "%"));
    }
    if (user.getAge() != null && !user.getAge().equals("")) {
        predicates.add(cb.like(root.get("age").as(String.class), "%" + user.getAge() + "%"));
    }
    Predicate[] pre = new Predicate[predicates.size()];
    criteriaQuery.where(predicates.toArray(pre));
    return cb.and(predicates.toArray(pre));
    

    查询方式二的第二种查询方式(多表、多条件)

    备注:记得设置 @ManyToOne或者 @OneToMany之类的外键关联哦

    // (Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb)
    List<Predicate> list = new ArrayList<>();
                    //根据userId 查询user
    if (StringUtils.isNotBlank(params.getUserId())) {
        list.add(cb.equal(root.get("userId").as(String.class), params.getUserId()));
    }
                    //根据userName 模糊查询user
    if (StringUtils.isNotBlank(params.getUserName())) {
        list.add(cb.like(root.get("userName").as(String.class), "%" + params.getUserName() + "%"));
    }
                    //根据gender 查询user
    if (StringUtils.isNotBlank(params.getGender())) {
        list.add(cb.equal(root.get("gender").as(String.class), params.getGender()));
    }
                    //根据age>? 查询user
    if (StringUtils.isNotBlank(params.getAge())) {
        list.add(cb.gt(root.get("age").as(Integer.class), Integer.valueOf(params.getAge())));
    }
                    //根据gradeName 查询user
    if (StringUtils.isNotBlank(params.getGradeName())) {
        Join<Grade, User> join = root.join("grade", JoinType.LEFT);
        list.add(cb.equal(join.get("gradeName"), params.getGradeName()));
    }
                    //根据schoolName 查询user
    if (StringUtils.isNotBlank(params.getSchoolName())) {
        Join<School, User> join = root.join("grade", JoinType.LEFT);
        list.add(cb.equal(join.get("school").get("schoolName"), params.getSchoolName()));
    }
    Predicate[] pre = new Predicate[list.size()];
    criteriaQuery.where(list.toArray(pre));
    return cb.and(list.toArray(pre));
    

    不推荐上面这种用法,这么用让微服务颜面何存?

    附录:

    数据库SQL:

    -- MySql
    create table app_table
    (
        tab_name     varchar(255) not null comment '表名主键'
            primary key,
        alias        varchar(255) null comment '别名',
        can_del_data int          null comment '是否可以清除数据0否1是',
        id_cols      varchar(255) null comment '主键列名',
        remark       varchar(255) null comment '备注',
        tab_type     int          null comment '物理表类型(app 1应用级;cf_ 2配置级;bn 3 业务级;tmp 4临时;5其它数据表)'
    )comment '应用表信息' charset = utf8;
    -- 数据
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_area', '地区编码表', 0, 'id', '地区编码表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_column', '应用表字段信息', 0, 'id', '应用表字段信息', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_dept', '部门管理', 0, 'id', '部门管理', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_dict', '应用字典表', 0, 'id', '应用字典表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_dict_value', '应用字典属性表', 0, 'id', '应用字典属性表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_log', '系统日志', 0, 'id', '系统日志', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_menu', '菜单表', 0, 'id', '菜单表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_role', '角色表', 0, 'id', '角色表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_role_group', '角色组表', 0, 'id', '角色组表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_role_group_link', '角色与角色组表', 0, 'id', '角色与角色组表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_role_menu', '角色菜单表', 0, 'id', '角色菜单表', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_table', '应用表信息', 0, 'tab_name', '应用表信息', 1);
    INSERT INTO app_table (tab_name, alias, can_del_data, id_cols, remark, tab_type) VALUES ('app_user', '用户表', 0, 'id', '用户表', 1);
    
    create table app_column
    (
        id             varchar(255)  not null comment '主键'
            primary key,
        alias          varchar(255)  null comment '别名',
        app_dict_id    bigint        null comment '绑定字典主键',
        char_length    int           null comment '内容长度',
        col_default    varchar(255)  null comment '默认值',
        col_name       varchar(255)  null comment '字段名',
        col_type       varchar(255)  null comment '字段类型(用于与使用控件相关)',
        col_type_alias varchar(255)  null comment '字段类型描述',
        is_null        int default 1 null comment '是否允许空(1 是 0 否)',
        remark         varchar(255)  null comment '备注',
        scale          int           null comment '精度',
        tab_name       varchar(255)  null comment '表名',
        time_format    varchar(255)  null comment '时间格式'
    )comment '应用表字段信息' charset = utf8;
    
    
    

    specificationin语句

    Path<String> path = root.get("tabName");
    CriteriaBuilder.In<String> in = criteriaBuilder.in(path);
    in.value("app_table");
    in.value("app_column");
    criteriaBuilder.and(in);
    return criteriaBuilder.and(p1, in);
    

    本文地址:Spring Boot Jpa 我已知的两种分页多查询条件的查询方式

    相关文章

      网友评论

        本文标题:Spring Boot Jpa 我已知的两种分页多查询条件的查询

        本文链接:https://www.haomeiwen.com/subject/smdczqtx.html