最も単純なことは何ですか
simplest はシンプルさと完璧さを追求します。
プロジェクトの迅速な開発のための一連の基本機能を提供し、ユーザーがプロジェクトのニーズに応じて機能を迅速に拡張できるようにすることを目的としています。
もう面倒なことは気にしないでください。仕事は重複しますが、ビジネスに集中してください。
序文
プログラムは10年。複数年にわたるプログラムとして。どのプロジェクトやプログラムにも、繰り返し行わなければならない作業がたくさんあることは承知しています。
私はこの業界に 10 年近く携わっており、たくさんのプログラムを書いてきました。死ぬほどたくさんのプログラムを書いてきました。。。。。
インターネットの黄金時代から現在の低迷までを目撃してください。幸いなことに、私はまだプログラマーの軍隊の一員です。あなたと一緒に戦いましょう!
私の物語《プログラマー3時間》公式アカウントでは皆様とのコミュニケーションをお待ちしております。迷ったときにヒントやヒントを与えられれば幸いです。
関連倉庫
計画 | 序章 | ギティアドレス | ギットハブアドレス |
---|---|---|---|
最も単純な API | フロントエンドとバックエンドの分離プロジェクトは最も単純な API に基づいており、Web Json API 形式に基づいて統合された通信インタラクションを迅速に構築できます。 | https://gitee.com/daTouY/simplest-api/tree/main/ | https://github.com/coder-amiao/simplest-api |
最も単純な-jpa | QueryDSL 構文の柔軟で強力な QueryWrapper に基づいた、チェーンされた QueryChain は強力でクエリーを自由に組み合わせることができ、ネイティブ SQL を書くのと同じように Java API に基づいてデータをクエリします。これは非常にエレガントです。 | https://gitee.com/daTouY/simplest-jpa | https://github.com/coder-amiao/simplest-jpa |
最も単純なブート | ビジネス共通のエコロジーコアコンポーネント | https://gitee.com/daTouY/simplest-boot.git | https://github.com/coder-amiao/simplest-boot |
最も単純な管理者 | RABC 許可モデルに基づいて、豊富な機能と最新のテクノロジー スタック Vue3.3 + Vite4 + TS + Pinia + Element-Plus 管理バックグラウンド スキャフォールディングを備えています。すぐに使えるカスタマイズ可能なコード生成テンプレートを使用すると、ビジネス開発に集中できます。 | https://gitee.com/daTouY/simplest-admin.git | https://github.com/coder-amiao/simplest-admin |
simplest-jpa
ここでは主に使い方を紹介します。
クイックスタート
プロジェクト pom に依存関係を導入する
<dependency>
<groupId>cn.soboys</groupId>
<artifactId>simplest-jpa-spring-boot-starter</artifactId>
<version>1.0.1</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</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
データベーステーブルに対応する対応エンティティを定義します。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<Module> modules=new HashSet<>();
/**
* 额外其他属性
* @Transient 解绑和数据联系。属于实体类属性
*/
@Transient
private String other;
}
対応するクエリEntityPathを生成する
QueryDSL に基づくAPTテクノロジーは、Maven の pom.xml に対応するプラグインを導入します。
<plugin>
<!--因为QueryDsl是类型安全的,所以还需要加上Maven APT plugin,使用 APT 自动生成Q类:-->
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
次に、Maven 経由でプロジェクトをコンパイルします。
対応するクエリEntityPahtエンティティは、指定したディレクトリに生成されます
単純なクエリ
- 独自の
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<Category, Long{
}
- 独自の
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<Category,Long> {
}
実装クラス
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<CategoryRepository, Category, Long> implements ICategoryService {
}
このようにして、サービスにはすべての運用データを追加、削除、変更、確認する基本的な方法が備わっています。
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> 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<Category> 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<Long> 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<String> 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<Category> categories = categoryService.list(booleanBuilder);
log.info("返回条数{}", categories.size());
}
}
最も単純な 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 に基づいて更新されます。カスタム無視 NULL 値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
ユーザーが連鎖操作を行う際に便利な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();
}
最も単純な JPA クエリとページング クエリ
基本的なクエリ
Simplest-jpa は、Service
データベース データをクエリするための次の関数を提供します。
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: チェーンの更新
クエリチェーンのデモ
@Test
void selectQueryChain() {
QCategory qCategory = QCategory.category;
List<String> categoryList = categoryService.queryChain()
.select(qCategory.title)
.from(qCategory)
.fetch();
log.info("返回条数{}", categoryList.size());
}
条件付きクエリ
@Test
void selectQueryChainWhere() {
QCategory qCategory = QCategory.category;
List<String> 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<String> categoryList = categoryService.queryChain()
.select(qCategory.title)
.from(qCategory)
.where(qCategory.id.eq(1l))
.limit(1)
.fetch();
log.info("返回条数{}", categoryList.size());
}
アップデートチェーンの例
@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
これは 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<String> a = queryWrapper
.select(qCategory.title)
.from(qCategory)
.fetch();
log.info("返回数量{}", a.size());
}
/**
* 查询实体-selectFrom()
*/
@Test
public void fetchEntity() {
QCategory qCategory = QCategory.category;
List<Category> categories = queryWrapper.selectFrom(qCategory).fetch();
log.info("返回数量{}", categories.size());
}
/**
* 查询并将结果封装至dto中
*/
@Test
public void fetchDto() {
QCategory qCategory = QCategory.category;
List<CategoryDto> 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<String> 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<Category> 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<Category> 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<Category> categories = queryWrapper
.selectFrom(qCategory)
.where(builder)
.fetch();
log.info("返回数量{}", categories.size());
}
/**
* 自定义封装查询的结果集
* JPAQueryFactory查询工厂的select方法可以将Projections方法返回的QBean作为参数,通过Projections的bean方法来构建返回的结果集映射到实体内,有点像Mybatis内的ResultMap的形式,不过内部处理机制肯定是有着巨大差别的!
* <p>
* bean方法第一个参数需要传递一个实体的泛型类型作为返回集合内的单个对象类型,如果QueryDSL查询实体内的字段与DTO实体的字段名字不一样时,可以采用as方法来处理,为查询的结果集指定的字段添加别名,这样就会自动映射到DTO实体内。
*/
/**
* 使用Projections的Bean方法
*/
@Test
public void fetchBean() {
QCategory qCategory = QCategory.category;
QModule qModule = QModule.module;
List<CategoryDto> 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<CategoryDto> categoryDtos = queryWrapper
.select(
Projections.fields(CategoryDto.class
, qCategory.title)
).from(qCategory).fetch();
log.info("返回数量{}", categoryDtos.size());
}
/**
* 使用集合的stream转换
* 从方法开始到fetch()结束完全跟QueryDSL没有任何区别,采用了最原始的方式进行返回结果集,但是从fetch()获取到结果集后处理的方式就有所改变了。
* <p>
* fetch()方法返回的类型是泛型List(List),List继承了Collection,完全存在使用Collection内非私有方法的权限,通过调用stream方法可以将集合转换成Stream泛型对象,该对象的map方法可以操作集合内单个对象的转换,具体的转换代码可以根据业务逻辑进行编写。
* <p>
* 在map方法内有个lambda表达式参数tuple,通过tuple对象get方法就可以获取对应select方法内的查询字段。
* ————————————————
*/
@Test
public void selectWithStream() {
QCategory qCategory = QCategory.category;
List<CategoryDto> 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<Category> c = categoryService.list(builder);
log.info("条数{}",c.size());
}
@Test
public void findByQueryWrapper(){
QCategory qCategory = QCategory.category;
JPAQueryFactory queryWrapper=QueryWrapper.of();
List<String> c = queryWrapper
.selectDistinct(qCategory.title)
.from(qCategory)
.fetch();
log.info("返回数量{}", c.size());
}
}
例
- 単一テーブルクエリ
@Service
@Transactional
public class UserService {
@Autowired
private JPAQueryFactory queryFactory;
/**
* attention:
* Details:查询user表中的所有记录
*/
public List<User> 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<User> 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<User> findUserAndOrder(){
QUser quser = QUser.user;
return queryFactory.selectFrom(quser)
.orderBy(quser.id.desc())
.fetch();
}
/**
* Details:Group By使用
*/
public List<String> 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<User> 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<UserDTO> findAllUserDto(Pageable pageable) {
QUser user = QUser.user;
List<UserDTO> 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<UserDTO> findAllDto2() {
QUser user = QUser.user;
List<UserDTO> dtoList = jpaQueryFactory
.select(
Projections.bean(
UserDTO.class,
user.username,
user.userId,
user.nickName,
user.birthday
)
)
.from(user)
.fetch();
return dtoList;
}
}
- 複数テーブルのクエリ
/**
* @Description 查询全部
* @Author 程序员三时
* @Date 10:53
* @return java.util.List<com.cs.querydsl.model.Loc>
**/
@Override
public List<Loc> 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<Tuple> listResult = queryFactory
.select(QLoc.loc1,QUser.user)
.from(qLoc, qUser)//查询两表
.where(booleanBuilder)
.fetchResults();
//遍历 java8 自带流转换成集合
List<Loc> 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<UserDeptDTO> 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<UserDeptDTO> 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<PersonIDCardDto> 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<PersonIDCardDto> 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<PersonIDCardDto> 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();
}