什么是 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 中引入依赖

  1. <dependency>
  2. <groupId>cn.soboys</groupId>
  3. <artifactId>simplest-jpa-spring-boot-starter</artifactId>
  4. <version>1.0.1</version>
  5. </dependency>
  6. <!-- MySQL -->
  7. <dependency>
  8. <groupId>mysql</groupId>
  9. <artifactId>mysql-connector-java</artifactId>
  10. <version>8.0.28</version>
  11. </dependency>

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

  1. @SpringBootApplication
  2. @EnableJPAQuery
  3. public class SpringbootJpaApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SpringbootJpaApplication.class, args);
  6. }
  7. }

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

数据库配置

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

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

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

  1. package cn.soboys.springbootjpa.entity;
  2. import cn.soboys.springbootjpa.entity.base.BaseEntity;
  3. import cn.soboys.springbootjpa.entity.dto.TitleVo;
  4. import io.swagger.v3.oas.annotations.media.Schema;
  5. import lombok.Data;
  6. import javax.persistence.*;
  7. import java.util.HashSet;
  8. import java.util.Set;
  9. /**
  10. * @author 公众号 程序员三时
  11. * @version 1.0
  12. * @date 2023/7/19 10:44
  13. * @webSite https://github.com/coder-amiao
  14. * 内容分类
  15. */
  16. @Data
  17. @Entity
  18. @Table(name = "cms_category")
  19. public class Category extends BaseEntity {
  20. @Id
  21. @GeneratedValue(strategy = GenerationType.IDENTITY)
  22. private Long id;
  23. /**
  24. * 标题
  25. */
  26. @Column(nullable = false, length = 64)
  27. @Schema(description = "标题")
  28. private String title;
  29. /**
  30. * @Embedded 用户映射数据库表到一个实体vo。
  31. */
  32. @Embedded
  33. @Schema(description = "标题信息")
  34. private TitleVo titleVo;
  35. /**
  36. * 描述
  37. */
  38. @Schema(description = "描述")
  39. private String described;
  40. /**
  41. * 图标
  42. */
  43. @Column( length = 32)
  44. @Schema(description = "图标",maxLength = 32)
  45. private String icon;
  46. /**
  47. * 图片
  48. */
  49. @Column( length = 32)
  50. @Schema(description = "图片",maxLength = 32)
  51. private String pic;
  52. /***
  53. * 引用关系不填写。默认对应主键。
  54. */
  55. @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.LAZY)
  56. @JoinTable(name = "cms_module_relation",
  57. joinColumns = @JoinColumn(name = "resource_id"),
  58. inverseJoinColumns = @JoinColumn(name = "module_id"))
  59. private Set<Module> modules=new HashSet<>();
  60. /**
  61. * 额外其他属性
  62. * @Transient 解绑和数据联系。属于实体类属性
  63. */
  64. @Transient
  65. private String other;
  66. }

生成对应查询EntityPath

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

  1. <plugin>
  2. <!--因为QueryDsl是类型安全的,所以还需要加上Maven APT plugin,使用 APT 自动生成Q类:-->
  3. <groupId>com.mysema.maven</groupId>
  4. <artifactId>apt-maven-plugin</artifactId>
  5. <version>1.1.3</version>
  6. <executions>
  7. <execution>
  8. <phase>generate-sources</phase>
  9. <goals>
  10. <goal>process</goal>
  11. </goals>
  12. <configuration>
  13. <outputDirectory>target/generated-sources/java</outputDirectory>
  14. <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
  15. </configuration>
  16. </execution>
  17. </executions>
  18. </plugin>

然后通过 maven 编译项目。

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

简单查询

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

