В предыдущей статье мы представили основы использования JPA,«Spring Boot (3): ORM Framework JPA и пул соединений Hikari», В этой статье мы представим QueryDSL, который добавляет крылья JPA от начального до расширенного.
1. Введение
Нельзя отрицать, что использование JPA очень удобно, чрезвычайно упрощенная конфигурация, нужно использовать только аннотации, без какого-либо файла конфигурации xml, семантика проста и понятна, но все вышеперечисленное основано на предпосылке запроса к одной таблице. , вы можете использовать методы, предоставляемые JPA по умолчанию, для простого и легкого выполнения операций CRUD.
Но если это включает в себя многотабличный динамический запрос, функция JPA кажется немного растянутой, хотя мы можем использовать аннотации.@Query
Пишите SQL или HQL в этой аннотации к сплайсированным строкам, а читаемость строки после сплайсинга очень плохая, конечно JPA для нас тоже доступен.Specification
Для этого из личного опыта, хотя читабельность неплохая, когда новички начинают,Predicate
иCriteriaBuilder
Подсчитано, что метод использования может отговорить многих людей, и если вы напрямую выполните SQL-запрос с таблицей, вы получитеObject[]
, какой тип? Как называется поле? Ничего из этого нельзя получить интуитивно, и нам нужно вручнуюObject[]
сопоставить с тем, что нам нужноModel
Внутри класса этот опыт, несомненно, крайне плохой.
Это все закончилось после рождения QueryDSL.Синтаксис QueryDSL очень похож на SQL, код очень читабелен, введение красиво, и он сильно интегрирован с JPA, без избыточной настройки, что очень хорошо от личного автора. опыт. Можно сказать, что пока вы умеете писать SQL, вы можете достичь начального уровня, взглянув на пример кода.
2. Введение в QueryDSL
QueryDSL — это очень активный проект с открытым исходным кодом. В настоящее время на Github выпущена 251 версия Release. Последняя версия — 4.2.1, и она поддерживается Querydsl Google Group и StackOverflow.
QueryDSL — это платформа, которую можно использовать для построения статически типизированных SQL-подобных запросов. Вместо того, чтобы писать запрос в виде встроенной строки или преобразовывать его в файл XML, запрос можно создать с помощью API, такого как QueryDSL.
Например, преимущества использования API по сравнению с простыми строками:
-
Автодополнение кода в IDE
-
Несколько синтаксически недопустимых запросов
-
Ссылочные типы полей и атрибуты могут безопасно
-
Улучшенный рефакторинг изменений типа домена
3. QueryDSL с использованием реального боя
3.1 Знакомство с зависимостями Maven
Листинг кода: spring-boot-jpa-querydsl/pom.xml
<!--QueryDSL支持-->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
<!--QueryDSL支持-->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
- Номер версии здесь указывать не нужно, он уже есть в
spring-boot-dependencies
Определяется в проекте.
3.2 Добавить плагин Maven
Этот плагин добавлен, чтобы позволить программе автоматически генерировать тип запроса (сущность запроса, названная как: «Q» + соответствующее имя сущности). В приведенных выше зависимостях этот подключаемый модуль обслуживается querydsl-apt.
Примечание. Если во время использования тип запроса не может быть сгенерирован автоматически, это можно решить, обновив проект с помощью maven (щелкните проект правой кнопкой мыши -> Maven -> Обновить папки).
Листинг кода: spring-boot-jpa-querydsl/pom.xml
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
3.3 Обновления и удаления
JPA предоставил нам очень простой способ обновления и удаления, нам не нужно использовать QueryDSL для обновления и удаления, но вот его использование для справки:
Список кодов: Spring-boot-jpa-querydsl / src / main / java / com / springboot / springbootjpaquerydsl / Service / Impa / userviceimpl.java
@Override
public Long update(String id, String nickName) {
QUserModel userModel = QUserModel.userModel;
// 更新
return queryFactory.update(userModel).set(userModel.nickName, nickName).where(userModel.id.eq(id)).execute();
}
@Override
public Long delete(String id) {
QUserModel userModel = QUserModel.userModel;
// 删除
return queryFactory.delete(userModel).where(userModel.id.eq(id)).execute();
}
3.2 Запрос
Можно сказать, что QueryDSL очень дорог с точки зрения запросов, таких как некоторые связанныеselect()
иfetch()
Обычно используется следующая письменность:
Листинг кода: spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
@Override
public List<String> selectAllNameList() {
QUserModel userModel = QUserModel.userModel;
// 查询字段
return queryFactory.select(userModel.nickName).from(userModel).fetch();
}
@Override
public List<UserModel> selectAllUserModelList() {
QUserModel userModel = QUserModel.userModel;
// 查询实体
return queryFactory.selectFrom(userModel).fetch();
}
@Override
public List<UserDTO> selectAllUserDTOList() {
QUserModel userModel = QUserModel.userModel;
QLessonModel lessonModel = QLessonModel.lessonModel;
// 连表查询实体并将结果封装至DTO
return queryFactory
.select(
Projections.bean(UserDTO.class, userModel.nickName, userModel.age, lessonModel.startDate, lessonModel.address, lessonModel.name)
)
.from(userModel)
.leftJoin(lessonModel)
.on(userModel.id.eq(lessonModel.userId))
.fetch();
}
@Override
public List<String> selectDistinctNameList() {
QUserModel userModel = QUserModel.userModel;
// 去重查询
return queryFactory.selectDistinct(userModel.nickName).from(userModel).fetch();
}
@Override
public UserModel selectFirstUser() {
QUserModel userModel = QUserModel.userModel;
// 查询首个实体
return queryFactory.selectFrom(userModel).fetchFirst();
}
@Override
public UserModel selectUser(String id) {
QUserModel userModel = QUserModel.userModel;
// 查询单个实体,如果结果有多个,会抛`NonUniqueResultException`。
return queryFactory.selectFrom(userModel).fetchOne();
}
3.4 Комплексные операции запросов
Простые запросы перечислены выше, но на практике мы столкнулись с довольно сложными операциями, такими как подпросы, несколько запросов на условиях и несколькими таблицами. Примеры использования следующие:
Листинг кода: spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/LessonServiceImpl.java
@Service
public class LessonServiceImpl implements LessonService {
@Autowired
JPAQueryFactory queryFactory;
@Override
public List<LessonModel> findLessonList(String name, Date startDate, String address, String userId) throws ParseException {
QLessonModel lessonModel = QLessonModel.lessonModel;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// 多条件查询示例
return queryFactory.selectFrom(lessonModel)
.where(
lessonModel.name.like("%" + name + "%")
.and(lessonModel.address.contains(address))
.and(lessonModel.userId.eq(userId))
.and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date()))
)
.fetch();
}
@Override
public List<LessonModel> findLessonDynaList(String name, Date startDate, String address, String userId) throws ParseException {
QLessonModel lessonModel = QLessonModel.lessonModel;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// 动态查询示例
BooleanBuilder builder = new BooleanBuilder();
if (!StringUtils.isEmpty(name)){
builder.and(lessonModel.name.like("%" + name + "%"));
}
if (startDate != null) {
builder.and(lessonModel.startDate.between(simpleDateFormat.parse("2018-12-31 00:00:00"), new Date()));
}
if (!StringUtils.isEmpty(address)) {
builder.and(lessonModel.address.contains(address));
}
if (!StringUtils.isEmpty(userId)) {
builder.and(lessonModel.userId.eq(userId));
}
return queryFactory.selectFrom(lessonModel).where(builder).fetch();
}
@Override
public List<LessonModel> findLessonSubqueryList(String name, String address) {
QLessonModel lessonModel = QLessonModel.lessonModel;
// 子查询示例,并无实际意义
return queryFactory.selectFrom(lessonModel)
.where(lessonModel.name.in(
JPAExpressions
.select(lessonModel.name)
.from(lessonModel)
.where(lessonModel.address.eq(address))
))
.fetch();
}
}
3.5 Агрегатные функции Mysql
QueryDSL имеет встроенные некоторые часто используемые функции агрегации Mysql. Если вы столкнулись с функциями агрегации, не предоставляемыми QueryDSL, вам не нужно паниковать. QueryDSL предоставляет намExpressions
Этот класс, мы можем использовать этот класс для сращивания вручную, в следующем примере:
Листинг кода: spring-boot-jpa-querydsl/src/main/java/com/springboot/springbootjpaquerydsl/service/impl/UserServiceImpl.java
@Override
public String mysqlFuncDemo(String id, String nickName, int age) {
QUserModel userModel = QUserModel.userModel;
// Mysql 聚合函数示例
// 聚合函数-avg()
Double averageAge = queryFactory.select(userModel.age.avg()).from(userModel).fetchOne();
// 聚合函数-sum()
Integer sumAge = queryFactory.select(userModel.age.sum()).from(userModel).fetchOne();
// 聚合函数-concat()
String concat = queryFactory.select(userModel.nickName.concat(nickName)).from(userModel).fetchOne();
// 聚合函数-contains()
Boolean contains = queryFactory.select(userModel.nickName.contains(nickName)).from(userModel).where(userModel.id.eq(id)).fetchOne();
// 聚合函数-DATE_FORMAT()
String date = queryFactory.select(Expressions.stringTemplate("DATE_FORMAT({0},'%Y-%m-%d')", userModel.createDate)).from(userModel).fetchOne();
return null;
}
4. Резюме
Введение в querydsl закончено, я не знаю, читали ли вы пример выше. Есть ли ощущение, что вы читаете SQL напрямую, и этот SQL все еще использует мысли OOM, дайте исходному спящему режиму не делать ничего хорошего? Довольно идеальное решение, прост в эксплуатации, но не нужно писать SQL, на самом деле мы работаем или классы объектов.