В нашем обычном процессе разработки проектов исключения, как правило, являются наиболее хлопотными проблемами для программистов.Выброс, захват и обработка исключений включают не только откат транзакций, но и возврат интерфейсных сообщений-напоминаний. Итак, как мы можем спроектировать, чтобы решить две вышеупомянутые болевые точки? Можем ли мы единообразно обработать бизнес-логику, а затем предоставить соответствующее содержимое напоминания об исключении во внешнем интерфейсе?
Цели этой главы
на основе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-examplesubmodule, в проект необходимо добавить некоторые дополнительные зависимости конфигурации, и, конечно же, нам нужно добавить наш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 Каталог обученияСпасибо за чтение!