实现类

  1. package cn.soboys.springbootjpa.service.impl;
  2. import cn.soboys.simplestjpa.ServiceImpl;
  3. import cn.soboys.springbootjpa.entity.Category;
  4. import cn.soboys.springbootjpa.repository.CategoryRepository;
  5. import cn.soboys.springbootjpa.service.ICategoryService;
  6. import org.springframework.stereotype.Service;
  7. /**
  8. * @author 公众号 程序员三时
  9. * @version 1.0
  10. * @date 2023/7/20 14:46
  11. * @webSite https://github.com/coder-amiao
  12. */
  13. @Service
  14. public class CategoryServerImpl extends ServiceImpl<CategoryRepository, Category, Long> implements ICategoryService {
  15. }

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

  1. package cn.soboys.springbootjpa;
  2. import cn.soboys.simplestjpa.UpdateWrapper;
  3. import cn.soboys.springbootjpa.entity.Category;
  4. import cn.soboys.springbootjpa.entity.QCategory;
  5. import cn.soboys.springbootjpa.entity.dto.QTitleVo;
  6. import cn.soboys.springbootjpa.service.ICategoryService;
  7. import com.querydsl.core.BooleanBuilder;
  8. import com.querydsl.core.types.Predicate;
  9. import com.querydsl.jpa.impl.JPAUpdateClause;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.dromara.hutool.core.text.StrUtil;
  12. import org.junit.jupiter.api.Test;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.boot.test.context.SpringBootTest;
  15. import org.springframework.data.domain.Example;
  16. import org.springframework.data.domain.Page;
  17. import org.springframework.data.domain.PageRequest;
  18. import org.springframework.test.annotation.Rollback;
  19. import org.springframework.transaction.annotation.Transactional;
  20. import java.util.ArrayList;
  21. import java.util.List;
  22. import java.util.Optional;
  23. /**
  24. * @author 公众号 程序员三时
  25. * @version 1.0
  26. * @date 2023/7/26 21:54
  27. * @webSite https://github.com/coder-amiao
  28. */
  29. @SpringBootTest
  30. @Slf4j
  31. public class ServiceTest {
  32. @Autowired
  33. private ICategoryService categoryService;
  34. @Test
  35. void countByExample() {
  36. Category category = new Category();
  37. //category.setTitle("测试");
  38. long count = categoryService.count(Example.of(category));
  39. log.info("条件count{}", count);
  40. }
  41. @Test
  42. void getById() {
  43. Optional<Category> category = categoryService.getByIdOpt(6l);
  44. if (category.isPresent()) {
  45. log.info(category.get().toString());
  46. }
  47. }
  48. @Test
  49. void getOne() {
  50. QCategory qCategory = QCategory.category;
  51. QTitleVo vo=QTitleVo.titleVo;
  52. Predicate query=vo.subTitle.eq("batch1");
  53. Category category = categoryService.getOne(query);
  54. log.info(category.toString());
  55. }
  56. @Test
  57. void getPageQuery() {
  58. QCategory qCategory = QCategory.category;
  59. PageRequest pageRequest = PageRequest.of(0, 20); //第一页从零开始
  60. Predicate query = qCategory.title.like("%" + "batch" + "%");
  61. Page<Category> categoryList = categoryService.page(pageRequest, query);
  62. log.info("数量{}", categoryList.getContent().size());
  63. }
  64. @Test
  65. void getPage() {
  66. QCategory qCategory = QCategory.category;
  67. // categoryService.getJPAQueryFactory().select().where(qCategory.)
  68. }
  69. @Test
  70. void save() {
  71. Category c = new Category();
  72. // c.setTitle("保存");
  73. categoryService.save(c);
  74. }
  75. @Test
  76. void deleteById() {
  77. categoryService.removeById(6l);
  78. }
  79. @Test
  80. void deleteAll() {
  81. List<Long> ids = new ArrayList<>();
  82. ids.add(6l);
  83. ids.add(7l);
  84. Boolean flag = categoryService.removeByIds(ids);
  85. }
  86. /**
  87. * 实体ID对应存在更新否则添加
  88. */
  89. @Test
  90. void saveOrUpdate() {
  91. Category c = new Category();
  92. // c.setTitle("保存");
  93. categoryService.saveOrUpdate(c);
  94. }
  95. @Test
  96. @Rollback(value = false)
  97. @Transactional
  98. void updateChain() {
  99. QCategory qCategory = QCategory.category;
  100. categoryService.updateChain(qCategory)
  101. .set(qCategory.title, "测试jpa")
  102. .where(qCategory.id.eq(6l)).execute();
  103. }
  104. @Test
  105. @Rollback(value = false)
  106. @Transactional
  107. void update() {
  108. QCategory qCategory = QCategory.category;
  109. JPAUpdateClause updateWrapper = UpdateWrapper.of(qCategory);
  110. updateWrapper.set(qCategory.title, "bh").where(qCategory.id.eq(6l));
  111. Boolean flag = categoryService.update(updateWrapper);
  112. log.info("更新{}", flag);
  113. }
  114. @Test
  115. @Rollback(value = false)
  116. @Transactional
  117. void updateIgnoreNull() {
  118. Category category = new Category();
  119. category.setId(6l);
  120. // category.setSubTitle("忽略");
  121. //Category category1 = categoryService.update(category, true); //会自动忽略实体空属性。
  122. //category.setTitle("");
  123. Category category1 = categoryService.update(category, true, new String[]{"title"}); //自定义不忽略字段,
  124. log.info("更新{}", category1);
  125. }
  126. @Test
  127. void selectQueryChain() {
  128. QCategory qCategory = QCategory.category;
  129. List<String> categoryList = categoryService.queryChain()
  130. .select(qCategory.title)
  131. .from(qCategory).fetch();
  132. log.info("返回条数{}", categoryList.size());
  133. }
  134. @Test
  135. void selectQuery() {
  136. QCategory qCategory = QCategory.category;
  137. BooleanBuilder booleanBuilder = new BooleanBuilder();
  138. String subTitle = "88";
  139. if (StrUtil.isNotEmpty(subTitle)) {
  140. booleanBuilder.and(qCategory.described.eq("88"));
  141. }
  142. long id = 6l;
  143. if (!StrUtil.isBlankIfStr(id)) {
  144. booleanBuilder.and(qCategory.id.eq(6l));
  145. }
  146. List<Category> categories = categoryService.list(booleanBuilder);
  147. log.info("返回条数{}", categories.size());
  148. }
  149. }

