#ВопросЭксперту: Есть ли аналог Criteria API в Spring Data JDBC?



Мы рады представить вам новую рубрику, в которой эксперты сообщества Spring АйО будут отвечать на актуальные и интересные вопросы. Для дебютного выпуска мы пригласили Михаила Поливаху, контрибьютора в Spring Data JDBC, который любезно согласился принять участие. Большое спасибо ему за это! Если вам понравится этот формат, поддержите нас лайками и репостами.



Итак, ниже ответ Михаила на заявленный в заголовке вопрос.



–––



Друзья, по поводу аналога Criteria API в Spring Data JDBC и механизма динамического построения запросов в целом.



TL;DR: Аналог, какой-никакой, имеется, он даже работает😅. Но он менее функциональный, чем тот же Spring Data Specification API.



Long answer:

Если говорить про динамическое построение запроса в целом, то мы можем разбить это на несколько частей:



1. Динамическое построение условий.



Например, LIKE, BETWEEN, IS NOT NULL и любые другие keyword-ы, которые предназначены для фильтрации набора данных, но которые никак не влияют на структуру запроса в целом. Вот это в Spring Data JDBC имеется. Основные классы, которыми вы будете оперировать в таком случае, - Criteria и Query. Использовать это API достаточно просто, единственное - вам придется использовать напрямую JdbcAggregateTemplate. На данный момент распознавание методов с Query как параметром метода в Repository/CrudRepository не поддерживается (речь не про default методы, а именно про абстрактные методы в интерфейсе, которые воспринимаются как PartTreeJdbcQuery запросы), и скорее всего не будет поддерживаться, так как мало кому это пока было нужно (то есть никакого аналога интерфейсу JpaSpecificationExecutor в Spring Data JDBC нет).



Вот пример использования:



Сущность:





@Data

@Table("users")

@EqualsAndHashCode(onlyExplicitlyIncluded = true)

public class User {



@Id

@EqualsAndHashCode.Include

private Long id;



private String status;



private String name;



private OffsetDateTime createdAt;

}





И пример запроса:





@Autowired

private JdbcAggregateTemplate repository;



@Test

void testCriteriaApi() {

Criteria criteria = Criteria

.where("status").is("READY").ignoreCase(true)

.and("created_at").between(OffsetDateTime.now().minusDays(2), OffsetDateTime.now().plusDays(2))

.and("name").like("%J%");



Query query = Query.query(criteria);



Page<User> allByQuery = repository.findAll(query, User.class, Pageable.unpaged());



Assertions.assertThat(allByQuery.getTotalElements()).isEqualTo(3);

}





Надеюсь, здесь все понятно. Код довольно легко читаемый, вот какой SQL мы сгенерируем:





SELECT

"users"."id" AS "id",

"users"."name" AS "name",

"users"."status" AS "status",

"users"."created_at" AS "created_at"

FROM

"users"

WHERE

UPPER("users"."status") = UPPER(?)

AND "users".created_at BETWEEN ?

AND ?

AND "users"."name" LIKE ?





2. Динамическое построение структуры запроса



Теперь, есть другая часть той же Criteria API - это уже динамическое построение самой структуры запроса. Частично с этим помогает CriteriaQuery из Criteria API. Это все возможные группировки, формирование селект листа, формирование подзапросов и т.п. Вот этого в Spring Data JDBC нет и в ближайшее время не планируется. Это огромный пласт работы, который предстоит сделать, если, конечно, это кому-то нужно. Сейчас там конь не валялся по этому вопросу.