Skip to content

Commit aad5d12

Browse files
committed
Add integration test for a query that uses the same parameter for an equals comparison and a LIKE comparison.
Relates to spring-projects#1929
1 parent 752fe46 commit aad5d12

File tree

3 files changed

+217
-0
lines changed

3 files changed

+217
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2008-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.domain.sample;
17+
18+
import jakarta.persistence.Entity;
19+
import jakarta.persistence.GeneratedValue;
20+
import jakarta.persistence.Id;
21+
import lombok.Data;
22+
import lombok.NoArgsConstructor;
23+
24+
/**
25+
* @author Klajdi Paja
26+
*/
27+
@Entity
28+
@Data
29+
@NoArgsConstructor
30+
public class EmployeeWithMultipleFields {
31+
32+
@Id
33+
@GeneratedValue
34+
private Integer id;
35+
private String name;
36+
private String lastName;
37+
private String username;
38+
39+
public EmployeeWithMultipleFields(String name, String lastName, String username) {
40+
this.name = name;
41+
this.lastName = lastName;
42+
this.username = username;
43+
}
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.repository.query;
17+
18+
import jakarta.persistence.EntityManagerFactory;
19+
20+
import org.junit.jupiter.api.BeforeEach;
21+
import org.junit.jupiter.api.Test;
22+
import org.junit.jupiter.api.extension.ExtendWith;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.annotation.Bean;
25+
import org.springframework.context.annotation.ComponentScan.Filter;
26+
import org.springframework.context.annotation.FilterType;
27+
import org.springframework.data.jpa.domain.sample.Address;
28+
import org.springframework.data.jpa.domain.sample.EmployeeWithMultipleFields;
29+
import org.springframework.data.jpa.domain.sample.EmployeeWithMultipleFields;
30+
import org.springframework.data.jpa.repository.JpaRepository;
31+
import org.springframework.data.jpa.repository.Query;
32+
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
33+
import org.springframework.data.repository.query.Param;
34+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
35+
import org.springframework.lang.Nullable;
36+
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
37+
import org.springframework.orm.jpa.JpaTransactionManager;
38+
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
39+
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
40+
import org.springframework.test.context.ContextConfiguration;
41+
import org.springframework.test.context.junit.jupiter.SpringExtension;
42+
import org.springframework.transaction.PlatformTransactionManager;
43+
import org.springframework.transaction.annotation.EnableTransactionManagement;
44+
import org.springframework.transaction.annotation.Transactional;
45+
import javax.sql.DataSource;
46+
import java.util.List;
47+
import java.util.Properties;
48+
49+
import static org.assertj.core.api.Assertions.assertThat;
50+
51+
/**
52+
* Verify that {@literal LIKE}s with a parameter used multiple times in the query works properly.
53+
*
54+
* @author Klajdi Paja
55+
*/
56+
@ExtendWith(SpringExtension.class)
57+
@ContextConfiguration(classes = QueryStringWithLikeRepeatingParametersIntegrationTests.Config.class)
58+
@Transactional
59+
class QueryStringWithLikeRepeatingParametersIntegrationTests {
60+
61+
@Autowired
62+
EmployeeWithMultipleFieldsRepository repository;
63+
64+
@BeforeEach
65+
void setUp() {
66+
repository.saveAllAndFlush(List.of( //
67+
new EmployeeWithMultipleFields("Frodo Lasly", "Baggins", "Frodo Lasly"), //
68+
new EmployeeWithMultipleFields("Lasly", "Baggins", "Lasly"), //
69+
new EmployeeWithMultipleFields("John", "Doe", "unique-username"), //
70+
new EmployeeWithMultipleFields("Frodo C.", "Lasly", "Frodo C") //
71+
));
72+
}
73+
74+
@Test // GH-1929
75+
void customQueryWithEqualsAtTheEndAndNamedParam() {
76+
77+
List<EmployeeWithMultipleFields> employees = repository.customQueryWithEqualsAtTheEndAndNamedParam("Lasly");
78+
79+
assertThat(employees).extracting(EmployeeWithMultipleFields::getName)
80+
.containsExactlyInAnyOrder("Frodo Lasly", "Frodo C.", "Lasly");
81+
}
82+
83+
@Test // GH-1929
84+
void customQueryWithEqualsAtStartAndNamedParam() {
85+
86+
List<EmployeeWithMultipleFields> employees = repository.customQueryWithEqualsAtStartAndNamedParam(
87+
"unique-username");
88+
89+
assertThat(employees).extracting(EmployeeWithMultipleFields::getName).containsExactlyInAnyOrder("John");
90+
}
91+
92+
@Test // GH-1929
93+
void customQueryWithEqualsAtTheStartAndIndexedParam() {
94+
95+
List<EmployeeWithMultipleFields> employees = repository.customQueryWithEqualsAtTheStartAndIndexedParam(
96+
"unique-username");
97+
98+
assertThat(employees).extracting(EmployeeWithMultipleFields::getName).containsExactlyInAnyOrder("John");
99+
}
100+
101+
@Test // GH-1929
102+
void customQueryWithEqualsAtTheEndAndIndexedParam() {
103+
104+
List<EmployeeWithMultipleFields> employees = repository.customQueryWithEqualsAtTheEndAndIndexedParam("Lasly");
105+
106+
assertThat(employees).extracting(EmployeeWithMultipleFields::getName)
107+
.containsExactlyInAnyOrder("Frodo Lasly", "Frodo C.", "Lasly");
108+
109+
}
110+
111+
@Transactional
112+
public interface EmployeeWithMultipleFieldsRepository extends JpaRepository<EmployeeWithMultipleFields, Integer> {
113+
114+
@Query("select e from EmployeeWithMultipleFields e " //
115+
+ "where e.username= :fullSearchQuery "//
116+
+ " OR e.name like %:fullSearchQuery% " //
117+
+ "OR e.lastName like %:fullSearchQuery% ")
118+
List<EmployeeWithMultipleFields> customQueryWithEqualsAtStartAndNamedParam(
119+
@Param("fullSearchQuery") String fullSearchQuery);
120+
121+
@Query("select e from EmployeeWithMultipleFields e " //
122+
+ "where e.name like %:fullSearchQuery% " //
123+
+ "OR e.lastName like %:fullSearchQuery% "//
124+
+ "OR e.username= :fullSearchQuery")
125+
List<EmployeeWithMultipleFields> customQueryWithEqualsAtTheEndAndNamedParam(
126+
@Param("fullSearchQuery") String fullSearchQuery);
127+
128+
@Query("select e from EmployeeWithMultipleFields e " //
129+
+ "where e.name like %?1% " //
130+
+ "OR e.lastName like %?1% "//
131+
+ "OR e.username= ?1 ")
132+
List<EmployeeWithMultipleFields> customQueryWithEqualsAtTheEndAndIndexedParam(String fullSearchQuery);
133+
134+
@Query("select e from EmployeeWithMultipleFields e " //
135+
+ "where e.username= ?1 " //
136+
+ "OR e.lastName like %?1% "//
137+
+ "OR e.name like %?1%")
138+
List<EmployeeWithMultipleFields> customQueryWithEqualsAtTheStartAndIndexedParam(String fullSearchQuery);
139+
140+
}
141+
142+
@EnableJpaRepositories(considerNestedRepositories = true, //
143+
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = { EmployeeWithMultipleFieldsRepository.class }))
144+
@EnableTransactionManagement
145+
static class Config {
146+
147+
@Bean
148+
DataSource dataSource() {
149+
return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
150+
}
151+
152+
@Bean
153+
AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
154+
155+
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
156+
factoryBean.setDataSource(dataSource);
157+
factoryBean.setPersistenceUnitName("spring-data-jpa");
158+
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
159+
160+
Properties properties = new Properties();
161+
properties.setProperty("hibernate.hbm2ddl.auto", "create");
162+
factoryBean.setJpaProperties(properties);
163+
164+
return factoryBean;
165+
}
166+
167+
@Bean
168+
PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
169+
return new JpaTransactionManager(emf);
170+
}
171+
}
172+
}

spring-data-jpa/src/test/resources/META-INF/persistence.xml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<class>org.springframework.data.jpa.domain.sample.EmbeddedIdExampleEmployee</class>
2424
<class>org.springframework.data.jpa.domain.sample.EmbeddedIdExampleDepartment</class>
2525
<class>org.springframework.data.jpa.domain.sample.EmployeeWithName</class>
26+
<class>org.springframework.data.jpa.domain.sample.EmployeeWithMultipleFields</class>
2627
<class>org.springframework.data.jpa.domain.sample.IdClassExampleEmployee</class>
2728
<class>org.springframework.data.jpa.domain.sample.IdClassExampleDepartment</class>
2829
<class>org.springframework.data.jpa.domain.sample.Invoice</class>

0 commit comments

Comments
 (0)