Simplest-JPA

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

新增数据

Service 提供了save,saveBatch,saveOrUpdate 方法

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

删除数据

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

  • removeById(ID id) 根据主键删除数据
  • removeById(Collection<? extends ID> ids) 根据多个主键批量删除数据
  • remove(Collection<T> 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) 根据查询条件来更新数据。
  1. @Test
  2. @Rollback(value = false)
  3. @Transactional
  4. void update() {
  5. QCategory qCategory = QCategory.category;
  6. JPAUpdateClause updateWrapper = UpdateWrapper.of(qCategory);
  7. updateWrapper.set(qCategory.title, "bh").where(qCategory.id.eq(6l));
  8. Boolean flag = categoryService.update(updateWrapper);
  9. log.info("更新{}", flag);
  10. }

updateChain

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

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

Simplest-JPA 查询和分页查询

基础查询

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

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

分页查询

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

链式查询

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

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

queryChain 示列

  1. @Test
  2. void selectQueryChain() {
  3. QCategory qCategory = QCategory.category;
  4. List<String> categoryList = categoryService.queryChain()
  5. .select(qCategory.title)
  6. .from(qCategory)
  7. .fetch();
  8. log.info("返回条数{}", categoryList.size());
  9. }

条件查询

  1. @Test
  2. void selectQueryChainWhere() {
  3. QCategory qCategory = QCategory.category;
  4. List<String> categoryList= categoryService.queryChain()
  5. .select(qCategory.title)
  6. .from(qCategory)
  7. .where(qCategory.id.eq(1l))
  8. .fetch();
  9. log.info("返回条数{}", categoryList.size());
  10. }

分页查询

  1. @Test
  2. void selectQueryChainWhere() {
  3. QCategory qCategory = QCategory.category;
  4. List<String> categoryList = categoryService.queryChain()
  5. .select(qCategory.title)
  6. .from(qCategory)
  7. .where(qCategory.id.eq(1l))
  8. .limit(1)
  9. .fetch();
  10. log.info("返回条数{}", categoryList.size());
  11. }

updateChain 示例

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

queryChain 的方法

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

灵活的 QueryWrapper

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

