Основное, на что стоит обратить внимание:
* Переход на Jakarta Persistence версии 3.2
* Более строгая проверка доменной модели
* Новая схема mapping.xsd c расширенными возможностями маппинга
* Переход на Hibernate Models с Hibernate Commons Annotations (HCANN)
#spring_news #hibernate
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍7🤯2
Команда Spring АйО перевела важнейший документ для Spring-разработчиков!
В начале июня компания VMWare, владеющая Spring, опубликовала результаты исследования, в котором приняло учатие более 1,500 разработчиков по всему миру. Отчёт получился действительно интересным и всеобъемлющим. Были рассмотрены как базовые темы, такие как выбор архитектурных подходов и типов API, так и продвинутые, такие как компиляция в Native Image и использование Spring вместе с Kubernetes.
Обязательно к прочтению: https://habr.com/ru/companies/spring_aio/articles/834050/
Сохраняй, делись с друзьями и оставляй своё мнение касаемо результатов этого исследования в комментариях!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥9⚡4🤯3❤1
Рекурсивные запросы в SQL очень полезны при работе с иерархическими или графовыми структурами данных. Конструкция
WITH, введенная в SQL:1999, позволяет задавать Common Table Expressions (CTE), которые представляют собой именованные подзапросы. CTE упрощают сложные запросы, улучшают их читаемость и, что самое важное, позволяют нам реализовать рекурсию. Рассмотрим таблицу, которая хранит информацию о сотрудниках и их менеджерах:
CREATE TABLE employees (
employee_id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
employee_name VARCHAR(255),
manager_id INTEGER,
CONSTRAINT pk_employees PRIMARY KEY (employee_id)
);
ALTER TABLE employees ADD CONSTRAINT
FK_EMPLOYEES_ON_MANAGER FOREIGN KEY (manager_id)
REFERENCES employees (employee_id);
Эта таблица представляет собой список сотрудников, где у каждого сотрудника есть уникальный идентификатор (
employee_id), имя (employee_name) и ссылка на менеджера (manager_id). Поле manager_id является внешним ключом, ссылающимся на employee_id в той же таблице, что позволяет создать иерархическую структуру, где один сотрудник может быть менеджером для других.Предположим, что нам нужно выбрать всех подчиненных определенного менеджера. Для решения этой задачи мы можем использовать рекурсивный SQL запрос:
WITH RECURSIVE EmployeeHierarchy AS (
-- Базовый случай: начнем с верхнего уровня менеджера
SELECT employee_id, employee_name, manager_id
FROM employees
WHERE manager_id = :id
UNION ALL
-- Рекурсивный случай: найдем всех сотрудников, подчиненных найденным ранее сотрудникам
SELECT e.employee_id, e.employee_name, e.manager_id
FROM employees e
INNER JOIN EmployeeHierarchy eh ON e.manager_id = eh.employee_id
)
SELECT *
FROM EmployeeHierarchy;
До выхода версии Hibernate 6.2, для выполнения подобных запросов приходилось использовать
nativeQuery. Пример с использованием entityManager выглядит следующим образом:
var sql = {SQL-запрос, представленный выше};
var employees = entityManager.createNativeQuery(sql, Employee.class)
.setParameter("id", 1)
.getResultList();
Либо так, если используется Spring Data JPA:
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
@Query(value = "{SQL-запрос, представленный выше}", nativeQuery = true)
List<Employee> findAllEmployeesByManagerIdSQL(@Param("id") Integer id);
}
List<Employee> employees = employeeRepository.findAllEmployeesByManagerIdSQL(1);
С выходом Hibernate 6.2, появилась возможность писать рекурсивные запросы, используя HQL. Запрос, представленный выше, теперь можно написать так:
String jpql = """
WITH EmployeeHierarchy AS (
SELECT e.employeeId AS id, e.employeeName AS name, e.manager AS mgr
FROM Employee e
WHERE e.manager.id = :id
UNION ALL
SELECT e.employeeId, e.employeeName, e.manager.id
FROM Employee e
JOIN EmployeeHierarchy eh ON e.manager.id = eh.id
)
SELECT new Employee(
eh.id,
eh.name,
eh.mgr
)
FROM EmployeeHierarchy eh
""";
var employees = entityManager.createQuery(jpql, Employee.class)
.setParameter("id", 1)
.getResultList();
А также со Spring Data JPA:
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
@Query(value = "{JPQL-запрос, представленный выше}")
List<Employee> findAllEmployeesByManagerIdJPQL(@Param("id") Integer id);
}
List<Employee> employees = employeeRepository.findAllEmployeesByManagerIdJPQL(1);
Как можно заметить, синтаксис HQL и SQL запросов довольно сильно похож. Но есть некоторые различия:
* В отличие от стандартного SQL, в Hibernate нет необходимости использовать ключевое слово
RECURSIVE* В Hibernate имена атрибутов CTE задаются через псевдонимы в выражении
SELECT. Другими словами, в заголовке CTE имена не указываются.#SpringTips #Hiberante #CTE
Please open Telegram to view this post
VIEW IN TELEGRAM
👍28❤5🔥4
Когда вы изучаете новую технологию, наличие хорошего примера может значительно облегчить процесс. Представляем полезный ресурс под названием RealWorld или, как, говорят сами его разработчики, - The Mother Of All Demo Apps.
RealWorld - это сервис, на котором собраны проекты с различным стеком, которые, благодаря спеке, должны предоствлять один и тот же API.
Команда Spring АйО выбрала из всего списка самые свежие проекты на Spring Boot с актуальным стеком:
- Spring Boot
- Spring Boot + GraalVM + Spring Data JPA + Spring Security
- Spring Boot + MyBatis
- Spring Boot + Spring Data JPA
- Spring Boot + Spring Data JPA + openapi-generator
- Spring Boot + Spring Data JPA + PostgreSQL
- Spring Boot + Spring Data JPA + Spring Security
- Spring Boot + Spring Security + jOOQ
- Spring Boot + WebFlux + MongoDB
- Spring Boot + WebFlux + RouterFunction + Spock
- Spring Data Couchbase + Spring Boot + Couchbase
Сохраняйте, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥34👍11❤5👎1
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
😁28👍7❤3👎2
Для тех, кто был слишком занят на неделе или просто пропустил некоторые посты, публикуем дайджест!
– Вышел Hibernate 7.0.0.Beta1 – узнали, что нового в грядущем мажорном релизе
– Состояние Spring в 2024 году – выяснили, какие технологии по-прежнему остаются актуальными, а какие стремительно набирают популярность в Spring-экосистеме
– Рекурсивные запросы в Hibernate – вспомнили одну из ключевых фич Hibernate 6.3, и посмотрели на неё в действии
– The Mother Of All Demo Apps – нашли ресурс с большим количеством open-source проектов, но не все из них оказались безупречными
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍7🔥3
Недавно мы рассказывали про Scoped Values. Одной из ключевых особенностей Scoped Values (JEP-481) является отсутствие метода
set, в отличие от ThreadLocal, что позволяет гарантировать, что после привязки значения к run/call методам конкретных Runnable и Callable, оно не может быть в них изменено:
private static final ScopedValue<String> X = ScopedValue.newInstance();
void foo() {
ScopedValue.runWhere(X, "hello", () -> bar()); //привязываем значение "hello" для метода bar()
}
void bar() {
System.out.println(X.get()); // "hello"
ScopedValue.runWhere(X, "goodbye", () -> baz()); //привязываем значение "goodbye" для метода baz()
System.out.println(X.get()); // "hello"
}
void baz() {
System.out.println(X.get()); // "goodbye"
}
1. В методе
foo() устанавливаем значение "hello" для области видимости внутри метода bar()2. В методе
bar() запрашиваем значение. Получаем "hello"3. В методе
bar() устанавливаем значение "goodbye" для области видимости внутри метода baz()4. В методе
bar() запрашиваем значение. Получаем "hello". Тем самым убеждаемся, что после установки значения для области видимости внутри метода baz() оно не поменялось для метода bar()5. В методе
baz() запрашиваем значение. Получаем "goodbye".Важно отметить, что привязка "goodbye" действует только внутри вложенной области видимости. После завершения выполнения метода
baz() значение X в bar() возвращается к "hello". Таким образом, тело метода bar() не может изменить привязку, видимую этим методом, но может изменить привязку, видимую его вызываемыми методами. После завершения метода foo() X возвращается в состояние без привязки. Такое вложение гарантирует ограниченный срок действия для совместного использования нового значения.Scoped Values API также предоставляет метод
callWhere, который позволяет получить результат выполнения:
var result = ScopedValue.callWhere(X, "hello", () -> bar());
В этом случае
callWhere выполняет код внутри области видимости, где X привязан к значению "hello", и возвращает результат выполнения.Подводя итог, складывается ощущение, что нововведение предоставляет более безопасный и эффективный способ передачи данных между потоками по сравнению с использованием
ThreadLocal. Однако с уверенностью можно будет об этом говорить только после того, как фича выйдет из preview и войдет в состав языка на постоянной основе.Ставьте 🔥 если разбор JEP был полезен и вы хотели бы видеть больше подобных обзоров
#Java #JEP
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥36❤4👍4
В конце прошлой недели стала доступна новая версия IntelliJ IDEA. Вот основные нововведения, которые попали в свежую сборку:
🏎 Уменьшено время запуска IDE, ключевые функции стали доступны еще до завершения полной индексации проекта
▶️ Теперь можно запускать методы Spring Data JPA непосредственно в среде разработки, без необходимости запускать проект целиком
⏰ Добавлены человеко-понятные описания для cron-выражений, улучшено их автодополнение
🔧 Улучшен режим K2, — новый механизм поддержки Kotlin, — который делает IDE стабильнее и быстрее
⭐️ Новый интерфейс теперь включен по умолчанию для всех пользователей
Полный список изменений и улучшений доступен на сайте IntelliJ IDEA: https://www.jetbrains.com/ru-ru/idea/whatsnew/
P.S. А какие улучшения вам показались самыми полезными?
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥23👍10❤3🤔1
JPA часто подвергается критике за невозможность загружать сущности частично, что на самом деле является большим заблуждением. Spring Data JPA и Hibernate включают в себя множество инструментов по частичной загрузке сущностей.
Команда Spring АйО подготовила статью, в которой рассмотрела имеющиеся в Spring Data JPA инструменты для частичной загрузки сущностей, а также разобрала их особенности и corner-кейсы.
📚 Подробнее читайте на Хабре
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26❤10🔥7😁2👎1
List<Student> getStudentsOfGradeStateAndGenderWithPositionalParams(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student where grade = ? and state = ? and gender = ?";
return jdbcTemplate.query(sql, new Object[]{grade, state, gender}, new StudentRowMapper());
}
List<Student> getStudentsOfGradeStateAndGenderWithPositionalParams(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student where grade = ? and state = ? and gender = ?";
return jdbcClient.sql(sql)
.param(grade)
.param(state)
.param(gender)
.query(new StudentRowMapper()).list();
}
Это и есть новая фича Spring Framework 6.1, которая предоставляет интерфейс
JdbcClient, благодаря которому параметры запроса можно указывать прямо в chain-вызовах.Предположим, что у нас есть таблица
student с полями student_id (INT), student_name (VARCHAR(255)), age (INT), grade (INT), gender (VARCHAR(10)) и state VARCHAR(100)Объявим bean:
@Repository
class StudentDao {
@Autowired
private JdbcClient jdbcClient;
}
JdbcClient готов к использованию.Нам также завезли поддержку именованных параметров:
int getCountOfStudentsOfGradeStateAndGenderWithNamedParam(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student where grade = :grade and state = :state and gender = :gender";
var countCallbackHandler = new RowCountCallbackHandler();
jdbcClient.sql(sql)
.param("grade", grade)
.param("state", state)
.param("gender", gender)
.query(countCallbackHandler);
return countCallbackHandler.getRowCount();
}
Если необходимо обратиться к параметру запроса по индексу:
List<Student> getStudentsOfGradeStateAndGenderWithParamIndex(int grade, String state, String gender) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = ? and state = ? and gender = ?";
return jdbcClient.sql(sql)
.param(1, grade)
.param(2, state)
.param(3, gender)
.query(new StudentResultExtractor());
}
А еще можем вытащить значения полей прямо из объекта класса, но в этот раз продемонстрируем пример с использованием перегруженного метода
param(), который в качестве третьего параметра примет тип данных, чтобы избежать возможных проблем при сопоставлении типов между Java-приложением и БД (например, при использовании null-значений), после чего вызвать метод update():
Integer insertWithSetParamWithNamedParamAndSqlType(Student student) {
String sql = "INSERT INTO student (student_name, age, grade, gender, state) VALUES (:name, :age, :grade, :gender, :state)";
var noOfRowsAffected = this.jdbcClient.sql(sql)
.param("name", student.getStudentName(), Types.VARCHAR)
.param("age", student.getAge(), Types.INTEGER)
.param("grade", student.getGrade(), Types.INTEGER)
.param("gender", student.getStudentGender(), Types.VARCHAR)
.param("state", student.getState(), Types.VARCHAR)
.update()
return noOfRowsAffected;
}
Можно передать целый список параметров, однако в этом случае необходимо использовать метод
params() (таким же образом можно использовать и объект Map в качестве входного параметра):
Optional<Student> getStudentsOfGradeStateAndGenderWithParamsInList(List params) {
String sql = "select student_id, student_name, age, grade, gender, state from student"
+ " where grade = ? and state = ? and gender = ? limit 1";
return jdbcClient.sql(sql)
.params(params)
.query(new StudentRowMapper()).optional();
}
По итогу интерфейс
JdbcClient объединяет возможности JdbcTemplate и NamedParameterJdbcTemplate, предоставляя более удобный API для работы с JDBC. Благодаря этому можно вызывать методы цепочкой.Ставьте 🔥 если фича кажется полезной
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥91👍14❤6🤔1
Команда Spring АйО продолжает следить за выходом новых версий популярных технологий. В новом переводе рассказали про основные улучшения, которые вошли в состав Spring Boot 3.3. Среди них:
– Поддержка Class Data Sharing (CDS)
– Улучшение производительности виртуальных потоков для web-сокетов
– Новые свойства для настройки Spring Data JDBC, GraphQL websocket, WebFlux и Tomcat
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥5❤2
#SpringHero ⭐️ Rod Johnson
Команда Spring АйО считает важным рассказать о людях, которые внесли наибольший вклад в развитие нашего любимого фреймворка и его экосистемы. В первом посте из серии Spring Hero мы расскажем о создателе Spring.
–––
Rod Johnson получил широкую известность благодаря созданию Spring Framework, который быстро стал одним из самых популярных инструментов для разработки на языке Java.
Первую версию Spring Rod написал сам, и она была выпущена в октябре 2002 года вместе с его книгой "Expert One-on-One J2EE Design and Development". Spring фреймворк стал настоящим прорывом, предложив разработчикам гибкий и мощный инструмент для создания корпоративных приложений. А уже в марте 2004 года вышла первая стабильная версия 1.0.
В августе 2009 года компания VMware приобрела проект за $420 миллионов. Но после этого Rod Johnson не спешил уходить на пенсию. И уже в 2011 году стал председателем компании Neo4j, в 2012 году вошел в совет директоров Typesafe Inc, а в 2016 году основал компанию Atomist.
Сегодня Rod Johnson, судя по описанию на LinkedIn, является членом совета директоров нескольких технологических компаний, таких как Neo Technology, Elasticsearch, Typesafe, Meteor и Hazelcast. А в X (Twitter) он также отметил, что сейчас его внимание сосредоточено на Generative AI.
Команда Spring АйО считает важным рассказать о людях, которые внесли наибольший вклад в развитие нашего любимого фреймворка и его экосистемы. В первом посте из серии Spring Hero мы расскажем о создателе Spring.
–––
Rod Johnson получил широкую известность благодаря созданию Spring Framework, который быстро стал одним из самых популярных инструментов для разработки на языке Java.
Первую версию Spring Rod написал сам, и она была выпущена в октябре 2002 года вместе с его книгой "Expert One-on-One J2EE Design and Development". Spring фреймворк стал настоящим прорывом, предложив разработчикам гибкий и мощный инструмент для создания корпоративных приложений. А уже в марте 2004 года вышла первая стабильная версия 1.0.
В августе 2009 года компания VMware приобрела проект за $420 миллионов. Но после этого Rod Johnson не спешил уходить на пенсию. И уже в 2011 году стал председателем компании Neo4j, в 2012 году вошел в совет директоров Typesafe Inc, а в 2016 году основал компанию Atomist.
Сегодня Rod Johnson, судя по описанию на LinkedIn, является членом совета директоров нескольких технологических компаний, таких как Neo Technology, Elasticsearch, Typesafe, Meteor и Hazelcast. А в X (Twitter) он также отметил, что сейчас его внимание сосредоточено на Generative AI.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤22🔥12👍10👌1
Для тех, кто был слишком занят на неделе или просто пропустил некоторые посты, публикуем дайджест!
– Scoped Values в Java (Часть 2) – завершили исследование новых возможностей Java, представленных в JEP-481
– Вышла IntelliJ IDEA 2024.2 – обсудили основные нововведения новой версии самой популярной IDE
– JPA Entity: загрузи меня не полностью – в новой статье рассказали о способах загрузки только действительно нужных полей из базы данных, используя JPA
– Spring 6 JdbcClient API – на практике рассмотрели, что из себя представляет JdbcClient API
– Spring Boot 3.3: повышение производительности, безопасности и наблюдаемости – узнали о ключевых улучшениях, вошедших в последнюю минорную версию Spring Boot
– #SpringHero: Rod Johnson – в новой рубрике рассказали о человеке, чей вклад в наш любимый фреймворк действительно трудно переоценить
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥6❤4
Дискуссии о том, стоит ли использовать Lombok, похоже, будут актуальны еще долго. Решение об использовании этой библиотеки каждый примет сам, а разработчики на Kotlin, вероятно, будут наблюдать за этим спором со стороны с улыбкой 😅
Если же вы всё таки решили использовать Lombok, эта статья расскажет вам о ключевых подводных камнях, которые могут возникнуть во время использования Lombok вместе с JPA.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26🔥7❤4👎2🤯1
🤿 Глубокое погружение в Sealed классы и интерфейсы
В новом переводе от команды Spring АйО рассмотрим, как sealed классы и интерфейсы позволяют строго контролировать иерархию классов, обеспечивая тем самым безопасность и простоту поддержки кода.
Мы познакомимся с ключевыми особенностями sealed классов и интерфейсов, их влиянием на архитектуру приложений и практическими примерами их использования.
📚 Читать на Хабр: https://habr.com/ru/companies/spring_aio/articles/837262/
В новом переводе от команды Spring АйО рассмотрим, как sealed классы и интерфейсы позволяют строго контролировать иерархию классов, обеспечивая тем самым безопасность и простоту поддержки кода.
Мы познакомимся с ключевыми особенностями sealed классов и интерфейсов, их влиянием на архитектуру приложений и практическими примерами их использования.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥7❤5
Во втором посте из цикла "#ВопросЭксперту" Михаил Поливаха рассказал, что из себя представляет поддержка SpEL в Spring Data JDBC и как мы с вами можем сделать её ещё лучше.
–––
Периодически получаю вопросы, связанные с работой Spel-a в Sping Data JDBC (на самом деле, все ниже сказанное относится также к R2DBC модулю). Решил написать пост с объяснением, почему имеем то, что имеем, и, что также важно - что можно улучшить.
Чтобы пояснить проблематику, предлагаю посмотреть на запрос ниже и сказать, все ли в нем корректно:
@Modifying
@Query("update :#{#entityName} set captured = false where captured = true and captured_time < :#{#dateMillis.toEpochMilli()}")
Long updateCaptureFlag(Instant dateMillis);
На самом деле ответ следующий - в целом, этот запрос, с точки зрения SPEL-а, написан вполне корректно. Единственный момент - работать он не будет из-за одного нюанса, который заключается в placeholder-е - в
entityName. Начнем с того, что в рамках запроса в репозитории создается EvaluationContext. Если коротко - это контекст, в рамках которого происходит evalutaion (там находятся пары ключ значение, объекты и т.д - потенциально все, на что мы ссылаемся в рамках Spel-а). Однако в нашем случае Sprign Data JDBC/R2DBC создает его лишь со значениями параметров вызываемого query метода. Иными словами, в рамках expression-а мы можем лишь резолвить ссылки на параметры query метода (
updateCaptureFlag например, в нашем случае). Этот момент в целом можно расширить, если работать с EvaluationContextExtension, но конкретно сделать так, чтобы entityName резолвился в имя сущности в текущем контексте запроса не получится.Наверняка опытные пользователи Spring Data JPA знают, что в ней достаточно давно существует специальный placeholder для того, чтобы внутри запроса по SPEL-у запросить имя сущности. Это бывает полезно, если какой-то общий запрос описывается в родительском репозитории, а в каждом наследнике этого репозитория этот placeholder в запросе будет резолвится в соответствующе конкретное имя сущности. То есть технически на самом деле это реализовать вполне реально.
С другой стороны, обратите внимание, как резолвится
#entityName:> The entityName is resolved as follows: If the domain type has set the name property on the @Entity annotation, it is used. Otherwise, the simple class-name of the domain type is used.
Фактически, для Spring Data JDBC/R2DBC имя класса бесполезно. Данные модули не работают с JPA, там нет Persistence Context-а, JPQL и пр. Поэтому изначально при проектировании этого не было заложено, но, как оказалось, потребность в этом есть.
Тогда, если сформулировать корректно, нас должна интересовать возможность иметь placeholder с указанием на актуальное имя таблицы, которое ассоциировано с тем aggregate-ом, которым мы оперируем.
Именно такой тред обсуждения мы недавно завели на GitHub: https://github.com/spring-projects/spring-data-relational/issues/1856
Поэтому, если есть интерес, можете зайти и выразить поддержку этому треду комментарием/лайком.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤5🔥2