• rest-apiV2.0.0升级为simplest-api开源框架生态之simplest-jpa发布


    什么是 simplest#

    simplest 追求存粹简单和极致。

    旨在为项目快速开发提供一系列的基础能力,方便用户根据项目需求快速进行功能拓展

    不在去关心一些繁琐。重复工作,而是把重点聚焦到业务。

    前言#

    程序 10 年。作为一个多年程序。深知每个项目和程序,都有很多重复性工作要做。
    入行近 10 年,我写过很多程序,也写死过很多程序。。。。。

    见证互联网黄金时代,到如今的萎靡。幸运是我还在程序员大军中。和你们一起奋斗!

    我的故事 <<程序员三时>> 公众号 期待与你交流。希望给迷茫你一点启发和帮助。

    相关仓库#

    项目 简介 gitee 地址 github 地址
    simplest-api 前后端分离项目基于 simplest-api 可以快速构建基于 Web Json API 格式的统一通讯交互 https://gitee.com/daTouY/simplest-api/tree/main/ https://github.com/coder-amiao/simplest-api
    simplest-jpa 基于 QueryDSL 语法灵活强大的 QueryWrapper,链式 QueryChain 强大自由组合查询器,像写原生 SQL 一样基于 Java API 查询数据,优雅极致。 https://gitee.com/daTouY/simplest-jpa https://github.com/coder-amiao/simplest-jpa
    simplest-admin 基于 RABC 权限模型,功能丰富最新技术栈 Vue3.3 + Vite4 + TS + Pinia + Element-Plus 管理后台脚手架。开箱即用,可定制的代码生成模板,让你只需专注于业务开发。 https://gitee.com/daTouY/simplest-admin.git https://github.com/coder-amiao/simplest-admin
    simplest-boot 业务通用生态核心组件 https://gitee.com/daTouY/simplest-boot.git https://github.com/coder-amiao/simplest-boot

    Simplest开发文档

    这里主要介绍simplest-jpa 使用

    快速开始#

    项目 pom 中引入依赖

    <dependency>
        <groupId>cn.soboysgroupId>
        <artifactId>simplest-jpa-spring-boot-starterartifactId>
        <version>1.0.1version>
    dependency>
    
    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.28version>
    dependency>
    
    

    在 SpringBoot 启动类或者配置类上通过 @EnableJPAQuery注解开启 simplest-jpa

    @SpringBootApplication
    @EnableJPAQuery
    public class SpringbootJpaApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootJpaApplication.class, args);
        }
    
    }
    

    到此你项目中就可以使用所有的功能了。

    数据库配置#

    在项目中配置对应数据库连接

    spring:
      datasource:
        url: jdbc:mysql://127.0.0.1:3306/rest-admin?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai
        username: root
        password: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.zaxxer.hikari.HikariDataSource
        hikari:
          minimum-idle: 10
          maximum-pool-size: 20
          idle-timeout: 600000
          max-life-time: 1800000
    
      jpa:
        hibernate:
          naming:
            implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
          ddl-auto: update # 控制是否可以基于程序中Entity的定义自动创建或者修改DB中表结构
        show-sql: true #控制是否打印运行时的SQL语句与参数信息
        database-platform: org.hibernate.dialect.MySQLDialect #数据库方言
        open-in-view: true
        properties:
          hibernate:
            enable_lazy_load_no_trans: true
    

    定义对应entity 对应数据库表。JPA 会自动帮你生成数据库。

    package cn.soboys.springbootjpa.entity;
    
    import cn.soboys.springbootjpa.entity.base.BaseEntity;
    import cn.soboys.springbootjpa.entity.dto.TitleVo;
    import io.swagger.v3.oas.annotations.media.Schema;
    import lombok.Data;
    
    import javax.persistence.*;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * @author 公众号 程序员三时
     * @version 1.0
     * @date 2023/7/19 10:44
     * @webSite https://github.com/coder-amiao
     * 内容分类
     */
    @Data
    @Entity
    @Table(name = "cms_category")
    public class Category extends BaseEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
    
        /**
         * 标题
         */
        @Column(nullable = false, length = 64)
        @Schema(description = "标题")
        private String title;
    
    
        /**
         * @Embedded 用户映射数据库表到一个实体vo。
         */
        @Embedded
        @Schema(description = "标题信息")
        private TitleVo titleVo;
    
        /**
         * 描述
         */
        @Schema(description = "描述")
        private String described;
    
        /**
         * 图标
         */
        @Column( length = 32)
        @Schema(description = "图标",maxLength = 32)
        private String icon;
    
        /**
         * 图片
         */
        @Column( length = 32)
        @Schema(description = "图片",maxLength = 32)
        private String pic;
    
        /***
         * 引用关系不填写。默认对应主键。
         */
        @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.LAZY)
        @JoinTable(name = "cms_module_relation",
                joinColumns = @JoinColumn(name = "resource_id"),
                inverseJoinColumns = @JoinColumn(name = "module_id"))
        private Set modules=new HashSet<>();
    
    
        /**
         * 额外其他属性
         * @Transient 解绑和数据联系。属于实体类属性
         */
        @Transient
        private String other;
    
    
    }
    
    

    生成对应查询EntityPath

    基于 QueryDSL 的APT 技术 在 maven 的 pom.xml 中引入对应的插件

    <plugin>
        
        <groupId>com.mysema.mavengroupId>
        <artifactId>apt-maven-pluginartifactId>
        <version>1.1.3version>
        <executions>
            <execution>
                <phase>generate-sourcesphase>
                <goals>
                    <goal>processgoal>
                goals>
                <configuration>
                    <outputDirectory>target/generated-sources/javaoutputDirectory>
                    <processor>com.querydsl.apt.jpa.JPAAnnotationProcessorprocessor>
                configuration>
            execution>
        executions>
    plugin>
    

    然后通过 maven 编译项目。

    会在你指定目录生成对应查询EntityPaht实体

    简单查询#

    1. 编写自己的Repository 继承通用BaseRepository
    package cn.soboys.springbootjpa.repository;
    
    import cn.soboys.simplestjpa.BaseRepository;
    import cn.soboys.springbootjpa.entity.Category;
    import org.springframework.stereotype.Repository;
    
    /**
     * @author 公众号 程序员三时
     * @version 1.0
     * @date 2023/7/19 12:02
     * @webSite https://github.com/coder-amiao
     * 数据库 dao层。
     */
    @Repository
    public interface CategoryRepository extends BaseRepository
    
    }
    
    
    1. 编写自己的Service 继承通用Service
    package cn.soboys.springbootjpa.service;
    
    import cn.soboys.simplestjpa.IService;
    import cn.soboys.springbootjpa.entity.Category;
    import org.springframework.data.jpa.repository.Query;
    
    /**
     * @author 公众号 程序员三时
     * @version 1.0
     * @date 2023/7/19 17:08
     * @webSite https://github.com/coder-amiao
     */
    public interface ICategoryService extends IService {
    
    
    
    }
    

    实现类

    package cn.soboys.springbootjpa.service.impl;
    
    import cn.soboys.simplestjpa.ServiceImpl;
    import cn.soboys.springbootjpa.entity.Category;
    import cn.soboys.springbootjpa.repository.CategoryRepository;
    import cn.soboys.springbootjpa.service.ICategoryService;
    import org.springframework.stereotype.Service;
    
    /**
     * @author 公众号 程序员三时
     * @version 1.0
     * @date 2023/7/20 14:46
     * @webSite https://github.com/coder-amiao
     */
    @Service
    public class CategoryServerImpl extends ServiceImpl implements ICategoryService {
    
    
    }
    

    这样你 service 有基础所有操作数据增删改查的方法

    package cn.soboys.springbootjpa;
    
    import cn.soboys.simplestjpa.UpdateWrapper;
    import cn.soboys.springbootjpa.entity.Category;
    import cn.soboys.springbootjpa.entity.QCategory;
    import cn.soboys.springbootjpa.entity.dto.QTitleVo;
    import cn.soboys.springbootjpa.service.ICategoryService;
    import com.querydsl.core.BooleanBuilder;
    import com.querydsl.core.types.Predicate;
    import com.querydsl.jpa.impl.JPAUpdateClause;
    import lombok.extern.slf4j.Slf4j;
    import org.dromara.hutool.core.text.StrUtil;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.domain.Example;
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.test.annotation.Rollback;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    
    /**
     * @author 公众号 程序员三时
     * @version 1.0
     * @date 2023/7/26 21:54
     * @webSite https://github.com/coder-amiao
     */
    @SpringBootTest
    @Slf4j
    public class ServiceTest {
        @Autowired
        private ICategoryService categoryService;
    
        @Test
        void countByExample() {
            Category category = new Category();
            //category.setTitle("测试");
            long count = categoryService.count(Example.of(category));
            log.info("条件count{}", count);
        }
    
        @Test
        void getById() {
            Optional category = categoryService.getByIdOpt(6l);
            if (category.isPresent()) {
                log.info(category.get().toString());
            }
        }
    
        @Test
        void getOne() {
            QCategory qCategory = QCategory.category;
            QTitleVo vo=QTitleVo.titleVo;
            Predicate query=vo.subTitle.eq("batch1");
            Category category = categoryService.getOne(query);
            log.info(category.toString());
        }
    
        @Test
        void getPageQuery() {
            QCategory qCategory = QCategory.category;
            PageRequest pageRequest = PageRequest.of(0, 20); //第一页从零开始
            Predicate query = qCategory.title.like("%" + "batch" + "%");
            Page categoryList = categoryService.page(pageRequest, query);
            log.info("数量{}", categoryList.getContent().size());
        }
    
        @Test
        void getPage() {
            QCategory qCategory = QCategory.category;
    
          // categoryService.getJPAQueryFactory().select().where(qCategory.)
        }
    
    
        @Test
        void save() {
            Category c = new Category();
           // c.setTitle("保存");
            categoryService.save(c);
        }
    
        @Test
        void deleteById() {
            categoryService.removeById(6l);
        }
    
        @Test
        void deleteAll() {
            List ids = new ArrayList<>();
            ids.add(6l);
            ids.add(7l);
            Boolean flag = categoryService.removeByIds(ids);
        }
    
        /**
         * 实体ID对应存在更新否则添加
         */
        @Test
        void saveOrUpdate() {
            Category c = new Category();
           // c.setTitle("保存");
            categoryService.saveOrUpdate(c);
        }
    
    
        @Test
        @Rollback(value = false)
        @Transactional
        void updateChain() {
            QCategory qCategory = QCategory.category;
            categoryService.updateChain(qCategory)
                    .set(qCategory.title, "测试jpa")
                    .where(qCategory.id.eq(6l)).execute();
        }
    
    
        @Test
        @Rollback(value = false)
        @Transactional
        void update() {
            QCategory qCategory = QCategory.category;
            JPAUpdateClause updateWrapper = UpdateWrapper.of(qCategory);
            updateWrapper.set(qCategory.title, "bh").where(qCategory.id.eq(6l));
    
            Boolean flag = categoryService.update(updateWrapper);
    
            log.info("更新{}", flag);
        }
    
        @Test
        @Rollback(value = false)
        @Transactional
        void updateIgnoreNull() {
            Category category = new Category();
            category.setId(6l);
           // category.setSubTitle("忽略");
            //Category category1 = categoryService.update(category, true);  //会自动忽略实体空属性。
    
            //category.setTitle("");
            Category category1 = categoryService.update(category, true, new String[]{"title"});  //自定义不忽略字段,
            log.info("更新{}", category1);
        }
    
        @Test
        void selectQueryChain() {
            QCategory qCategory = QCategory.category;
            List categoryList = categoryService.queryChain()
                    .select(qCategory.title)
                    .from(qCategory).fetch();
            log.info("返回条数{}", categoryList.size());
        }
    
    
        @Test
        void selectQuery() {
            QCategory qCategory = QCategory.category;
            BooleanBuilder booleanBuilder = new BooleanBuilder();
    
            String subTitle = "88";
            if (StrUtil.isNotEmpty(subTitle)) {
                booleanBuilder.and(qCategory.described.eq("88"));
            }
            long id = 6l;
            if (!StrUtil.isBlankIfStr(id)) {
                booleanBuilder.and(qCategory.id.eq(6l));
            }
            List categories = categoryService.list(booleanBuilder);
            log.info("返回条数{}", categories.size());
        }
    
    }
    
    

    Simplest-JPA#

    simplest-jpa内置名为 BaseRepository 接口,它实现了基本的增删改查功能以及分页查询功能。 和对应Service

    新增数据#

    Service 提供了save,saveBatch,saveOrUpdate 方法

    • save(T entity)插入实体类数据,不忽略 null 值。
    • saveBatch(Collection entities) 批量插入实体类数据
    • saveOrUpdate(T entity) 插入或者更新,若主键有值,则更新,若没有主键值,则插入,插入或者更新都不会忽略 null 值。
    • saveOrUpdateSelective(T entity)插入或者更新,若主键有值,则更新,若没有主键值,则插入,更新会忽略 null 值。

    删除数据#

    Service 提供了remove,removeAll,removeById,removeByIds 方法

    • removeById(ID id) 根据主键删除数据
    • removeById(Collection ids) 根据多个主键批量删除数据
    • remove(Collection entities) 根据多个实体(实体需要有主键)进行批量删除
    • remove(T entity) 根据实体条件进行删除

    更新数据#

    Service 提供了update 多个重载方法

    • update(T entity) 查询条件 根据实体 ID 更新。不会忽略 null 值
    • update(T entity, Boolean ignore) 查询条件根据实体 ID 更新。自定义忽略 nul 值
    • update(T entity, Boolean ignore, String[] ignoreProperties) 自定义忽略实体字段属性
    • update(JPAUpdateClause query) 根据查询条件来更新数据。
    @Test
    @Rollback(value = false)
    @Transactional
    void update() {
        QCategory qCategory = QCategory.category;
        JPAUpdateClause updateWrapper = UpdateWrapper.of(qCategory);
        updateWrapper.set(qCategory.title, "bh").where(qCategory.id.eq(6l));
        Boolean flag = categoryService.update(updateWrapper);
        log.info("更新{}", flag);
    }
    

    updateChain#

    updateChain是一个对 UpdateWrapper 等进行封装的一个工具类,方便用户用于进行链式操作。

    @Test
    @Rollback(value = false)
    @Transactional
    void updateChain() {
        QCategory qCategory = QCategory.category;
        categoryService.updateChain(qCategory)
                .set(qCategory.title, "测试jpa")
                .where(qCategory.id.eq(6l)).execute();
    }
    

    Simplest-JPA 查询和分页查询#

    基础查询#

    simplest-jpaService提供了如下的功能用于查询数据库的数据

    • getById(ID id) 根据主键查询数据。
    • getByIdOpt(ID id)根据主键查询数据。返回Optional类型
    • getOne(Example example) 根据查询条件来查询 1 条数据。
    • getOne(Predicate query) 根据查询条件来查询 1 条数据。
    • getOneOpt(Example example)根据查询条件来查询 1 条数据。返回Optional类型查询到多条匹配数据时,会抛 NonUniqueResultException
    • listByIds(Collection ids) 根据数据主键查询数据集合。
    • list(Predicate query)根据查询条件查询数据集合。
    • list(Example query) 根据查询条件查询数据集合。
    • list()查询所有数据。
    • count(Predicate query) 根据查询条件查询数据数量。
    • count(Example example) 根据查询条件查询数据数量。
    • exists(Predicate query) 根据查询条件判断数据是否存在。
    • existsById(ID id) 根据 ID 判断是否存在

    分页查询#

    • page(Pageable page)分页查询所有数据。
    • page(Pageable page, Predicate query) 根据查询条件分页查询数据。

    链式查询#

    simplest-jpa 中,内置了 queryChain 和 updateChain 用于对数据进行链式查询操作和链式数据操作(修改和删除)。

    • queryChain:链式查询
    • updateChain:链式更新

    queryChain 示列#

    @Test
    void selectQueryChain() {
        QCategory qCategory = QCategory.category;
        List categoryList = categoryService.queryChain()
                .select(qCategory.title)
                .from(qCategory)
                .fetch();
        log.info("返回条数{}", categoryList.size());
    }
    

    条件查询

    @Test
    void selectQueryChainWhere() {
        QCategory qCategory = QCategory.category;
        List categoryList=  categoryService.queryChain()
                .select(qCategory.title)
                .from(qCategory)
                .where(qCategory.id.eq(1l))
                .fetch();
            log.info("返回条数{}", categoryList.size());
    }
    

    分页查询

    @Test
    void selectQueryChainWhere() {
        QCategory qCategory = QCategory.category;
        List categoryList = categoryService.queryChain()
                .select(qCategory.title)
                .from(qCategory)
                .where(qCategory.id.eq(1l))
                .limit(1)
                .fetch();
        log.info("返回条数{}", categoryList.size());
    }
    

    updateChain 示例#

    @Test
    @Rollback(value = false)
    @Transactional
    void updateChain() {
        QCategory qCategory = QCategory.category;
        categoryService.updateChain(qCategory)
                .set(qCategory.title, "测试jpa")
                .where(qCategory.id.eq(6l)).execute();
    }
    

    queryChain 的方法#

    • fetch() 获取多条数据 懒加载模式
    • fetchAll() 获取多条数据 忽略懒加载
    • fetchOne() 获取一条数据 多条会报错
    • fetchFirst() 查询第一条数据
    • fetchCount() 查询数据条数

    灵活的 QueryWrapper#

    在 增删改 和 查询和分页 章节中,我们随时能看到 QueryWrapper 的身影,QueryWrapper 是用于构造 Sql 的 强有力工具,也是 simplest-jpa 的亮点和特色。

    QueryWrapper 的使用#

    @SpringBootTest
    @Slf4j
    public class JpaQueryDSLTest {
    
        @Autowired
        private ICategoryService categoryService;
    
        @Autowired
        private JPAQueryFactory queryWrapper;
    
    
        /**
         * select() 和 fetch() 的常用写法
         * 使用fetch()查询时,数据库没有符合该条件的数据时,返回的是空集合,而不是null
         */
    
        /**
         * 查询字段-select()
         */
        @Test
        public void fetchColum() {
            QCategory qCategory = QCategory.category;
            List a = queryWrapper
                    .select(qCategory.title)
                    .from(qCategory)
                    .fetch();
            log.info("返回数量{}", a.size());
        }
    
        /**
         * 查询实体-selectFrom()
         */
        @Test
        public void fetchEntity() {
            QCategory qCategory = QCategory.category;
            List categories = queryWrapper.selectFrom(qCategory).fetch();
            log.info("返回数量{}", categories.size());
    
        }
    
        /**
         * 查询并将结果封装至dto中
         */
        @Test
        public void fetchDto() {
            QCategory qCategory = QCategory.category;
            List categoryDtos = queryWrapper.select(
                            Projections.bean(CategoryDto.class, qCategory.title)
                    )
                    .from(qCategory).fetch();
            log.info("返回数量{}", categoryDtos.size());
    
        }
    
        /**
         * 去重查询-selectDistinct()
         */
        @Test
        public void fetchDistinct() {
            QCategory qCategory = QCategory.category;
            List c = queryWrapper
                    .selectDistinct(qCategory.title)
                    .from(qCategory)
                    .fetch();
            log.info("返回数量{}", c.size());
        }
    
        /**
         * 获取首个查询结果-fetchFirst() 单条记录 limit 1
         */
        @Test
        public void fetchFirst() {
            QCategory qCategory = QCategory.category;
            Category category = queryWrapper
                    .selectFrom(qCategory)
                    .fetchFirst();
            log.info("返回数量{}", category.toString());
        }
    
        /**
         * 获取唯一查询结果-fetchOne()
         * 当fetchOne()根据查询条件从数据库中查询到多条匹配数据时,会抛`NonUniqueResultException`
         */
        @Test
        public void fetchOne() {
            QCategory qCategory = QCategory.category;
            Category category = queryWrapper
                    .selectFrom(qCategory)
                    .fetchOne();
            log.info("返回数量{}", category.toString());
        }
    
    
        /**
         * where 子句查询条件的常用写法
         */
        @Test
        public void fetchWhere() {
            QCategory qCategory = QCategory.category;
            List categories = queryWrapper
                    .selectFrom(qCategory)
                    .where(qCategory.title.eq("更新")
                            .and(qCategory.subTitle.like('%' + "测试" + '%')))
                    .fetch();
            log.info("返回数量{}", categories.size());
    
        }
    
        /**
         * where 动态条件查询
         */
    
        /**
         * 使用QueryDSL提供的BooleanBuilder来进行查询条件管理。
         */
        @Test
        public void fetchWhereDynamic() {
            QCategory qCategory = QCategory.category;
            BooleanBuilder builder = new BooleanBuilder();
            String title = "a";
            if (StrUtil.isNotEmpty(title)) {
                builder.and(qCategory.title.eq(title));
            }
            String subTitle = "";
            if (StrUtil.isNotEmpty(subTitle)) {
                builder.and(qCategory.subTitle.eq(subTitle));
            }
            List categories = queryWrapper
                    .selectFrom(qCategory)
                    .where(builder)
                    .fetch();
            log.info("返回数量{}", categories.size());
    
        }
    
        /**
         * 复杂的查询关系
         */
        @Test
        public void fetchWhereDynamicComplex() {
            QCategory qCategory = QCategory.category;
    
            BooleanBuilder builder = new BooleanBuilder();
            builder.or(qCategory.id.eq(1l));
    
            String title = "a";
            if (StrUtil.isNotEmpty(title)) {
                builder.and(qCategory.title.eq(title));
            }
            String subTitle = "";
            if (StrUtil.isNotEmpty(subTitle)) {
                builder.and(qCategory.subTitle.eq(subTitle));
            }
    
    
            List categories = queryWrapper
                    .selectFrom(qCategory)
                    .where(builder)
                    .fetch();
            log.info("返回数量{}", categories.size());
        }
    
    
        /**
         * 自定义封装查询的结果集
         * JPAQueryFactory查询工厂的select方法可以将Projections方法返回的QBean作为参数,通过Projections的bean方法来构建返回的结果集映射到实体内,有点像Mybatis内的ResultMap的形式,不过内部处理机制肯定是有着巨大差别的!
         * 

    * bean方法第一个参数需要传递一个实体的泛型类型作为返回集合内的单个对象类型,如果QueryDSL查询实体内的字段与DTO实体的字段名字不一样时,可以采用as方法来处理,为查询的结果集指定的字段添加别名,这样就会自动映射到DTO实体内。 */ /** * 使用Projections的Bean方法 */ @Test public void fetchBean() { QCategory qCategory = QCategory.category; QModule qModule = QModule.module; List categoryDtos = queryWrapper .select( Projections.bean(CategoryDto.class , qCategory.title, qModule.code) ).from(qCategory, qModule).fetch(); log.info("返回数量{}", categoryDtos.size()); } /** * 使用Projections的fields方法 */ @Test public void fetchFields() { QCategory qCategory = QCategory.category; List categoryDtos = queryWrapper .select( Projections.fields(CategoryDto.class , qCategory.title) ).from(qCategory).fetch(); log.info("返回数量{}", categoryDtos.size()); } /** * 使用集合的stream转换 * 从方法开始到fetch()结束完全跟QueryDSL没有任何区别,采用了最原始的方式进行返回结果集,但是从fetch()获取到结果集后处理的方式就有所改变了。 *

    * fetch()方法返回的类型是泛型List(List),List继承了Collection,完全存在使用Collection内非私有方法的权限,通过调用stream方法可以将集合转换成Stream泛型对象,该对象的map方法可以操作集合内单个对象的转换,具体的转换代码可以根据业务逻辑进行编写。 *

    * 在map方法内有个lambda表达式参数tuple,通过tuple对象get方法就可以获取对应select方法内的查询字段。 * ———————————————— */ @Test public void selectWithStream() { QCategory qCategory = QCategory.category; List categoryDtos = queryWrapper .select(qCategory.title, qCategory.subTitle) .from(qCategory) .fetch().stream().map(tuple -> { CategoryDto c = new CategoryDto(); c.setTitle(tuple.get(qCategory.title)); return c; }).collect(Collectors.toList()); log.info("返回数量{}", categoryDtos.size()); } @Test public void findByQuery() { QCategory qCategory = QCategory.category; //该Predicate为querydsl下的类,支持嵌套组装复杂查询条件 BooleanBuilder builder = new BooleanBuilder(); String title = "a"; if (StrUtil.isNotEmpty(title)) { builder.and(qCategory.title.eq(title)); } String subTitle = ""; if (StrUtil.isNotEmpty(subTitle)) { builder.and(qCategory.subTitle.eq(subTitle)); } List c = categoryService.list(builder); log.info("条数{}",c.size()); } @Test public void findByQueryWrapper(){ QCategory qCategory = QCategory.category; JPAQueryFactory queryWrapper=QueryWrapper.of(); List c = queryWrapper .selectDistinct(qCategory.title) .from(qCategory) .fetch(); log.info("返回数量{}", c.size()); } }

    实例#

    1. 单表查询
    @Service
    @Transactional
    public class UserService {
    
    	@Autowired
        private JPAQueryFactory queryFactory;
    
    	/**
    	 * attention:
    	 * Details:查询user表中的所有记录
    	 */
    	public List findAll(){
    		QUser quser = QUser.user;
    		return queryFactory.selectFrom(quser)
    					.fetch();
    	}
    
    	/**
    	 * Details:单条件查询
    	 */
    	public User findOneByUserName(final String userName){
    		QUser quser = QUser.user;
    		return queryFactory.selectFrom(quser)
    			.where(quser.name.eq(userName))
    			.fetchOne();
    	}
    
    	/**
    	 * Details:单表多条件查询
    	 */
    	public User findOneByUserNameAndAddress(final String userName, final String address){
    		QUser quser = QUser.user;
    		return queryFactory.select(quser)
    			.from(quser) // 上面两句代码等价与selectFrom
    			.where(quser.name.eq(userName).and(quser.address.eq(address)))// 这句代码等同于where(quser.name.eq(userName), quser.address.eq(address))
    			.fetchOne();
    	}
    
    	/**
    	 * Details:使用join查询
    	 */
    	public List findUsersByJoin(){
    		QUser quser = QUser.user;
    		QUser userName = new QUser("name");
    		return queryFactory.selectFrom(quser)
    			.innerJoin(quser)
    			.on(quser.id.intValue().eq(userName.id.intValue()))
    			.fetch();
    	}
    
    	/**
    	 * Details:将查询结果排序
    	 */
    	public List findUserAndOrder(){
    		QUser quser = QUser.user;
    		return queryFactory.selectFrom(quser)
    			.orderBy(quser.id.desc())
    			.fetch();
    	}
    
    	/**
    	 * Details:Group By使用
    	 */
    	public List findUserByGroup(){
    		QUser quser = QUser.user;
    		return queryFactory.select(quser.name)
    					.from(quser)
    					.groupBy(quser.name)
    					.fetch();
    	}
    
    	/**
    	 * Details:删除用户
    	 */
    	public long deleteUser(String userName){
    		QUser quser = QUser.user;
    		return queryFactory.delete(quser).where(quser.name.eq(userName)).execute();
    	}
    
    	/**
    	 * Details:更新记录
    	 */
    	public long updateUser(final User u, final String userName){
    		QUser quser = QUser.user;
    		return queryFactory.update(quser).where(quser.name.eq(userName))
    			.set(quser.name, u.getName())
    			.set(quser.age, u.getAge())
    			.set(quser.address, u.getAddress())
    			.execute();
    	}
    
    	/**
    	 * Details:使用原生Query
    	 */
    	public User findOneUserByOriginalSql(final String userName){
    		QUser quser = QUser.user;
    		Query query = queryFactory.selectFrom(quser)
    				.where(quser.name.eq(userName)).createQuery();
    		return (User) query.getSingleResult();
    	}
    
    
        /**
         *分页查询所有的实体,根据uIndex字段排序
         *
         * @return
         */
        public QueryResults findAllPage(Pageable pageable) {
            QUser user = QUser.user;
            return jpaQueryFactory
                    .selectFrom(user)
                    .orderBy(user.uIndex.asc())
                    .offset(pageable.getOffset())   //偏移量,计算:offset = ( 当前页 - 1) * 每页条数,这里直接使用的是Pageable中的Offset
                    .limit(pageable.getPageSize())  //每页大小
                    .fetchResults();    //获取结果,该结果封装了实体集合、分页的信息,需要这些信息直接从该对象里面拿取即可
        }
    
    
        /**
         * 部分字段映射查询
         * 投影为UserRes,lambda方式(灵活,类型可以在lambda中修改)
         *
         * @return
         */
        public List findAllUserDto(Pageable pageable) {
            QUser user = QUser.user;
            List dtoList = jpaQueryFactory
                    .select(
                            user.username,
                            user.userId,
                            user.nickName,
                            user.birthday
                    )
                    .from(user)
                    .offset(pageable.getOffset())
                    .limit(pageable.getPageSize())
                    .fetch()
                    .stream()
                    .map(tuple -> UserDTO.builder()
                            .username(tuple.get(user.username))
                            .nickname(tuple.get(user.nickName))
                            .userId(tuple.get(user.userId).toString())
                            .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
                            .build()
                    )
                    .collect(Collectors.toList());
            return dtoList;
        }
    
    
        /**
         * 部分字段映射查询
         * 投影为UserRes,自带的Projections方式,不能转换类型,但是可以使用as转换名字
         *
         * @return
         */
        public List findAllDto2() {
            QUser user = QUser.user;
            List dtoList = jpaQueryFactory
                    .select(
                            Projections.bean(
                                    UserDTO.class,
                                    user.username,
                                    user.userId,
                                    user.nickName,
                                    user.birthday
                            )
                    )
                    .from(user)
                    .fetch();
            return dtoList;
        }
    
    
    }
    
    1. 多表查询
    
    
    /**
     * @Description 查询全部
     * @Author 程序员三时
     * @Date  10:53
     * @return java.util.List
     **/
    @Override
    public List findAll(Loc loc) {
        // 使用 QueryDSL 进行查询
        QLoc qLoc = QLoc.loc1;
        QUser qUser = QUser.user;
        // 定于获取条件
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        // 要查询的条件
        if(!StringUtils.isEmpty(loc.getLoc())){
            // 放入要查询的条件信息
            booleanBuilder.and(qLoc.loc.contains(loc.getLoc()));
        }
        //连接查询条件(Loc.id = User.id )
        booleanBuilder.and(qLoc.id.eq(qUser.id));
        // 使用 QueryDSL 进行多表联合查询
        QueryResults listResult = queryFactory
                .select(QLoc.loc1,QUser.user)
                .from(qLoc, qUser)//查询两表
                .where(booleanBuilder)
                .fetchResults();
        //遍历 java8 自带流转换成集合
        List collect = listResult.getResults().stream().map(tuple -> {
            Loc lcs = tuple.get(qLoc);
            return lcs;
        }).collect(Collectors.toList());
        return collect;
    }
    
    
    
    部分字段映射的投影查询:
    
    当使用`@ManyToOne`、`@ManyToMany`建立关联时:
    
    /**
     * 根据部门的id查询用户的基本信息+用户所属部门信息,并且使用UserDeptDTO进行封装返回给前端展示
     * @param departmentId
     * @return
     */
    public List findByDepatmentIdDTO(int departmentId) {
        QUser user = QUser.user;
        QDepartment department = QDepartment.department;
        //直接返回
        return jpaQueryFactory
                //投影只去部分字段
                .select(
                        user.username,
                        user.nickName,
                        user.birthday,
                        department.deptName,
                        department.createDate
    
                )
                .from(user)
                //联合查询
                .join(user.department, department)
                .where(department.deptId.eq(departmentId))
                .fetch()
                //lambda开始
                .stream()
                .map(tuple ->
                        //需要做类型转换,所以使用map函数非常适合
                        UserDeptDTO.builder()
                                .username(tuple.get(user.username))
                                .nickname(tuple.get(user.nickName))
                                .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
                                .deptName(tuple.get(department.deptName))
                                .deptBirth(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(department.createDate)))
                                .build()
                )
                .collect(Collectors.toList());
    }
    
    
    当使用id建立关联时:
    /**
     * 根据部门的id查询用户的基本信息+用户所属部门信息,并且使用UserDeptDTO进行封装返回给前端展示
     *
     * @param departmentId
     * @return
     */
    public List findByDepatmentIdDTO(int departmentId) {
        QUser user = QUser.user;
        QDepartment department = QDepartment.department;
        //直接返回
        return jpaQueryFactory
                //投影只去部分字段
                .select(
                        user.username,
                        user.nickName,
                        user.birthday,
                        department.deptName,
                        department.createDate
    
                )
                .from(user, department)
                //联合查询
                .where(
                        user.departmentId.eq(department.deptId).and(department.deptId.eq(departmentId))
                )
                .fetch()
                //lambda开始
                .stream()
                .map(tuple ->
                        //需要做类型转换,所以使用map函数非常适合
                        UserDeptDTO.builder()
                                .username(tuple.get(user.username))
                                .nickname(tuple.get(user.nickName))
                                .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
                                .deptName(tuple.get(department.deptName))
                                .deptBirth(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(department.createDate)))
                                .build()
                )
                .collect(Collectors.toList());
    }
    
    
    使用 Projections 自定义返回 Bean:
    
    /**
     * Details:方式一:使用Bean投影
     */
    public List findByDTOUseBean(){
        Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
        return queryFactory.select(
                Projections.bean(PersonIDCardDto.class, QIDCard.iDCard.idNo, QPerson.person.address, QPerson.person.name))
                .from(QIDCard.iDCard, QPerson.person)
                .where(predicate)
                .fetch();
    }
    
    /**
     * Details:方式二:使用fields来代替setter
     */
    public List findByDTOUseFields(){
        Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
        return queryFactory.select(
                Projections.fields(PersonIDCardDto.class, QIDCard.iDCard.idNo, QPerson.person.address, QPerson.person.name))
                .from(QIDCard.iDCard, QPerson.person)
                .where(predicate)
                .fetch();
    }
    
    /**
     * Details:方式三:使用构造方法,注意构造方法中属性的顺序必须和构造器中的顺序一致
     */
    public List findByDTOUseConstructor(){
        Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
        return queryFactory.select(
                Projections.constructor(PersonIDCardDto.class, QPerson.person.name, QPerson.person.address, QIDCard.iDCard.idNo))
                .from(QIDCard.iDCard, QPerson.person)
                .where(predicate)
                .fetch();
    }
    
    
  • 相关阅读:
    SpringMVC (JSR303 & 拦截器)
    GLSL声明数组
    79基于matlab的大米粒中杂质识别
    YbtOJ「动态规划」第1章 背包问题
    Linux学习-46-LVM逻辑卷管理机制
    初识设计模式 - 策略模式
    【工业现场】核心工段机器代人,维视智造助力环保行业智能升级
    3.6、媒体接入控制
    【C语言】操作符与优先级详解
    STL应用 —— queue(队列)
  • 原文地址:https://www.cnblogs.com/kenx/p/17595627.html