QueryWrapper 的使用

  1. @SpringBootTest
  2. @Slf4j
  3. public class JpaQueryDSLTest {
  4. @Autowired
  5. private ICategoryService categoryService;
  6. @Autowired
  7. private JPAQueryFactory queryWrapper;
  8. /**
  9. * select() 和 fetch() 的常用写法
  10. * 使用fetch()查询时,数据库没有符合该条件的数据时,返回的是空集合,而不是null
  11. */
  12. /**
  13. * 查询字段-select()
  14. */
  15. @Test
  16. public void fetchColum() {
  17. QCategory qCategory = QCategory.category;
  18. List<String> a = queryWrapper
  19. .select(qCategory.title)
  20. .from(qCategory)
  21. .fetch();
  22. log.info("返回数量{}", a.size());
  23. }
  24. /**
  25. * 查询实体-selectFrom()
  26. */
  27. @Test
  28. public void fetchEntity() {
  29. QCategory qCategory = QCategory.category;
  30. List<Category> categories = queryWrapper.selectFrom(qCategory).fetch();
  31. log.info("返回数量{}", categories.size());
  32. }
  33. /**
  34. * 查询并将结果封装至dto中
  35. */
  36. @Test
  37. public void fetchDto() {
  38. QCategory qCategory = QCategory.category;
  39. List<CategoryDto> categoryDtos = queryWrapper.select(
  40. Projections.bean(CategoryDto.class, qCategory.title)
  41. )
  42. .from(qCategory).fetch();
  43. log.info("返回数量{}", categoryDtos.size());
  44. }
  45. /**
  46. * 去重查询-selectDistinct()
  47. */
  48. @Test
  49. public void fetchDistinct() {
  50. QCategory qCategory = QCategory.category;
  51. List<String> c = queryWrapper
  52. .selectDistinct(qCategory.title)
  53. .from(qCategory)
  54. .fetch();
  55. log.info("返回数量{}", c.size());
  56. }
  57. /**
  58. * 获取首个查询结果-fetchFirst() 单条记录 limit 1
  59. */
  60. @Test
  61. public void fetchFirst() {
  62. QCategory qCategory = QCategory.category;
  63. Category category = queryWrapper
  64. .selectFrom(qCategory)
  65. .fetchFirst();
  66. log.info("返回数量{}", category.toString());
  67. }
  68. /**
  69. * 获取唯一查询结果-fetchOne()
  70. * 当fetchOne()根据查询条件从数据库中查询到多条匹配数据时,会抛`NonUniqueResultException`
  71. */
  72. @Test
  73. public void fetchOne() {
  74. QCategory qCategory = QCategory.category;
  75. Category category = queryWrapper
  76. .selectFrom(qCategory)
  77. .fetchOne();
  78. log.info("返回数量{}", category.toString());
  79. }
  80. /**
  81. * where 子句查询条件的常用写法
  82. */
  83. @Test
  84. public void fetchWhere() {
  85. QCategory qCategory = QCategory.category;
  86. List<Category> categories = queryWrapper
  87. .selectFrom(qCategory)
  88. .where(qCategory.title.eq("更新")
  89. .and(qCategory.subTitle.like('%' + "测试" + '%')))
  90. .fetch();
  91. log.info("返回数量{}", categories.size());
  92. }
  93. /**
  94. * where 动态条件查询
  95. */
  96. /**
  97. * 使用QueryDSL提供的BooleanBuilder来进行查询条件管理。
  98. */
  99. @Test
  100. public void fetchWhereDynamic() {
  101. QCategory qCategory = QCategory.category;
  102. BooleanBuilder builder = new BooleanBuilder();
  103. String title = "a";
  104. if (StrUtil.isNotEmpty(title)) {
  105. builder.and(qCategory.title.eq(title));
  106. }
  107. String subTitle = "";
  108. if (StrUtil.isNotEmpty(subTitle)) {
  109. builder.and(qCategory.subTitle.eq(subTitle));
  110. }
  111. List<Category> categories = queryWrapper
  112. .selectFrom(qCategory)
  113. .where(builder)
  114. .fetch();
  115. log.info("返回数量{}", categories.size());
  116. }
  117. /**
  118. * 复杂的查询关系
  119. */
  120. @Test
  121. public void fetchWhereDynamicComplex() {
  122. QCategory qCategory = QCategory.category;
  123. BooleanBuilder builder = new BooleanBuilder();
  124. builder.or(qCategory.id.eq(1l));
  125. String title = "a";
  126. if (StrUtil.isNotEmpty(title)) {
  127. builder.and(qCategory.title.eq(title));
  128. }
  129. String subTitle = "";
  130. if (StrUtil.isNotEmpty(subTitle)) {
  131. builder.and(qCategory.subTitle.eq(subTitle));
  132. }
  133. List<Category> categories = queryWrapper
  134. .selectFrom(qCategory)
  135. .where(builder)
  136. .fetch();
  137. log.info("返回数量{}", categories.size());
  138. }
  139. /**
  140. * 自定义封装查询的结果集
  141. * JPAQueryFactory查询工厂的select方法可以将Projections方法返回的QBean作为参数,通过Projections的bean方法来构建返回的结果集映射到实体内,有点像Mybatis内的ResultMap的形式,不过内部处理机制肯定是有着巨大差别的!
  142. * <p>
  143. * bean方法第一个参数需要传递一个实体的泛型类型作为返回集合内的单个对象类型,如果QueryDSL查询实体内的字段与DTO实体的字段名字不一样时,可以采用as方法来处理,为查询的结果集指定的字段添加别名,这样就会自动映射到DTO实体内。
  144. */
  145. /**
  146. * 使用Projections的Bean方法
  147. */
  148. @Test
  149. public void fetchBean() {
  150. QCategory qCategory = QCategory.category;
  151. QModule qModule = QModule.module;
  152. List<CategoryDto> categoryDtos = queryWrapper
  153. .select(
  154. Projections.bean(CategoryDto.class
  155. , qCategory.title, qModule.code)
  156. ).from(qCategory, qModule).fetch();
  157. log.info("返回数量{}", categoryDtos.size());
  158. }
  159. /**
  160. * 使用Projections的fields方法
  161. */
  162. @Test
  163. public void fetchFields() {
  164. QCategory qCategory = QCategory.category;
  165. List<CategoryDto> categoryDtos = queryWrapper
  166. .select(
  167. Projections.fields(CategoryDto.class
  168. , qCategory.title)
  169. ).from(qCategory).fetch();
  170. log.info("返回数量{}", categoryDtos.size());
  171. }
  172. /**
  173. * 使用集合的stream转换
  174. * 从方法开始到fetch()结束完全跟QueryDSL没有任何区别,采用了最原始的方式进行返回结果集,但是从fetch()获取到结果集后处理的方式就有所改变了。
  175. * <p>
  176. * fetch()方法返回的类型是泛型List(List),List继承了Collection,完全存在使用Collection内非私有方法的权限,通过调用stream方法可以将集合转换成Stream泛型对象,该对象的map方法可以操作集合内单个对象的转换,具体的转换代码可以根据业务逻辑进行编写。
  177. * <p>
  178. * 在map方法内有个lambda表达式参数tuple,通过tuple对象get方法就可以获取对应select方法内的查询字段。
  179. * ————————————————
  180. */
  181. @Test
  182. public void selectWithStream() {
  183. QCategory qCategory = QCategory.category;
  184. List<CategoryDto> categoryDtos = queryWrapper
  185. .select(qCategory.title, qCategory.subTitle)
  186. .from(qCategory)
  187. .fetch().stream().map(tuple -> {
  188. CategoryDto c = new CategoryDto();
  189. c.setTitle(tuple.get(qCategory.title));
  190. return c;
  191. }).collect(Collectors.toList());
  192. log.info("返回数量{}", categoryDtos.size());
  193. }
  194. @Test
  195. public void findByQuery() {
  196. QCategory qCategory = QCategory.category;
  197. //该Predicate为querydsl下的类,支持嵌套组装复杂查询条件
  198. BooleanBuilder builder = new BooleanBuilder();
  199. String title = "a";
  200. if (StrUtil.isNotEmpty(title)) {
  201. builder.and(qCategory.title.eq(title));
  202. }
  203. String subTitle = "";
  204. if (StrUtil.isNotEmpty(subTitle)) {
  205. builder.and(qCategory.subTitle.eq(subTitle));
  206. }
  207. List<Category> c = categoryService.list(builder);
  208. log.info("条数{}",c.size());
  209. }
  210. @Test
  211. public void findByQueryWrapper(){
  212. QCategory qCategory = QCategory.category;
  213. JPAQueryFactory queryWrapper=QueryWrapper.of();
  214. List<String> c = queryWrapper
  215. .selectDistinct(qCategory.title)
  216. .from(qCategory)
  217. .fetch();
  218. log.info("返回数量{}", c.size());
  219. }
  220. }

