В нашем обычном процессе разработки проектов исключения, как правило, являются наиболее хлопотными проблемами для программистов.Выброс, захват и обработка исключений включают не только откат транзакций, но и возврат интерфейсных сообщений-напоминаний. Итак, как мы можем спроектировать, чтобы решить две вышеупомянутые болевые точки? Можем ли мы единообразно обработать бизнес-логику, а затем предоставить соответствующее содержимое напоминания об исключении во внешнем интерфейсе?
Цели этой главы
на основеSpringBoot
Платформа выстраивает унифицированную обработку исключений бизнес-логики и форматирует содержимое сообщений об исключениях.
Благосостояние здесь
Облачный специальный сервер Tencent 10 юаней / месяц,Нажмите, чтобы присоединиться
Тема изучения основных технологий SpringBoot корпоративного уровня
Тема | Название темы | Тематическое описание |
---|---|---|
001 | Основная технология Spring Boot | Объяснить некоторые основные компоненты SpringBoot на уровне предприятия. |
002 | Исходный код главы о технологии Spring Boot | Каждая статья в кратком справочнике по основным технологиям Spring Boot соответствует исходному коду облака кода. |
003 | Базовая технология Spring Cloud | Всестороннее объяснение основных технологий Spring Cloud |
004 | Исходный код главы о технологии Spring Cloud | Каждая статья в кратком описании основных технологий Spring Cloud соответствует исходному коду. |
005 | Базовая технология QueryDSL | Всесторонне объясните основную технологию QueryDSL и интегрируйте SpringDataJPA на основе SpringBoot. |
006 | Базовая технология SpringDataJPA | Всестороннее объяснение основной технологии Spring Data JPA |
007 | Каталог изучения основных технологий SpringBoot | Каталог обучения системе Springboot, обратите внимание! ! ! |
Построить проект
Мы извлекаем основную часть обработки логического исключения как отдельныйjar
Для справки другими модулями создайте проект вparent
проектpom.xml
Добавьте зависимости для общего пользования, содержимое конфигурации выглядит следующим образом:
<dependencies>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--测试模块依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
После создания проекта, кроме.idea
,iml
,pom.xml
Зарезервировано, другие удаляются.
Основной подмодуль обработки исключений
Мы создаемspringboot-core-exception
Подмодуль , в котором пользовательскийLogicException
Класс исключений времени выполнения, унаследованныйRuntimeException
и переопределить конструктор, код выглядит так:
/**
* 自定义业务逻辑异常类
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午2:38
* 码云:http://git.oschina.net/jnyqy
* ========================
*
* @author yuqiyu
*/
public class LogicException extends RuntimeException {
/**
* 日志对象
*/
private Logger logger = LoggerFactory.getLogger(LogicException.class);
/**
* 错误消息内容
*/
protected String errMsg;
/**
* 错误码
*/
protected String errCode;
/**
* 格式化错误码时所需参数列表
*/
protected String[] params;
/**
* 获取错误消息内容
* 根据errCode从redis内获取未被格式化的错误消息内容
* 并通过String.format()方法格式化错误消息以及参数
*
* @return
*/
public String getErrMsg() {
return errMsg;
}
/**
* 获取错误码
*
* @return
*/
public String getErrCode() {
return errCode;
}
/**
* 获取异常参数列表
*
* @return
*/
public String[] getParams() {
return params;
}
/**
* 构造函数设置错误码以及错误参数列表
*
* @param errCode 错误码
* @param params 错误参数列表
*/
public LogicException(String errCode, String... params) {
this.errCode = errCode;
this.params = params;
//获取格式化后的异常消息内容
this.errMsg = ErrorMessageTools.getErrorMessage(errCode, params);
//错误信息
logger.error("系统遇到如下异常,异常码:{}>>>异常信息:{}", errCode, errMsg);
}
}
Необходимо передать два параметра внутри переопределенного конструктораerrCode
,params
, целью которого является инициализация глобальных переменных внутри класса.
-
errCode
: Это поле является соответствующим кодом исключения. Мы создадим перечисление для хранения кода ошибки исключения в содержании последующей статьи иerrCode
Это значение соответствующей строки перечисления. -
params
: Вот соответствующийerrCode
Список параметров, необходимых для описания значения строки. -
errMsg
: Отформатированное описание сообщения об исключении бизнес-логики, мы можем видеть вызов в конструктореErrorMessageTools.getErrorMessage(errCode,params);
, функция этого метода состоит в том, чтобы получить неформатированное описание исключения в базе данных с помощью кода исключения и отформатировать описание сообщения об исключении с помощью переданных параметров.
Цель создания основного пакета исключений — позволить другим модулям напрямую добавлять зависимости Как получить содержимое описания исключения?
Определить интерфейс получения сообщения об исключении
мы вspringboot-exception-core
Добавьте интерфейс к модулюLogicExceptionMessage
Этот интерфейс предоставляет неформатированное сообщение об исключении для получения неформатированного сообщения об исключении по коду аномалии, и интерфейс определяется следующим образом:
/**
* 逻辑异常接口定义
* 使用项目需要实现该接口方法并提供方法实现
* errCode对应逻辑异常码
* getMessage返回字符串为逻辑异常消息内容
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午2:41
* 码云:http://git.oschina.net/jnyqy
* ========================
* @author yuqiyu
*/
public interface LogicExceptionMessage {
/**
* 获取异常消息内容
* @param errCode 错误码
* @return
*/
public String getMessage(String errCode);
}
загружать по мере необходимостиspringboot-exception-core
В зависимом проекте создайте реализацию класса сущностей.LogicExceptionMessage
интерфейс и переопределениеgetMessage(String errCode)
метод, который мы можем передатьspring IOC
Получите экземпляр класса реализации для работы и получения данных, на которые мы будем ссылаться, когда будем писать модуль исключений ниже.
Класс утилиты сообщения об исключении формата
Вернемся назад и посмотрим на класс инструмента сообщения об исключении в формате конструктора.ErrorMessageTools
, который предоставляется в классе инструментовgetErrorMessage
Метод используется для получения форматированного описания сообщения об исключении.Реализация кода выглядит следующим образом:
/**
* 异常消息描述格式化工具类
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午2:40
* 码云:http://git.oschina.net/jnyqy
* ========================
*
* @author yuqiyu
*/
public class ErrorMessageTools {
/**
* 异常消息获取
*
* @param errCode 异常消息码
* @param params 格式化异常参数所需参数列表
* @return
*/
public static String getErrorMessage(String errCode, Object... params) {
//获取业务逻辑消息实现
LogicExceptionMessage logicExceptionMessage = SpringBeanTools.getBean(LogicExceptionMessage.class);
if (ObjectUtils.isEmpty(logicExceptionMessage)) {
try {
throw new Exception("请配置实现LogicExceptionMessage接口并设置实现类被SpringIoc所管理。");
} catch (Exception e) {
e.printStackTrace();
}
}
//获取错误消息内容
String errMsg = logicExceptionMessage.getMessage(errCode);
//格式化错误消息内容
return ObjectUtils.isEmpty(params) ? errMsg : String.format(errMsg, params);
}
}
Примечание. Поскольку все наши классы инструментов представляют собой вызовы статических методов, их нельзя использовать напрямую.
Spring IOC
Получено путем внедрения аннотацийLogicExceptionMessage
пример.
Поскольку экземпляр нельзя внедрить, вgetErrorMessage
метод, мы передаем класс инструментаSpringBeanTools
получитьApplicationContext
Экземпляр контекста, а затем получить указанный тип через контекстBean
; получитьLogicExceptionMessage
вызов после экземпляраgetMessage
метод, в зависимости от поступающихerrCode
Вы можете получить описание неформатированного исключения непосредственно из экземпляра класса реализации интерфейса!
Конечно, класс реализации может быть
Redis
,Map集合
,数据库
,文本
в качестве источника данных.
Пройти после получения неформатированного описания исключенияString.format
Метод и переданные параметры могут напрямую получить отформатированную строку, например:
未格式化异常消息 => 用户:%s已被冻结,无法操作.
格式化代码 => String.format("%s已被冻结,无法操作.","恒宇少年");
格式化后效果 => 用户:恒宇少年已被冻结,无法操作.
Конкретное значение специального символа форматирования можно просмотретьString.format
Документация, как получитьApplicationContext
объект контекста, пожалуйста, посетитеГлава 32: Как получить объект applicationContext проекта SpringBootПроверять.
мы возвращаемсяLogicException
внутри конструктора, когдаerrMsg
Значение, соответствующее полю, будет форматированным описанием сообщения об исключении, которое мы вызываем извне.getErrMsg
Метод может напрямую получить описание исключения.
До сих пор у нас естьspringboot-exception-core
Написание кода модуля завершено, давайте посмотрим, как использовать наше пользовательское исключение бизнес-логики и получить отформатированное описание сообщения об исключении.
пример модуля исключений
на основеparent
Давайте создадимspringboot-exception-example
submodule, в проект необходимо добавить некоторые дополнительные зависимости конфигурации, и, конечно же, нам нужно добавить нашspringboot-exception-core
Зависимость добавляется,pom.xml
Содержимое конфигурационного файла следующее:
<dependencies>
<!--异常核心依赖-->
<dependency>
<groupId>com.hengyu</groupId>
<artifactId>springboot-exception-core</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--spring data jpa依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--druid依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
Давайте настроим наш пример проектаapplication.yml
Конфигурация, требуемая файлом, выглядит следующим образом:
spring:
application:
name: springboot-exception-core
#数据源配置
datasource:
druid:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
properties:
hibernate:
#配置显示sql
show_sql: true
#配置格式化sql
format_sql: true
Выше мы упоминалиLogicExceptionMessage
Полученный контент может быть прочитан из различных источников данных. Мы по-прежнему используем базу данных для его чтения. Рекомендуется, чтобы формальная среда была размещена вredis
В кэше! ! !
таблица информации об исключениях
Затем создайте таблицу информации об исключении в базе данных.sys_exception_info
, утверждение выглядит следующим образом:
DROP TABLE IF EXISTS `sys_exception_info`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `sys_exception_info` (
`EI_ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键自增',
`EI_CODE` varchar(30) DEFAULT NULL COMMENT '异常码',
`EI_MESSAGE` varchar(50) DEFAULT NULL COMMENT '异常消息内容',
PRIMARY KEY (`EI_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='系统异常基本信息';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `sys_exception_info`
--
LOCK TABLES `sys_exception_info` WRITE;
/*!40000 ALTER TABLE `sys_exception_info` DISABLE KEYS */;
INSERT INTO `sys_exception_info` VALUES (1,'USER_NOT_FOUND','用户不存在.'),(2,'USER_STATUS_FAILD','用户状态异常.');
/*!40000 ALTER TABLE `sys_exception_info` ENABLE KEYS */;
UNLOCK TABLES;
мы проходимspring-data-jpa
Чтобы добиться чтения данных, создайте соответствующую таблицу данных нижеEntity
.
объект информации об исключении
/**
* 系统异常基本信息实体
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午3:35
* 码云:http://git.oschina.net/jnyqy
* ========================
* @author yuqiyu
*/
@Data
@Entity
@Table(name = "sys_exception_info")
public class ExceptionInfoEntity implements Serializable{
/**
* 异常消息编号
*/
@Id
@GeneratedValue
@Column(name = "EI_ID")
private Integer id;
/**
* 异常消息错误码
*/
@Column(name = "EI_CODE")
private String code;
/**
* 异常消息内容
*/
@Column(name = "EI_MESSAGE")
private String message;
}
Интерфейс данных об исключениях
/**
* 异常数据接口定义
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午3:34
* 码云:http://git.oschina.net/jnyqy
* ========================
* @author yuqiyu
*/
public interface ExceptionRepository
extends JpaRepository<ExceptionInfoEntity,Integer>
{
/**
* 根据异常码获取异常配置信息
* @param code 异常码
* @return
*/
ExceptionInfoEntity findTopByCode(String code);
}
через интерфейс данныхspring-data-jpa
Метод запроса, черезerrCode
Прочитайте содержимое объекта информации об исключении.
Используется, когда исключения заканчиваются во время разработкиerrCode
Обычно хранится в типе перечисления или постоянном интерфейсе, здесь мы выбираем относительно сильную расширяемость.枚举类型
, код показан ниже:
/**
* 错误码枚举类型
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午3:25
* 码云:http://git.oschina.net/jnyqy
* ========================
* @author yuqiyu
*/
public enum ErrorCodeEnum {
/**
* 用户不存在.
*/
USER_NOT_FOUND,
/**
* 用户状态异常.
*/
USER_STATUS_FAILD,
//...添加其他错误码
}
Элементы содержимого перечисления кода исключения должны быть изменены в соответствии с таблицей информации об исключении базы данных, что может гарантировать наличие соответствующей информации в базе данных при возникновении исключения.
Определение класса реализации LogicExceptionMessage
мы вspringboot-exception-core
Добавлен в основной модульLogicExceptionMessage
Определение интерфейса, нам нужно реализовать интерфейсgetMessage
Модуль ядра метода, чтобы можно было получить соответствующую информацию об исключении в базе данных. Класс реализации выглядит следующим образом:
/**
* 业务逻辑异常消息获取实现类
* - 消息可以从数据库内获取
* - 消息可从Redis内获取
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午3:16
* 码云:http://git.oschina.net/jnyqy
* ========================
* @author yuqiyu
*/
@Component
public class LogicExceptionMessageSupport implements LogicExceptionMessage {
/**
* 异常数据接口
*/
@Autowired
private ExceptionRepository exceptionRepository;
/**
* 根据错误码获取错误信息
* @param errCode 错误码
* @return
*/
@Override
public String getMessage(String errCode) {
ExceptionInfoEntity exceptionInfoEntity = exceptionRepository.findTopByCode(errCode);
if(!ObjectUtils.isEmpty(exceptionInfoEntity)) {
return exceptionInfoEntity.getMessage();
}
return "系统异常";
}
}
существуетgetMessage
пройти в методеExceptionRepository
определяется интерфейсом данныхfindTopByCode
Метод получает информацию об исключении для указанного исключения и возвращает неформатированное описание исключения при наличии информации об исключении.
Определение объекта единого возврата
Для интерфейсных проектов (включая проекты фронтального и тылового разделения) при обработке и возврате унифицированного формата мы обычно используем фиксированный метод сущности, чтобы разработчикам было удобнее вызывать интерфейс на фронтенде для парсинга контента. Содержимое формата, возвращаемое, когда система ненормальна или бизнес-логика ненормальна, конечно, это то же самое, что и формат, правильно возвращаемый интерфейсом запроса, но содержимое поля отличается.
Единая возвращаемая сущностьApiResponseEntity<T extends Object>
следующее:
/**
* 接口响应实体
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/9
* Time:下午3:04
* 码云:http://git.oschina.net/jnyqy
* ========================
* @author yuqiyu
*/
@Data
@Builder
public class ApiResponseEntity<T extends Object> {
/**
* 错误消息
*/
private String errorMsg;
/**
* 数据内容
*/
private T data;
}
существуетApiResponseEntity
внутри организации, используяLombok
Шаблон проектирования конструктора@Builder
Аннотацию, объект, который настраивает аннотацию, автоматически.class
Добавьте в файл внутренний класс для реализации шаблона проектирования, и часть автоматически сгенерированного кода выглядит следующим образом:
// ...
public static class ApiResponseEntityBuilder<T> {
private String errorMsg;
private T data;
ApiResponseEntityBuilder() {
}
public ApiResponseEntity.ApiResponseEntityBuilder<T> errorMsg(String errorMsg) {
this.errorMsg = errorMsg;
return this;
}
public ApiResponseEntity.ApiResponseEntityBuilder<T> data(T data) {
this.data = data;
return this;
}
public ApiResponseEntity<T> build() {
return new ApiResponseEntity(this.errorMsg, this.data);
}
public String toString() {
return "ApiResponseEntity.ApiResponseEntityBuilder(errorMsg=" + this.errorMsg + ", data=" + this.data + ")";
}
}
// ...
Пока что мы не добавили конфигурацию, связанную с глобальными исключениями, и конфигурацию глобальных исключений мы используем из предыдущих глав.@ControllerAdvice
реализовать,@ControllerAdvice
Для связанного контента, пожалуйста, посетитеГлава 21: Глобальная ненормальная обработка в проекте Springboot.
Определение глобального уведомления об исключении
В этой главе мы добавим только обработку исключений бизнес-логики.Конкретные коды следующие:
/**
* 控制器异常通知类
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午5:30
* 码云:http://git.oschina.net/jnyqy
* ========================
*
* @author yuqiyu
*/
@ControllerAdvice(annotations = RestController.class)
@ResponseBody
public class ExceptionAdvice {
/**
* logback new instance
*/
Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 处理业务逻辑异常
*
* @param e 业务逻辑异常对象实例
* @return 逻辑异常消息内容
*/
@ExceptionHandler(LogicException.class)
@ResponseStatus(code = HttpStatus.OK)
public ApiResponseEntity<String> logicException(LogicException e) {
logger.error("遇到业务逻辑异常:【{}】", e.getErrCode());
// 返回响应实体内容
return ApiResponseEntity.<String>builder().errorMsg(e.getErrMsg()).build();
}
}
Недавно одноклассник в технической группе спросил меня, так как мы используем
@RestController
Зачем вам здесь настраивать@ResponseBody
?这里给大家一个解释,我们控制器通知确实是监听的@RestController
,и@RestController
Все аннотированные контроллеры возвращаютсяJSON
форматировать данные. Затем, после того, как мы столкнулись с исключением, запрос больше не находится в контроллере и был доставлен в класс уведомлений контроллера, тогда, если наш класс уведомлений также хочет вернутьсяJSON
данные, здесь нужно настроить@ResponseBody
Аннотация для достижения.
Давайте посмотрим на вышеlogicException()
метод, возвращаемое значение этого метода представляет собой унифицированную возвращаемую сущность, определенную нами, цель состоит в том, чтобы вернуть тот же формат, что и правильный запрос при обнаружении исключений бизнес-логики.
-
@ ExceptionHandler
Настроено будет обрабатыватьLogicException
типа исключения, то есть всякий раз, когда система сталкивается сLogicException
Если исключение выброшено и выброшено в контроллер, будет вызван этот метод. -
@ResponseStatus
Возвращаемое значение статуса настроено, потому что, когда мы сталкиваемся с исключениями бизнес-логики, то, что нужно интерфейсу, — это не ошибка 500, а статус 200.JSON
Описание бизнес-исключения.
используется, когда метод возвращает构造者设计模式
и пройти сообщение исключенияerrorMsg()
Метод, таким образом, достижение поляerrorMsg
назначение.
контрольная работа
Кодирование, связанное с исключением, завершено, давайте создадим тестовый контроллер, чтобы смоделировать, как система возвращается, когда происходит бизнес-логика? Содержание тестового контроля следующее:
/**
* 测试控制器
* ========================
* Created with IntelliJ IDEA.
* User:恒宇少年
* Date:2018/1/7
* Time:下午3:12
* 码云:http://git.oschina.net/jnyqy
* ========================
*
* @author yuqiyu
*/
@RestController
public class IndexController {
/**
* 首页方法
*
* @return
*/
@RequestMapping(value = "/index")
public ApiResponseEntity<String> index() throws LogicException {
/**
* 模拟用户不存在
* 抛出业务逻辑异常
*/
if (true) {
throw new LogicException(ErrorCodeEnum.USER_STATUS_FAILD.toString());
}
return ApiResponseEntity.<String>builder().data("this is index mapping").build();
}
}
В соответствии со значением приведенного выше кода, когда мы посещаем/index
происходит, когдаUSER_STATUS_FAILD
Бизнес-логика является ненормальной, в соответствии с нашей предыдущей глобальной ненормальной конфигурацией и унифицированным экземпляром возвращаемого объекта, появится после доступаApiResponseEntity
ФорматJSON
Данные, ниже мы запускаем доступ к проекту, чтобы увидеть эффект.
Вывод интерфейса следующий:
{
"errorMsg": "用户状态异常.",
"data": null
}
В консоли, поскольку мы записали информацию журнала, также есть соответствующий вывод, как показано ниже:
Hibernate:
select
exceptioni0_.ei_id as ei_id1_0_,
exceptioni0_.ei_code as ei_code2_0_,
exceptioni0_.ei_message as ei_messa3_0_
from
sys_exception_info exceptioni0_
where
exceptioni0_.ei_code=? limit ?
2018-01-09 18:54:00.647 ERROR 2024 --- [nio-8080-exec-1] c.h.s.exception.core.LogicException : 系统遇到如下异常,异常码:USER_STATUS_FAILD>>>异常信息:用户状态异常.
2018-01-09 18:54:00.649 ERROR 2024 --- [nio-8080-exec-1] c.h.s.e.c.advice.ExceptionAdvice : 遇到业务逻辑异常:【USER_STATUS_FAILD】
Если исключение бизнес-логики находится в
Service
слой, нам вообще не нужно беспокоиться об откате транзакций, потому чтоLogicException
Это само исключение времени выполнения, и транзакция автоматически откатывается, когда в проекте создается исключение времени выполнения.
Мы экранируем исключения бизнес-логики и ставимtrue
изменить наfalse
Посмотрите формат, который возвращается, если он правильный, например:
{
"errorMsg": null,
"data": "this is index mapping"
}
Если вы хотите поставить соответствующийnull
Измените его на пустую строку, пожалуйста, посетите для просмотраГлава 5. Настройка использования FastJson для возврата представлений Json.
Суммировать
Эта глава объединяет часть содержания предыдущих глав, в основном глобальные исключения, возврат в унифицированном формате и т. д. Этот метод в настоящее время используется в продуктах нашей компании, которые уже могут соответствовать обычной бизнес-логике определения исключения и возврата, а также возврата исключения сообщения. хранить в数据库
Мы можем обновить содержимое подсказки в любое время, что относительно просто в использовании.
Исходный код этой главы загружен в облако кода: SpringBoot, поддерживающий адрес исходного кода:git ee.com/sh-wave-with/spr…Адрес исходного кода поддержки SpringCloud:git ee.com/sh-wave-with/spr…Для статей, связанных со SpringBoot, посетите:Каталог: Каталог обучения SpringBootДля ознакомления со статьями, связанными с QueryDSL, посетите:Каталог обучения общей структуре запросов QueryDSLДля статей, связанных со SpringDataJPA, посетите:Каталог: SpringDatajpa Каталог обученияСпасибо за чтение!