Skip to content

Commit 1de4074

Browse files
Mikhail2048mipo256
authored andcommitted
GH-1286 implemented
1 parent f6de0f3 commit 1de4074

10 files changed

+181
-4
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.data.domain.Sort;
2424
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
2525
import org.springframework.data.mapping.PersistentPropertyPath;
26+
import org.springframework.data.mapping.PropertyPath;
2627
import org.springframework.data.mapping.context.MappingContext;
2728
import org.springframework.data.relational.core.dialect.Dialect;
2829
import org.springframework.data.relational.core.dialect.RenderContextFactory;
@@ -783,12 +784,55 @@ private List<OrderByField> extractOrderByFields(Sort sort) {
783784
}
784785

785786
private OrderByField orderToOrderByField(Sort.Order order) {
786-
787-
SqlIdentifier columnName = this.entity.getRequiredPersistentProperty(order.getProperty()).getColumnName();
787+
SqlIdentifier columnName = getColumnNameToSortBy(order);
788788
Column column = Column.create(columnName, this.getTable());
789789
return OrderByField.from(column, order.getDirection()).withNullHandling(order.getNullHandling());
790790
}
791791

792+
private SqlIdentifier getColumnNameToSortBy(Sort.Order order) {
793+
SqlIdentifier columnName = null;
794+
RelationalPersistentProperty propertyToSortBy = entity.getPersistentProperty(order.getProperty());
795+
if (propertyToSortBy != null) {
796+
return propertyToSortBy.getColumnName();
797+
}
798+
799+
PersistentPropertyPath<RelationalPersistentProperty> persistentPropertyPath = mappingContext.getPersistentPropertyPath(
800+
order.getProperty(), entity.getType()
801+
);
802+
803+
propertyToSortBy = persistentPropertyPath.getBaseProperty();
804+
805+
if (propertyToSortBy == null || !propertyToSortBy.isEmbedded()) {
806+
throwPropertyNotMarkedAsEmbeddedException(order);
807+
} else {
808+
RelationalPersistentEntity<?> embeddedEntity = mappingContext.getRequiredPersistentEntity(propertyToSortBy.getType());
809+
columnName = embeddedEntity.getRequiredPersistentProperty(extractFieldNameFromEmbeddedProperty(order)).getColumnName();
810+
}
811+
return columnName;
812+
}
813+
814+
private void throwPropertyNotMarkedAsEmbeddedException(Sort.Order order) {
815+
throw new IllegalArgumentException(
816+
String.format(
817+
"Specified sorting property '%s' is expected to " +
818+
"be the property, named '%s', of embedded entity '%s', but field '%s' is " +
819+
"not marked with @Embedded",
820+
order.getProperty(),
821+
extractFieldNameFromEmbeddedProperty(order),
822+
extractEmbeddedPropertyName(order),
823+
extractEmbeddedPropertyName(order)
824+
)
825+
);
826+
}
827+
828+
public String extractEmbeddedPropertyName(Sort.Order order) {
829+
return order.getProperty().substring(0, order.getProperty().indexOf("."));
830+
}
831+
832+
public String extractFieldNameFromEmbeddedProperty(Sort.Order order) {
833+
return order.getProperty().substring(order.getProperty().indexOf(".") + 1);
834+
}
835+
792836
/**
793837
* Constructs a single sql query that performs select based on the provided query. Additional the bindings for the
794838
* where clause are stored after execution into the <code>parameterSource</code>

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,45 @@
1818
import static java.util.Arrays.*;
1919
import static org.assertj.core.api.Assertions.*;
2020

21+
import lombok.AllArgsConstructor;
2122
import lombok.Data;
2223

24+
import lombok.NoArgsConstructor;
25+
import org.assertj.core.api.Assertions;
26+
import org.junit.jupiter.api.Disabled;
2327
import org.junit.jupiter.api.Test;
2428
import org.junit.jupiter.api.extension.ExtendWith;
2529
import org.springframework.beans.factory.annotation.Autowired;
2630
import org.springframework.context.annotation.Bean;
2731
import org.springframework.context.annotation.Configuration;
2832
import org.springframework.context.annotation.Import;
2933
import org.springframework.data.annotation.Id;
34+
import org.springframework.data.domain.Sort;
3035
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
36+
import org.springframework.data.jdbc.testing.EnabledOnFeature;
3137
import org.springframework.data.jdbc.testing.TestConfiguration;
38+
import org.springframework.data.jdbc.testing.TestDatabaseFeatures;
39+
import org.springframework.data.relational.core.mapping.Column;
3240
import org.springframework.data.relational.core.mapping.Embedded;
3341
import org.springframework.data.relational.core.mapping.Embedded.OnEmpty;
42+
import org.springframework.data.relational.core.mapping.Table;
3443
import org.springframework.data.repository.CrudRepository;
44+
import org.springframework.data.repository.PagingAndSortingRepository;
3545
import org.springframework.jdbc.core.JdbcTemplate;
3646
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
3747
import org.springframework.test.context.ContextConfiguration;
3848
import org.springframework.test.context.junit.jupiter.SpringExtension;
3949
import org.springframework.test.jdbc.JdbcTestUtils;
4050
import org.springframework.transaction.annotation.Transactional;
4151

52+
import java.util.List;
53+
4254
/**
4355
* Very simple use cases for creation and usage of JdbcRepositories with test {@link Embedded} annotation in Entities.
4456
*
4557
* @author Bastian Wilhelm
4658
* @author Christoph Strobl
59+
* @author Mikhail Polivakha
4760
*/
4861
@ContextConfiguration
4962
@Transactional
@@ -66,10 +79,21 @@ DummyEntityRepository dummyEntityRepository() {
6679
return factory.getRepository(DummyEntityRepository.class);
6780
}
6881