实例

  1. 单表查询
  1. @Service
  2. @Transactional
  3. public class UserService {
  4. @Autowired
  5. private JPAQueryFactory queryFactory;
  6. /**
  7. * attention:
  8. * Details:查询user表中的所有记录
  9. */
  10. public List<User> findAll(){
  11. QUser quser = QUser.user;
  12. return queryFactory.selectFrom(quser)
  13. .fetch();
  14. }
  15. /**
  16. * Details:单条件查询
  17. */
  18. public User findOneByUserName(final String userName){
  19. QUser quser = QUser.user;
  20. return queryFactory.selectFrom(quser)
  21. .where(quser.name.eq(userName))
  22. .fetchOne();
  23. }
  24. /**
  25. * Details:单表多条件查询
  26. */
  27. public User findOneByUserNameAndAddress(final String userName, final String address){
  28. QUser quser = QUser.user;
  29. return queryFactory.select(quser)
  30. .from(quser) // 上面两句代码等价与selectFrom
  31. .where(quser.name.eq(userName).and(quser.address.eq(address)))// 这句代码等同于where(quser.name.eq(userName), quser.address.eq(address))
  32. .fetchOne();
  33. }
  34. /**
  35. * Details:使用join查询
  36. */
  37. public List<User> findUsersByJoin(){
  38. QUser quser = QUser.user;
  39. QUser userName = new QUser("name");
  40. return queryFactory.selectFrom(quser)
  41. .innerJoin(quser)
  42. .on(quser.id.intValue().eq(userName.id.intValue()))
  43. .fetch();
  44. }
  45. /**
  46. * Details:将查询结果排序
  47. */
  48. public List<User> findUserAndOrder(){
  49. QUser quser = QUser.user;
  50. return queryFactory.selectFrom(quser)
  51. .orderBy(quser.id.desc())
  52. .fetch();
  53. }
  54. /**
  55. * Details:Group By使用
  56. */
  57. public List<String> findUserByGroup(){
  58. QUser quser = QUser.user;
  59. return queryFactory.select(quser.name)
  60. .from(quser)
  61. .groupBy(quser.name)
  62. .fetch();
  63. }
  64. /**
  65. * Details:删除用户
  66. */
  67. public long deleteUser(String userName){
  68. QUser quser = QUser.user;
  69. return queryFactory.delete(quser).where(quser.name.eq(userName)).execute();
  70. }
  71. /**
  72. * Details:更新记录
  73. */
  74. public long updateUser(final User u, final String userName){
  75. QUser quser = QUser.user;
  76. return queryFactory.update(quser).where(quser.name.eq(userName))
  77. .set(quser.name, u.getName())
  78. .set(quser.age, u.getAge())
  79. .set(quser.address, u.getAddress())
  80. .execute();
  81. }
  82. /**
  83. * Details:使用原生Query
  84. */
  85. public User findOneUserByOriginalSql(final String userName){
  86. QUser quser = QUser.user;
  87. Query query = queryFactory.selectFrom(quser)
  88. .where(quser.name.eq(userName)).createQuery();
  89. return (User) query.getSingleResult();
  90. }
  91. /**
  92. *分页查询所有的实体,根据uIndex字段排序
  93. *
  94. * @return
  95. */
  96. public QueryResults<User> findAllPage(Pageable pageable) {
  97. QUser user = QUser.user;
  98. return jpaQueryFactory
  99. .selectFrom(user)
  100. .orderBy(user.uIndex.asc())
  101. .offset(pageable.getOffset()) //偏移量,计算:offset = ( 当前页 - 1) * 每页条数,这里直接使用的是Pageable中的Offset
  102. .limit(pageable.getPageSize()) //每页大小
  103. .fetchResults(); //获取结果,该结果封装了实体集合、分页的信息,需要这些信息直接从该对象里面拿取即可
  104. }
  105. /**
  106. * 部分字段映射查询
  107. * 投影为UserRes,lambda方式(灵活,类型可以在lambda中修改)
  108. *
  109. * @return
  110. */
  111. public List<UserDTO> findAllUserDto(Pageable pageable) {
  112. QUser user = QUser.user;
  113. List<UserDTO> dtoList = jpaQueryFactory
  114. .select(
  115. user.username,
  116. user.userId,
  117. user.nickName,
  118. user.birthday
  119. )
  120. .from(user)
  121. .offset(pageable.getOffset())
  122. .limit(pageable.getPageSize())
  123. .fetch()
  124. .stream()
  125. .map(tuple -> UserDTO.builder()
  126. .username(tuple.get(user.username))
  127. .nickname(tuple.get(user.nickName))
  128. .userId(tuple.get(user.userId).toString())
  129. .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
  130. .build()
  131. )
  132. .collect(Collectors.toList());
  133. return dtoList;
  134. }
  135. /**
  136. * 部分字段映射查询
  137. * 投影为UserRes,自带的Projections方式,不能转换类型,但是可以使用as转换名字
  138. *
  139. * @return
  140. */
  141. public List<UserDTO> findAllDto2() {
  142. QUser user = QUser.user;
  143. List<UserDTO> dtoList = jpaQueryFactory
  144. .select(
  145. Projections.bean(
  146. UserDTO.class,
  147. user.username,
  148. user.userId,
  149. user.nickName,
  150. user.birthday
  151. )
  152. )
  153. .from(user)
  154. .fetch();
  155. return dtoList;
  156. }
  157. }
  1. 多表查询
  1. /**
  2. * @Description 查询全部
  3. * @Author 程序员三时
  4. * @Date 10:53
  5. * @return java.util.List<com.cs.querydsl.model.Loc>
  6. **/
  7. @Override
  8. public List<Loc> findAll(Loc loc) {
  9. // 使用 QueryDSL 进行查询
  10. QLoc qLoc = QLoc.loc1;
  11. QUser qUser = QUser.user;
  12. // 定于获取条件
  13. BooleanBuilder booleanBuilder = new BooleanBuilder();
  14. // 要查询的条件
  15. if(!StringUtils.isEmpty(loc.getLoc())){
  16. // 放入要查询的条件信息
  17. booleanBuilder.and(qLoc.loc.contains(loc.getLoc()));
  18. }
  19. //连接查询条件(Loc.id = User.id )
  20. booleanBuilder.and(qLoc.id.eq(qUser.id));
  21. // 使用 QueryDSL 进行多表联合查询
  22. QueryResults<Tuple> listResult = queryFactory
  23. .select(QLoc.loc1,QUser.user)
  24. .from(qLoc, qUser)//查询两表
  25. .where(booleanBuilder)
  26. .fetchResults();
  27. //遍历 java8 自带流转换成集合
  28. List<Loc> collect = listResult.getResults().stream().map(tuple -> {
  29. Loc lcs = tuple.get(qLoc);
  30. return lcs;
  31. }).collect(Collectors.toList());
  32. return collect;
  33. }
  34. 部分字段映射的投影查询:
  35. 当使用`@ManyToOne``@ManyToMany`建立关联时:
  36. /**
  37. * 根据部门的id查询用户的基本信息+用户所属部门信息,并且使用UserDeptDTO进行封装返回给前端展示
  38. * @param departmentId
  39. * @return
  40. */
  41. public List<UserDeptDTO> findByDepatmentIdDTO(int departmentId) {
  42. QUser user = QUser.user;
  43. QDepartment department = QDepartment.department;
  44. //直接返回
  45. return jpaQueryFactory
  46. //投影只去部分字段
  47. .select(
  48. user.username,
  49. user.nickName,
  50. user.birthday,
  51. department.deptName,
  52. department.createDate
  53. )
  54. .from(user)
  55. //联合查询
  56. .join(user.department, department)
  57. .where(department.deptId.eq(departmentId))
  58. .fetch()
  59. //lambda开始
  60. .stream()
  61. .map(tuple ->
  62. //需要做类型转换,所以使用map函数非常适合
  63. UserDeptDTO.builder()
  64. .username(tuple.get(user.username))
  65. .nickname(tuple.get(user.nickName))
  66. .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
  67. .deptName(tuple.get(department.deptName))
  68. .deptBirth(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(department.createDate)))
  69. .build()
  70. )
  71. .collect(Collectors.toList());
  72. }
  73. 当使用id建立关联时:
  74. /**
  75. * 根据部门的id查询用户的基本信息+用户所属部门信息,并且使用UserDeptDTO进行封装返回给前端展示
  76. *
  77. * @param departmentId
  78. * @return
  79. */
  80. public List<UserDeptDTO> findByDepatmentIdDTO(int departmentId) {
  81. QUser user = QUser.user;
  82. QDepartment department = QDepartment.department;
  83. //直接返回
  84. return jpaQueryFactory
  85. //投影只去部分字段
  86. .select(
  87. user.username,
  88. user.nickName,
  89. user.birthday,
  90. department.deptName,
  91. department.createDate
  92. )
  93. .from(user, department)
  94. //联合查询
  95. .where(
  96. user.departmentId.eq(department.deptId).and(department.deptId.eq(departmentId))
  97. )
  98. .fetch()
  99. //lambda开始
  100. .stream()
  101. .map(tuple ->
  102. //需要做类型转换,所以使用map函数非常适合
  103. UserDeptDTO.builder()
  104. .username(tuple.get(user.username))
  105. .nickname(tuple.get(user.nickName))
  106. .birthday(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(user.birthday)))
  107. .deptName(tuple.get(department.deptName))
  108. .deptBirth(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(tuple.get(department.createDate)))
  109. .build()
  110. )
  111. .collect(Collectors.toList());
  112. }
  113. 使用 Projections 自定义返回 Bean
  114. /**
  115. * Details:方式一:使用Bean投影
  116. */
  117. public List<PersonIDCardDto> findByDTOUseBean(){
  118. Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
  119. return queryFactory.select(
  120. Projections.bean(PersonIDCardDto.class, QIDCard.iDCard.idNo, QPerson.person.address, QPerson.person.name))
  121. .from(QIDCard.iDCard, QPerson.person)
  122. .where(predicate)
  123. .fetch();
  124. }
  125. /**
  126. * Details:方式二:使用fields来代替setter
  127. */
  128. public List<PersonIDCardDto> findByDTOUseFields(){
  129. Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
  130. return queryFactory.select(
  131. Projections.fields(PersonIDCardDto.class, QIDCard.iDCard.idNo, QPerson.person.address, QPerson.person.name))
  132. .from(QIDCard.iDCard, QPerson.person)
  133. .where(predicate)
  134. .fetch();
  135. }
  136. /**
  137. * Details:方式三:使用构造方法,注意构造方法中属性的顺序必须和构造器中的顺序一致
  138. */
  139. public List<PersonIDCardDto> findByDTOUseConstructor(){
  140. Predicate predicate = (QPerson.person.id.intValue()).eq(QIDCard.iDCard.person.id.intValue());
  141. return queryFactory.select(
  142. Projections.constructor(PersonIDCardDto.class, QPerson.person.name, QPerson.person.address, QIDCard.iDCard.idNo))
  143. .from(QIDCard.iDCard, QPerson.person)
  144. .where(predicate)
  145. .fetch();
  146. }

