From 22c187747e65563092959a88fff88ca9a3de417b Mon Sep 17 00:00:00 2001
From: Eugene Kovko <37694937+eukovko@users.noreply.github.com>
Date: Thu, 21 Dec 2023 05:53:57 +0100
Subject: [PATCH] BAEL-6129: SpEL in @Query (#15449)
* BAEL-6129: SpEL in @Query
* BAEL-6129: Fix Naming
* BAEL-6129: Test Cleanup
---
.../spring-data-jpa-query-3/pom.xml | 20 ++-
.../spring/data/jpa/spel/NewsApplication.java | 23 +++
.../data/jpa/spel/config/WebMvcConfig.java | 17 +++
.../spel/controller/ArticleController.java | 26 ++++
.../spring/data/jpa/spel/entity/Article.java | 111 ++++++++++++++
.../data/jpa/spel/entity/ArticleWrapper.java | 14 ++
.../spring/data/jpa/spel/entity/Language.java | 56 +++++++
.../LocaleContextHolderExtension.java | 20 +++
.../spel/repository/ArticleRepository.java | 78 ++++++++++
.../BaseNewsApplicationRepository.java | 17 +++
.../ArticleControllerIntegrationTest.java | 47 ++++++
.../ArticleRepositoryIntegrationTest.java | 141 ++++++++++++++++++
.../src/test/resources/articles-dml.sql | 15 ++
13 files changed, 583 insertions(+), 2 deletions(-)
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/NewsApplication.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/config/WebMvcConfig.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/controller/ArticleController.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Article.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/ArticleWrapper.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Language.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/extension/LocaleContextHolderExtension.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepository.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/BaseNewsApplicationRepository.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/controller/ArticleControllerIntegrationTest.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepositoryIntegrationTest.java
create mode 100644 persistence-modules/spring-data-jpa-query-3/src/test/resources/articles-dml.sql
diff --git a/persistence-modules/spring-data-jpa-query-3/pom.xml b/persistence-modules/spring-data-jpa-query-3/pom.xml
index 1dff3024f6..5ea69791af 100644
--- a/persistence-modules/spring-data-jpa-query-3/pom.xml
+++ b/persistence-modules/spring-data-jpa-query-3/pom.xml
@@ -8,8 +8,20 @@
0.15
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 9
+
+
+
+
-
+
com.baeldung
parent-boot-2
0.0.1-SNAPSHOT
@@ -34,12 +46,16 @@
org.springframework.boot
spring-boot-starter-web
-
org.springframework.boot
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+ test
+
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/NewsApplication.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/NewsApplication.java
new file mode 100644
index 0000000000..04dca159c7
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/NewsApplication.java
@@ -0,0 +1,23 @@
+package com.baeldung.spring.data.jpa.spel;
+
+import java.util.Locale;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.servlet.LocaleResolver;
+import org.springframework.web.servlet.i18n.SessionLocaleResolver;
+
+@SpringBootApplication
+public class NewsApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(NewsApplication.class, args);
+ }
+
+ @Bean
+ public LocaleResolver localeResolver() {
+ SessionLocaleResolver slr = new SessionLocaleResolver();
+ slr.setDefaultLocale(Locale.US);
+ return slr;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/config/WebMvcConfig.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/config/WebMvcConfig.java
new file mode 100644
index 0000000000..6569e31af4
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/config/WebMvcConfig.java
@@ -0,0 +1,17 @@
+package com.baeldung.spring.data.jpa.spel.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
+
+@Configuration
+public class WebMvcConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
+ localeChangeInterceptor.setParamName("locale");
+ registry.addInterceptor(localeChangeInterceptor);
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/controller/ArticleController.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/controller/ArticleController.java
new file mode 100644
index 0000000000..4e092fe150
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/controller/ArticleController.java
@@ -0,0 +1,26 @@
+package com.baeldung.spring.data.jpa.spel.controller;
+
+import com.baeldung.spring.data.jpa.spel.entity.Article;
+import com.baeldung.spring.data.jpa.spel.repository.ArticleRepository;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/articles")
+public class ArticleController {
+
+ private final ArticleRepository articleRepository;
+
+ @Autowired
+ public ArticleController(final ArticleRepository articleRepository) {
+ this.articleRepository = articleRepository;
+ }
+
+ @GetMapping
+ List getAllArticlesWithNativeQuery() {
+ return articleRepository.findAllArticlesUsingLocaleWithNativeQuery();
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Article.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Article.java
new file mode 100644
index 0000000000..93a0e3b9a9
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Article.java
@@ -0,0 +1,111 @@
+package com.baeldung.spring.data.jpa.spel.entity;
+
+import java.util.Objects;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+
+@Entity(name = "articles")
+@Table(name = "articles")
+public class Article {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+ private String title;
+ private String content;
+ private String language;
+
+ public Article() {
+ }
+
+ public Article(final String title, final String content, final String language) {
+ this.title = title;
+ this.content = content;
+ this.language = language;
+ }
+
+ public Article(final Long id, final String title, final String content, final String language) {
+ this.id = id;
+ this.title = title;
+ this.content = content;
+ this.language = language;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(final Long id) {
+ this.id = id;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(final String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(final String content) {
+ this.content = content;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(final String language) {
+ this.language = language;
+ }
+
+ @Override
+ public String toString() {
+ return "News{" +
+ "id=" + id +
+ ", title='" + title + '\'' +
+ ", content='" + content + '\'' +
+ ", language=" + language +
+ '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final Article article = (Article) o;
+
+ if (!Objects.equals(id, article.id)) {
+ return false;
+ }
+ if (!Objects.equals(title, article.title)) {
+ return false;
+ }
+ if (!Objects.equals(content, article.content)) {
+ return false;
+ }
+ return Objects.equals(language, article.language);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = id != null ? id.hashCode() : 0;
+ result = 31 * result + (title != null ? title.hashCode() : 0);
+ result = 31 * result + (content != null ? content.hashCode() : 0);
+ result = 31 * result + (language != null ? language.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/ArticleWrapper.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/ArticleWrapper.java
new file mode 100644
index 0000000000..046a9c558a
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/ArticleWrapper.java
@@ -0,0 +1,14 @@
+package com.baeldung.spring.data.jpa.spel.entity;
+
+public class ArticleWrapper {
+
+ private final Article article;
+
+ public ArticleWrapper(Article article) {
+ this.article = article;
+ }
+
+ public Article getArticle() {
+ return article;
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Language.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Language.java
new file mode 100644
index 0000000000..6fea0e06f4
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/entity/Language.java
@@ -0,0 +1,56 @@
+package com.baeldung.spring.data.jpa.spel.entity;
+
+import java.util.Objects;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Language {
+
+ @Id
+ @Column(name = "iso_code")
+ private String isoCode;
+
+ public Language() {
+ }
+
+ public Language(final String isoCode) {
+ this.isoCode = isoCode;
+ }
+
+ public String getIsoCode() {
+ return isoCode;
+ }
+
+ public void setIsoCode(final String isoCode) {
+ this.isoCode = isoCode;
+ }
+
+ @Override
+ public String toString() {
+ return "Language{" +
+ "isoCode='" + isoCode + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final Language language = (Language) o;
+
+ return Objects.equals(isoCode, language.isoCode);
+ }
+
+ @Override
+ public int hashCode() {
+ return isoCode != null ? isoCode.hashCode() : 0;
+ }
+}
+
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/extension/LocaleContextHolderExtension.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/extension/LocaleContextHolderExtension.java
new file mode 100644
index 0000000000..c0a334440f
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/extension/LocaleContextHolderExtension.java
@@ -0,0 +1,20 @@
+package com.baeldung.spring.data.jpa.spel.extension;
+
+import java.util.Locale;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.data.spel.spi.EvaluationContextExtension;
+import org.springframework.stereotype.Component;
+
+@Component
+public class LocaleContextHolderExtension implements EvaluationContextExtension {
+
+ @Override
+ public String getExtensionId() {
+ return "locale";
+ }
+
+ @Override
+ public Locale getRootObject() {
+ return LocaleContextHolder.getLocale();
+ }
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepository.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepository.java
new file mode 100644
index 0000000000..26549f67f5
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepository.java
@@ -0,0 +1,78 @@
+package com.baeldung.spring.data.jpa.spel.repository;
+
+import com.baeldung.spring.data.jpa.spel.entity.Article;
+import com.baeldung.spring.data.jpa.spel.entity.ArticleWrapper;
+import java.util.List;
+import javax.transaction.Transactional;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+public interface ArticleRepository extends BaseNewsApplicationRepository {
+
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (?1, ?2, ?3, ?4)",
+ nativeQuery = true)
+ void saveWithPositionalArguments(Long id, String title, String content, String language);
+
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (?#{[0]}, ?#{[1]}, ?#{[2]}, ?#{[3]})",
+ nativeQuery = true)
+ void saveWithPositionalSpELArguments(long id, String title, String content, String language);
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (?#{[0]}, ?#{[1]}, ?#{[2] ?: 'Empty Article'}, ?#{[3]})",
+ nativeQuery = true)
+ void saveWithPositionalSpELArgumentsWithEmptyCheck(long id, String title, String content, String language);
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (:id, :title, :content, :language)",
+ nativeQuery = true)
+ void saveWithNamedArguments(@Param("id") long id, @Param("title") String title,
+ @Param("content") String content, @Param("language") String language);
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (:#{#id}, :#{#title}, :#{#content}, :#{#language})",
+ nativeQuery = true)
+ void saveWithNamedSpELArguments(@Param("id") long id, @Param("title") String title,
+ @Param("content") String content, @Param("language") String language);
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (:#{#id}, :#{#title}, :#{#content}, :#{#language.toLowerCase()})",
+ nativeQuery = true)
+ void saveWithNamedSpELArgumentsAndLowerCaseLanguage(@Param("id") long id, @Param("title") String title,
+ @Param("content") String content, @Param("language") String language);
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (:#{#article.id}, :#{#article.title}, :#{#article.content}, :#{#article.language})",
+ nativeQuery = true)
+ void saveWithSingleObjectSpELArgument(@Param("article") Article article);
+
+ @Modifying
+ @Transactional
+ @Query(value = "INSERT INTO articles (id, title, content, language) "
+ + "VALUES (:#{#wrapper.article.id}, :#{#wrapper.article.title}, :#{#wrapper.article.content}, :#{#wrapper.article.language})",
+ nativeQuery = true)
+ void saveWithSingleWrappedObjectSpELArgument(@Param("wrapper") ArticleWrapper articleWrapper);
+
+
+ @Query(value = "SELECT * FROM articles WHERE language = :#{locale.language}",
+ nativeQuery = true)
+ List findAllArticlesUsingLocaleWithNativeQuery();
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/BaseNewsApplicationRepository.java b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/BaseNewsApplicationRepository.java
new file mode 100644
index 0000000000..e76995eaa3
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/main/java/com/baeldung/spring/data/jpa/spel/repository/BaseNewsApplicationRepository.java
@@ -0,0 +1,17 @@
+package com.baeldung.spring.data.jpa.spel.repository;
+
+import com.baeldung.spring.data.jpa.spel.entity.Article;
+import java.util.List;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.NoRepositoryBean;
+
+@NoRepositoryBean
+public interface BaseNewsApplicationRepository extends JpaRepository {
+
+ @Query(value = "select e from #{#entityName} e")
+ List findAllEntitiesUsingEntityPlaceholder();
+
+ @Query(value = "SELECT * FROM #{#entityName}", nativeQuery = true)
+ List findAllEntitiesUsingEntityPlaceholderWithNativeQuery();
+}
diff --git a/persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/controller/ArticleControllerIntegrationTest.java b/persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/controller/ArticleControllerIntegrationTest.java
new file mode 100644
index 0000000000..7d3af20fae
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/controller/ArticleControllerIntegrationTest.java
@@ -0,0 +1,47 @@
+package com.baeldung.spring.data.jpa.spel.controller;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.baeldung.spring.data.jpa.spel.NewsApplication;
+import com.baeldung.spring.data.jpa.spel.entity.Article;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+@SpringBootTest(classes = NewsApplication.class,
+ webEnvironment = WebEnvironment.RANDOM_PORT,
+ properties = {
+ "spring.jpa.show-sql=true",
+ "spring.jpa.generate-ddl=true",
+ "spring.jpa.defer-datasource-initialization=true",
+ "spring.sql.init.data-locations=classpath:articles-dml.sql"
+ })
+class ArticleControllerIntegrationTest {
+
+ @Autowired
+ private ArticleController articleController;
+ @Autowired
+ private WebTestClient webTestClient;
+
+ @Test
+ void whenApplicationStartBeansArePresent() {
+ assertNotNull(articleController);
+ assertNotNull(webTestClient);
+ }
+
+ @ParameterizedTest
+ @CsvSource({"eng,2", "fr,2", "esp,2", "deu, 2", "jp,0"})
+ void whenAskForNewsGetAllNewsInSpecificLanguageBasedOnLocale(String language, int expectedResultSize) {
+ webTestClient.get().uri("/articles?locale=" + language)
+ .exchange()
+ .expectStatus().isOk()
+ .expectBodyList(Article.class)
+ .hasSize(expectedResultSize);
+ }
+
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepositoryIntegrationTest.java b/persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepositoryIntegrationTest.java
new file mode 100644
index 0000000000..f7f9cb996e
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/test/java/com/baeldung/spring/data/jpa/spel/repository/ArticleRepositoryIntegrationTest.java
@@ -0,0 +1,141 @@
+package com.baeldung.spring.data.jpa.spel.repository;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import com.baeldung.spring.data.jpa.spel.NewsApplication;
+import com.baeldung.spring.data.jpa.spel.entity.Article;
+import com.baeldung.spring.data.jpa.spel.entity.ArticleWrapper;
+import com.baeldung.spring.data.jpa.spel.extension.LocaleContextHolderExtension;
+import java.util.List;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest(classes = NewsApplication.class,
+ properties = {
+ "spring.jpa.show-sql=true",
+ "spring.jpa.generate-ddl=true",
+ })
+class ArticleRepositoryIntegrationTest {
+
+ @Autowired
+ private ArticleRepository articleRepository;
+
+ private static final String ENGLISH = "eng";
+ private static final Article SPORTS_ARTICLE
+ = new Article(1L, "Sports Update",
+ "The local team won their game last night...", ENGLISH);
+
+ @Autowired
+ private LocaleContextHolderExtension localeContextHolderExtension;
+
+ @Test
+ void whenContextStartRepositoryIsPresent() {
+ assertNotNull(articleRepository, "Repository should be present");
+ }
+
+ @Test
+ void whenContextStartContextHolderIsPresent() {
+ assertNotNull(localeContextHolderExtension, "Context holder should be present");
+ assertNotNull(localeContextHolderExtension.getRootObject());
+ }
+
+ @AfterEach
+ void tearDown() {
+ articleRepository.deleteAll();
+ }
+
+ @Test
+ void givenArticleWhenCreateWithPositionalArgumentsPlaceholdersShouldBePersisted() {
+ articleRepository.saveWithPositionalArguments(1L, SPORTS_ARTICLE.getTitle(),
+ SPORTS_ARTICLE.getContent(),
+ SPORTS_ARTICLE.getLanguage());
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ }
+
+ @Test
+ void givenArticleWhenCreateWithPositionalSpELArgumentsPlaceholdersShouldBePersisted() {
+ articleRepository.saveWithPositionalSpELArguments(1L, SPORTS_ARTICLE.getTitle(),
+ SPORTS_ARTICLE.getContent(),
+ SPORTS_ARTICLE.getLanguage());
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ }
+
+ @Test
+ void givenArticleWhenCreateWithPositionalSpELArgumentsPlaceholdersWithEmptyCheckShouldStoreDefaultValue() {
+ articleRepository.saveWithPositionalSpELArgumentsWithEmptyCheck(1L,
+ SPORTS_ARTICLE.getTitle(), null, SPORTS_ARTICLE.getLanguage());
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ assertEquals("Empty Article", articles.get(0).getContent());
+ }
+
+ @Test
+ void givenArticleWhenCreateWithPositionalSpELArgumentsPlaceholdersWithEmptyCheckShouldStoreTheOriginalContent() {
+ articleRepository.saveWithPositionalSpELArgumentsWithEmptyCheck(1L,
+ SPORTS_ARTICLE.getTitle(), SPORTS_ARTICLE.getContent(), SPORTS_ARTICLE.getLanguage());
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ assertEquals(SPORTS_ARTICLE, articles.get(0));
+ }
+
+ @Test
+ void givenArticleWhenCreateWithNamedArgumentsPlaceholdersShouldBePersisted() {
+ articleRepository.saveWithNamedArguments(1L, SPORTS_ARTICLE.getTitle(),
+ SPORTS_ARTICLE.getContent(), SPORTS_ARTICLE.getLanguage());
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ }
+
+ @Test
+ void givenArticleWhenCreateWithNamedSpELArgumentsPlaceholdersShouldBePersisted() {
+ articleRepository.saveWithNamedSpELArguments(1L, SPORTS_ARTICLE.getTitle(),
+ SPORTS_ARTICLE.getContent(), SPORTS_ARTICLE.getLanguage());
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ }
+
+ @Test
+ void givenArticleWhenCreateWithNamedSpELArgumentsAndLowerCaseLanguagePlaceholdersConvertLanguageToLowerCase() {
+ final String language = "ENG";
+ articleRepository.saveWithNamedSpELArgumentsAndLowerCaseLanguage(1L,
+ SPORTS_ARTICLE.getTitle(), SPORTS_ARTICLE.getContent(),
+ language);
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ assertEquals(language.toLowerCase(), articles.get(0).getLanguage());
+ }
+
+ @Test
+ void givenArticleWhenCreateWithSingleObjectArgumentPlaceholdersShouldBePersisted() {
+ articleRepository.saveWithSingleObjectSpELArgument(SPORTS_ARTICLE);
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ }
+
+ @Test
+ void givenArticleWhenCreateWithSingleWrappedObjectArgumentPlaceholdersShouldBePersisted() {
+ articleRepository.saveWithSingleWrappedObjectSpELArgument(new ArticleWrapper(SPORTS_ARTICLE));
+ final List articles = articleRepository.findAll();
+ assertEquals(1, articles.size());
+ }
+
+ @Test
+ void givenInheritedQueryWhenSearchForArticlesWillReturnThem() {
+ articleRepository.save(SPORTS_ARTICLE);
+ final List articles = articleRepository.findAllEntitiesUsingEntityPlaceholder();
+ assertEquals(1, articles.size());
+ }
+
+ @Test
+ void givenInheritedNativeQueryWhenSearchForArticlesWillReturnThem() {
+ articleRepository.save(SPORTS_ARTICLE);
+ final List articles = articleRepository.findAllEntitiesUsingEntityPlaceholderWithNativeQuery();
+ assertEquals(1, articles.size());
+ }
+
+}
\ No newline at end of file
diff --git a/persistence-modules/spring-data-jpa-query-3/src/test/resources/articles-dml.sql b/persistence-modules/spring-data-jpa-query-3/src/test/resources/articles-dml.sql
new file mode 100644
index 0000000000..c6e00ee7cc
--- /dev/null
+++ b/persistence-modules/spring-data-jpa-query-3/src/test/resources/articles-dml.sql
@@ -0,0 +1,15 @@
+-- English
+INSERT INTO articles (id, title, content, language) VALUES (11, 'Sports Update', 'The local team won their game last night...', 'eng');
+INSERT INTO articles (id, title, content, language) VALUES (17, 'Health News', 'New advancements in medical research were revealed...', 'eng');
+
+-- French
+INSERT INTO articles (id, title, content, language) VALUES (18, 'Nouvelles de la santé', 'De nouvelles avancées dans la recherche médicale ont été révélées...', 'fr');
+INSERT INTO articles (id, title, content, language) VALUES (12, 'Actualité sportive', 'L''équipe locale a gagné son match hier soir...', 'fr');
+
+-- Spanish
+INSERT INTO articles (id, title, content, language) VALUES (13, 'Actualización Deportiva', 'El equipo local ganó su partido anoche...', 'esp');
+INSERT INTO articles (id, title, content, language) VALUES (14, 'Innovación Tecnológica', 'Se ha anunciado un avance en la investigación de IA...', 'esp');
+
+-- German
+INSERT INTO articles (id, title, content, language) VALUES (15, 'Sportaktualisierung', 'Die lokale Mannschaft hat ihr Spiel gestern Abend gewonnen...', 'deu');
+INSERT INTO articles (id, title, content, language) VALUES (16, 'Technologie Durchbruch', 'Ein Durchbruch in der KI-Forschung wurde bekannt gegeben...', 'deu');