82+
@Bean
83+
PersonRepository personRepository() {
84+
return factory.getRepository(PersonRepository.class);
85+
}
86+
87+
@Bean
88+
WithDotColumnRepo withDotColumnRepo() { return factory.getRepository(WithDotColumnRepo.class);}
89+
6990
}
7091

7192
@Autowired NamedParameterJdbcTemplate template;
7293
@Autowired DummyEntityRepository repository;
94+
@Autowired PersonRepository personRepository;
95+
96+
@Autowired WithDotColumnRepo withDotColumnRepo;
7397

7498
@Test // DATAJDBC-111
7599
public void savesAnEntity() {
@@ -220,6 +244,35 @@ public void saveWithNullValueEmbeddable() {
220244
"id = " + entity.getId())).isEqualTo(1);
221245
}
222246

247+
@Test // GH-1286
248+
public void findOrderedByEmbeddedProperty() {
249+
Person first = new Person(null, "Bob", "Seattle", new PersonContacts("ddd@example.com", "+1 111 1111 11 11"));
250+
Person second = new Person(null, "Alex", "LA", new PersonContacts("aaa@example.com", "+2 222 2222 22 22"));
251+
Person third = new Person(null, "Sarah", "NY", new PersonContacts("ggg@example.com", "+3 333 3333 33 33"));
252+
253+
personRepository.saveAll(List.of(first, second, third));
254+
255+
Iterable<Person> fetchedPersons = personRepository.findAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "personContacts.email")));
256+
257+
Assertions.assertThat(fetchedPersons).hasSize(3);
258+
Assertions.assertThat(fetchedPersons).containsExactly(second, first, third);
259+
}
260+
261+
@Test // GH-1286
262+
public void sortingWorksCorrectlyIfColumnHasDotInItsName() {
263+
264+
WithDotColumn first = new WithDotColumn(null, "Salt Lake City");
265+
WithDotColumn second = new WithDotColumn(null, "Istanbul");
266+
WithDotColumn third = new WithDotColumn(null, "Tokyo");
267+
268+
withDotColumnRepo.saveAll(List.of(first, second, third));
269+
270+
Iterable<WithDotColumn> fetchedPersons = withDotColumnRepo.findAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "address")));
271+
272+
Assertions.assertThat(fetchedPersons).hasSize(3);
273+
Assertions.assertThat(fetchedPersons).containsExactly(second, first, third);
274+
}
275+
223276
private static DummyEntity createDummyEntity() {
224277
DummyEntity entity = new DummyEntity();
225278

@@ -246,6 +299,43 @@ private static DummyEntity createDummyEntity() {
246299

247300
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
248301

302+
interface PersonRepository extends PagingAndSortingRepository<Person, Long>, CrudRepository<Person, Long> {}
303+
304+
interface WithDotColumnRepo extends PagingAndSortingRepository<WithDotColumn, Integer>, CrudRepository<WithDotColumn, Integer> {}
305+
306+
@Data
307+
@AllArgsConstructor
308+
@NoArgsConstructor
309+
static class WithDotColumn {
310+
311+
@Id
312+
private Integer id;
313+
@Column("address.city")
314+
private String address;
315+
}
316+
317+
@Data
318+
@AllArgsConstructor
319+
@NoArgsConstructor
320+
@Table("SORT_EMBEDDED_ENTITY")
321+
static class Person {
322+
@Id
323+
private Long id;
324+
private String firstName;
325+
private String address;
326+
327+
@Embedded.Nullable
328+
private PersonContacts personContacts;
329+
}
330+
331+
@Data
332+
@AllArgsConstructor
333+
@NoArgsConstructor
334+
static class PersonContacts {
335+
private String email;
336+
private String phoneNumber;
337+
}
338+
249339
@Data
250340
static class DummyEntity {
251341

Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
DROP TABLE dummy_entity;
22
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
3+
4+
DROP TABLE SORT_EMBEDDED_ENTITY;
5+
CREATE TABLE SORT_EMBEDDED_ENTITY (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
6+
7+
DROP TABLE WITH_DOT_COLUMN;
8+
CREATE TABLE WITH_DOT_COLUMN (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, "address.city" VARCHAR(255));
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT)
1+
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
2+
CREATE TABLE SORT_EMBEDDED_ENTITY (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
3+
CREATE TABLE WITH_DOT_COLUMN (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, "address.city" VARCHAR(255));
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT)
1+
CREATE TABLE dummy_entity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
2+
CREATE TABLE SORT_EMBEDDED_ENTITY (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
3+
CREATE TABLE WITH_DOT_COLUMN (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, "address.city" VARCHAR(255));
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
CREATE TABLE dummy_entity (id BIGINT AUTO_INCREMENT PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
2+
CREATE TABLE SORT_EMBEDDED_ENTITY (id BIGINT AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
3+
CREATE TABLE WITH_DOT_COLUMN (id BIGINT AUTO_INCREMENT PRIMARY KEY, `address.city` VARCHAR(255));
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
DROP TABLE IF EXISTS dummy_entity;
22
CREATE TABLE dummy_entity (id BIGINT IDENTITY PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
3+
4+
DROP TABLE IF EXISTS SORT_EMBEDDED_ENTITY;
5+
CREATE TABLE SORT_EMBEDDED_ENTITY (id BIGINT IDENTITY PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
6+
7+
DROP TABLE IF EXISTS WITH_DOT_COLUMN;
8+
CREATE TABLE WITH_DOT_COLUMN (id BIGINT IDENTITY PRIMARY KEY, "address.city" VARCHAR(255));
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
CREATE TABLE dummy_entity (id BIGINT AUTO_INCREMENT PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
2+
CREATE TABLE SORT_EMBEDDED_ENTITY (id BIGINT AUTO_INCREMENT PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
3+
CREATE TABLE WITH_DOT_COLUMN (id BIGINT AUTO_INCREMENT PRIMARY KEY, `address.city` VARCHAR(255));

spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedIntegrationTests-oracle.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,20 @@ CREATE TABLE DUMMY_ENTITY (
77
PREFIX_TEST VARCHAR2(100),
88
PREFIX_PREFIX2_ATTR NUMBER
99
);
10+
11+
DROP TABLE SORT_EMBEDDED_ENTITY CASCADE CONSTRAINTS PURGE;
12+
13+
CREATE TABLE SORT_EMBEDDED_ENTITY (
14+
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
15+
first_name VARCHAR(100),
16+
address VARCHAR(255),
17+
email VARCHAR(255),
18+
phone_number VARCHAR(255)
19+
);
20+
21+
DROP TABLE WITH_DOT_COLUMN CASCADE CONSTRAINTS PURGE;
22+
23+
CREATE TABLE WITH_DOT_COLUMN (
24+
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
25+
"address.city" VARCHAR(255)
26+
);
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
DROP TABLE dummy_entity;
22
CREATE TABLE dummy_entity (id SERIAL PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
3+
4+
DROP TABLE "SORT_EMBEDDED_ENTITY";
5+
CREATE TABLE "SORT_EMBEDDED_ENTITY" (id SERIAL PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
6+
7+
DROP TABLE WITH_DOT_COLUMN;
8+
CREATE TABLE WITH_DOT_COLUMN (id SERIAL PRIMARY KEY, "address.city" VARCHAR(255));

0 commit comments

Comments
 (0)