rest-apiV2.0.0升级为simplest-api开源框架生态之simplest-jpa发布的更多相关文章

  1. 【实践】如何利用tensorflow的object_detection api开源框架训练基于自己数据集的模型(Windows10系统)

    如何利用tensorflow的object_detection api开源框架训练基于自己数据集的模型(Windows10系统) 一.环境配置 1. Python3.7.x(注:我用的是3.7.3.安 ...

  2. NetCore 3.0 中使用Swagger生成Api说明文档及升级报错原因

    认识Swagger Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.总体目标是使客户端和文件系统作为服务器以同样的速度来更新.文件的方法,参 ...

  3. Linux下Oracle 10.2.0.1升级到10.2.0.4总结

    最近部署测试环境时,将测试环境ORACLE数据库从10.2.0.1升级到了10.2.0.4,顺便整理记录一下升级过程. 实验环境: 操作系统:Oracle Linux Server release 5 ...

  4. WPF NET5 Prism8.0的升级指南

    前言 ​ 曾经我以学习的目的写了关于在.NET Core3.1使用Prism的系列文章.NET Core 3 WPF MVVM框架 Prism系列文章索引,也谢谢大家的支持,事实上当初的版本则是Pri ...

  5. NGINX Ingress控制器1.0.0升级迁移文档(翻译)

    Ingress 是什么 Ingress 是对k8s集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP. Ingress 可以提供负载均衡.SSL 终结和基于名称的虚拟托管. 最近 ...

  6. Apache ShardingSphere 5.0.0 内核优化及升级指南

    经过近两年时间的优化和打磨,Apache ShardingSphere 5.0.0 GA 版终于在本月正式发布,相比于 4.1.1 GA 版,5.0.0 GA 版在内核层面进行了大量的优化.首先,基于 ...

  7. MVC3.0 项目升级到 MVC4.0

    按照 http://www.asp.net/whitepapers/mvc4-release-notes#_Toc303253806 的步骤 第一步:修改web.config 注意,默认的MVC3网站 ...

  8. 系统补丁更新导致MVC3.0.0升级到3.0.1的问题解决

    在更新了系统补丁之后,会不知觉的将MVC3.0.0升级到MVC3.0.1的问题,解决的思路如下: 1.全部MVC引用使用NuGet进行包的管理. 2.单独把MVC库抽离出来,然后放在单独的项目文件夹, ...

  9. Asp.net Core 1.0.1升级到Asp.net Core 1.1.0 Preview版本发布到Windows Server2008 R2 IIS中的各种坑

    Asp.net Core 1.0.1升级到Asp.net Core 1.1.0后,程序无法运行了 解决方案:在project.json中加入runtime节点 "runtimes" ...

  10. web2.0最全的国外API应用集合

    web2.0最全的国外API应用集合 原文地址:http://www.buguat.com/post/98.html 2.0时代,越来越多的API被大家广泛应用,如果你还不了解API是何物,请看这里的 ...

随机推荐

  1. MySQL百万数据深度分页优化思路分析

    业务场景 一般在项目开发中会有很多的统计数据需要进行上报分析,一般在分析过后会在后台展示出来给运营和产品进行分页查看,最常见的一种就是根据日期进行筛选.这种统计数据随着时间的推移数据量会慢慢的变大,达 ...

  2. 2023-04-12:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息。

    2023-04-12:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息. 答案2023-04-12: 主要的过程包括: 打开输入视频文件并查找视频流信 ...

  3. 2020-09-10:java里Object类有哪些方法?

    福哥答案2020-09-10: registerNatives:private+static.getClass:返回此 Object 的运行时类. hashCode:返回该对象的哈希码值.equals ...

  4. 2021-05-01:给定一个有序数组arr,代表坐落在X轴上的点。给定一个正数K,代表绳子的长度。返回绳子最多压中几个点?即使绳子边缘处盖住点也算盖住。

    2021-05-01:给定一个有序数组arr,代表坐落在X轴上的点.给定一个正数K,代表绳子的长度.返回绳子最多压中几个点?即使绳子边缘处盖住点也算盖住. 福大大 答案2021-05-01: 滑动窗口 ...

  5. Alist云盘视频加密助手:支持云盘视频文件加密与在线播放,不用再担心视频文件被和谐了!

    在当前娱乐资源丰富的时代,人们每天都在接触各种视频资源.然而,网盘限速.版权审核.视频分级.少儿不宜等问题经常让人感到困扰.如何在保护隐私的前提下,让视频存储和分享变得更加便捷.安全呢?分享一款实用的 ...

  6. yaml的读写

    yaml文件的读写是真的快,也很简单.代码如下:from ruamel.yaml import YAMLimport os # 读取yaml配置文件def read_yaml(yaml_path): ...

  7. Taurus.mvc .Net Core 微服务开源框架发布V3.1.7:让分布式应用更高效。

    前言: 自首个带微服务版本的框架发布:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 已经过去快1年了,在这近一年的时间里,版本经历了N个版本的迭代. ...

  8. 聊聊Cola-StateMachine轻量级状态机的实现

    背景 在分析Seata的saga模式实现时,实在是被其复杂的 json 状态语言定义文件劝退,我是有点没想明白为啥要用这么来实现状态机:盲猜可能是基于可视化的状态机设计器来定制化流程,更方便快捷且上手 ...

  9. 搭建自动化 Web 页面性能检测系统 —— 实现篇

    我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值.. 本文作者:琉易 liuxianyu.cn 前段时间分享了<搭 ...

  10. ElasticSearch的使用和介绍

    1.概述 功能 Elasticsearch 是一个分布式的 RESTful 搜索和分析引擎,可用来集中存储您的数据,以便您对形形色色.规模不一的数据进行搜索.索引和分析. 例如: 在电商网站搜索商品 ...