Skip to content

Commit a7ed590

Browse files
committed
Part 14: Add FilterUserRepositoryImpl.java
1 parent 693d112 commit a7ed590

File tree

1 file changed

+197
-0
lines changed

1 file changed

+197
-0
lines changed
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package spring.oldboy.database.repository.user_repository;
2+
3+
/*
4+
Part 10: Lesson 57 - создаем класс реализующий наш кастомный интерфейс FilterUserRepository.java;
5+
Part 11: Lesson 60 - применяем JDBC в Spring приложении;
6+
Part 11: Lesson 61 - реализация метода для batch запросов в Spring приложении средствами JDBC;
7+
*/
8+
9+
import jakarta.persistence.EntityManager;
10+
import jakarta.persistence.criteria.CriteriaBuilder;
11+
import jakarta.persistence.criteria.CriteriaQuery;
12+
import jakarta.persistence.criteria.Predicate;
13+
import jakarta.persistence.criteria.Root;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.jdbc.core.JdbcTemplate;
16+
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
17+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
18+
import spring.oldboy.database.entity.Role;
19+
import spring.oldboy.database.entity.User;
20+
import spring.oldboy.dto.PersonalInfo;
21+
import spring.oldboy.dto.UserFilterDto;
22+
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
import java.util.Map;
26+
27+
/*
28+
Название класса должно совпадать с названием реализуемого интерфейса с
29+
постфиксом Impl - это очень важно, только так его нужно именовать
30+
(либо лезть в настройки Spring-a и курочить его под свои нужды).
31+
32+
Lesson 60 - не магия, но удобство, т.к. мы работаем с FilterUserRepository
33+
то обращение пойдет к уже готовому его bean-у, хотя мы в данном уроке
34+
подключили (внедрили) JDBC и применяем метод с его функционалом (технологией).
35+
*/
36+
@RequiredArgsConstructor
37+
public class FilterUserRepositoryImpl implements FilterUserRepository {
38+
/* Нам нужен EntityManager и он есть в SpringContext */
39+
private final EntityManager entityManager;
40+
/* Наш метод должен вернуть список User, подпадающих под фильтр */
41+
42+
/* Part 11: Lesson 60 - SQL запрос для метода реализующего механизм JDBC - *.findAllByCompanyIdAndRole() */
43+
private static final String FIND_BY_COMPANY_AND_ROLE = """
44+
SELECT
45+
firstname,
46+
lastname,
47+
birth_date
48+
FROM users
49+
WHERE company_id = ?
50+
AND role = ?
51+
""";
52+
/* Part 11: Lesson 61 - SQL запрос для метода реализующего batch запрос - *.updateCompanyAndRole() */
53+
private static final String UPDATE_COMPANY_AND_ROLE = """
54+
UPDATE users
55+
SET company_id = ?,
56+
role = ?
57+
WHERE id = ?
58+
""";
59+
/*
60+
Part 11: Lesson 61 - SQL запрос с именованными параметрами для метода
61+
реализующего batch запрос - *.updateCompanyAndRoleNamed см.
62+
DOC/SpringJDBC/NamedParameterJdbcTemplate.txt. Каждый параметр
63+
имеет имя и его мы применим при передаче параметров в метод
64+
*/
65+
private static final String UPDATE_COMPANY_AND_ROLE_NAMED = """
66+
UPDATE users
67+
SET company_id = :companyId,
68+
role = :role
69+
WHERE id = :id
70+
""";
71+
72+
/*
73+
Part 11: Lesson 60 - внедрим (подключим) зависимость JdbcTemplate,
74+
чтобы использовать методы передачи запросов к БД через JDBC.
75+
*/
76+
private final JdbcTemplate jdbcTemplate;
77+
/*
78+
Part 11: Lesson 60 - внедрим (подключим) зависимость NamedParameterJdbcTemplate,
79+
чтобы использовать методы передачи запросов к БД через JDBC с именованными
80+
параметрами см. DOC/SpringJDBC/NamedParameterJdbcTemplate.txt.
81+
*/
82+
private final NamedParameterJdbcTemplate namedJdbcTemplate;
83+
84+
/* Part 10: Lesson 57 */
85+
@Override
86+
public List<User> findAllByFilter(UserFilterDto filter) {
87+
/*
88+
Все что связано с созданием запросов с Criteria API можно посмотреть в
89+
https://github.com/JcoderPaul/Hibernate_Lessons/tree/master/Hibernate_part_5
90+
*/
91+
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
92+
CriteriaQuery<User> criteria = cb.createQuery(User.class);
93+
94+
Root<User> user = criteria.from(User.class);
95+
criteria.select(user);
96+
97+
/* Нам нужен Predicate из *.persistence.criteria */
98+
List<Predicate> predicates = new ArrayList<>();
99+
/*
100+
Создаем фильтры применив классический like и lessThan см. пример с комментариями к методам и логике запроса:
101+
https://github.com/JcoderPaul/Hibernate_Lessons/blob/master/Hibernate_part_5/src/main/java/oldboy/dao/UserDaoWithCriteriaAPI.java
102+
*/
103+
if (filter.firstname() != null) {
104+
predicates.add(cb.like(user.get("firstname"), filter.firstname()));
105+
}
106+
if (filter.lastname() != null) {
107+
predicates.add(cb.like(user.get("lastname"), filter.lastname()));
108+
}
109+
if (filter.birthDate() != null) {
110+
predicates.add(cb.lessThan(user.get("birthDate"), filter.birthDate()));
111+
}
112+
criteria.where(predicates.toArray(value -> new Predicate[value]));
113+
114+
return entityManager.createQuery(criteria).getResultList();
115+
}
116+
117+
/*
118+
Part 11: Lesson 60 - реализуем метод с JDBC функционалом см. описание
119+
класса и методов в DOC/SpringJDBC/JdbcTemplateClass.txt
120+
*/
121+
@Override
122+
public List<PersonalInfo> findAllByCompanyIdAndRole(Integer companyId, Role role) {
123+
/*
124+
В данном случае мы используем обычный select запрос в стиле findBy...,
125+
мы будем использовать запрос *.query(), а не более универсальный
126+
*.execute(), которым можно реализовать практически любой запрос к БД.
127+
128+
Используем <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args) -
129+
Запрос заданный SQL для создания подготовленного оператора из SQL и списка
130+
аргументов для привязки к запросу, сопоставляя каждую строку с объектом результата
131+
через RowMapper.
132+
133+
Где: sql - SQL-запрос для выполнения;
134+
rowMapper - обратный вызов, который будет отображать один объект в каждой строке;
135+
args - аргументы для привязки к запросу (оставляя возможность угадать соответствующий
136+
тип SQL). Также может содержать SqlParameterValue объекты, которые указывают
137+
не только значение аргумента, но также тип SQL и, возможно, масштаб.
138+
139+
Кроме String SQL запроса, идет RowMapper см. DOC/RowMapperInterface.txt в который передается
140+
лямда (либо анонимный объект) с нашим DTO. И конечно пара аргументов по которым мы делаем
141+
выборку из БД.
142+
*/
143+
return jdbcTemplate.query(FIND_BY_COMPANY_AND_ROLE,
144+
(rs, rowNum) -> new PersonalInfo(
145+
rs.getString("firstname"),
146+
rs.getString("lastname"),
147+
rs.getDate("birth_date").toLocalDate()
148+
), companyId, role.name());
149+
}
150+
151+
/*
152+
Part 11: Lesson 61 - реализуем метод с пакетным (batch) запросом.
153+
154+
Применим метод int[] batchUpdate(String sql, List<Object[]> batchArgs) из
155+
JdbcTemplate, который запустит пакетное обновление данных, используя
156+
предоставленный оператор SQL со списком LIST предоставленных аргументов,
157+
см. DOC/SpringJDBC/JdbcTemplateClass.txt
158+
*/
159+
@Override
160+
public void updateCompanyAndRole(List<User> users) {
161+
/*
162+
Мы список наших user-ов преобразуем в stream, а затем его средствами в список Object-ов,
163+
при этом необходимо быть максимально внимательным и помнить порядок параметров в SQL запросе:
164+
у нас сначала идет ID компании, затем Role user-a, и только потом ID user-a. Только так в
165+
случае неименованного запроса.
166+
*/
167+
List<Object[]> args = users
168+
.stream()
169+
.map(user -> new Object[]{user.getCompany().getId(), user.getRole().name(), user.getId()})
170+
.toList();
171+
/*
172+
В данном случае мы избежали настройки BatchPreparedStatement и его
173+
параметров, в том числе batchSize - размер передаваемого пакета.
174+
*/
175+
jdbcTemplate.batchUpdate(UPDATE_COMPANY_AND_ROLE, args);
176+
}
177+
178+
/*
179+
Part 11: Lesson 61 - применим именование параметров в SQL запросе,
180+
описание см. DOC/SpringJDBC/NamedParameterJdbcTemplate.txt
181+
*/
182+
@Override
183+
public void updateCompanyAndRoleNamed(List<User> users) {
184+
/* Специально смешаем порядок передаваемых параметров при картировании stream-a */
185+
MapSqlParameterSource[] args = users
186+
.stream()
187+
.map(user -> Map.of(
188+
"id", user.getId(),
189+
"companyId", user.getCompany().getId(),
190+
"role", user.getRole().name()
191+
))
192+
.map(values -> new MapSqlParameterSource(values))
193+
.toArray(value -> new MapSqlParameterSource[value]);
194+
/* Даже с перемешанными параметрами пакетный запрос пройдет */
195+
namedJdbcTemplate.batchUpdate(UPDATE_COMPANY_AND_ROLE_NAMED, args);
196+
}
197+
}

0 commit comments

Comments
 (0)