Support of embedded properties in Sort.by.
Original pull request #1403 Closes #1286
This commit is contained in:
committed by
Jens Schauder
parent
706de3f58a
commit
8eb80af044
@@ -23,6 +23,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.dialect.RenderContextFactory;
|
||||
@@ -783,12 +784,55 @@ class SqlGenerator {
|
||||
}
|
||||
|
||||
private OrderByField orderToOrderByField(Sort.Order order) {
|
||||
|
||||
SqlIdentifier columnName = this.entity.getRequiredPersistentProperty(order.getProperty()).getColumnName();
|
||||
SqlIdentifier columnName = getColumnNameToSortBy(order);
|
||||
Column column = Column.create(columnName, this.getTable());
|
||||
return OrderByField.from(column, order.getDirection()).withNullHandling(order.getNullHandling());
|
||||
}
|
||||
|
||||
private SqlIdentifier getColumnNameToSortBy(Sort.Order order) {
|
||||
SqlIdentifier columnName = null;
|
||||
RelationalPersistentProperty propertyToSortBy = entity.getPersistentProperty(order.getProperty());
|
||||
if (propertyToSortBy != null) {
|
||||
return propertyToSortBy.getColumnName();
|
||||
}
|
||||
|
||||
PersistentPropertyPath<RelationalPersistentProperty> persistentPropertyPath = mappingContext.getPersistentPropertyPath(
|
||||
order.getProperty(), entity.getType()
|
||||
);
|
||||
|
||||
propertyToSortBy = persistentPropertyPath.getBaseProperty();
|
||||
|
||||
if (propertyToSortBy == null || !propertyToSortBy.isEmbedded()) {
|
||||
throwPropertyNotMarkedAsEmbeddedException(order);
|
||||
} else {
|
||||
RelationalPersistentEntity<?> embeddedEntity = mappingContext.getRequiredPersistentEntity(propertyToSortBy.getType());
|
||||
columnName = embeddedEntity.getRequiredPersistentProperty(extractFieldNameFromEmbeddedProperty(order)).getColumnName();
|
||||
}
|
||||
return columnName;
|
||||
}
|
||||
|
||||
private void throwPropertyNotMarkedAsEmbeddedException(Sort.Order order) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Specified sorting property '%s' is expected to " +
|
||||
"be the property, named '%s', of embedded entity '%s', but field '%s' is " +
|
||||
"not marked with @Embedded",
|
||||
order.getProperty(),
|
||||
extractFieldNameFromEmbeddedProperty(order),
|
||||
extractEmbeddedPropertyName(order),
|
||||
extractEmbeddedPropertyName(order)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public String extractEmbeddedPropertyName(Sort.Order order) {
|
||||
return order.getProperty().substring(0, order.getProperty().indexOf("."));
|
||||
}
|
||||
|
||||
public String extractFieldNameFromEmbeddedProperty(Sort.Order order) {
|
||||
return order.getProperty().substring(order.getProperty().indexOf(".") + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a single sql query that performs select based on the provided query. Additional the bindings for the
|
||||
* where clause are stored after execution into the <code>parameterSource</code>
|
||||
|
||||
@@ -18,8 +18,12 @@ package org.springframework.data.jdbc.repository;
|
||||
import static java.util.Arrays.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -27,11 +31,17 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.jdbc.testing.EnabledOnFeature;
|
||||
import org.springframework.data.jdbc.testing.TestConfiguration;
|
||||
import org.springframework.data.jdbc.testing.TestDatabaseFeatures;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.mapping.Embedded;
|
||||
import org.springframework.data.relational.core.mapping.Embedded.OnEmpty;
|
||||
import org.springframework.data.relational.core.mapping.Table;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -39,11 +49,14 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Very simple use cases for creation and usage of JdbcRepositories with test {@link Embedded} annotation in Entities.
|
||||
*
|
||||
* @author Bastian Wilhelm
|
||||
* @author Christoph Strobl
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@Transactional
|
||||
@@ -66,10 +79,21 @@ public class JdbcRepositoryEmbeddedIntegrationTests {
|
||||
return factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PersonRepository personRepository() {
|
||||
return factory.getRepository(PersonRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
WithDotColumnRepo withDotColumnRepo() { return factory.getRepository(WithDotColumnRepo.class);}
|
||||
|
||||
}
|
||||
|
||||
@Autowired NamedParameterJdbcTemplate template;
|
||||
@Autowired DummyEntityRepository repository;
|
||||
@Autowired PersonRepository personRepository;
|
||||
|
||||
@Autowired WithDotColumnRepo withDotColumnRepo;
|
||||
|
||||
@Test // DATAJDBC-111
|
||||
public void savesAnEntity() {
|
||||
@@ -220,6 +244,35 @@ public class JdbcRepositoryEmbeddedIntegrationTests {
|
||||
"id = " + entity.getId())).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test // GH-1286
|
||||
public void findOrderedByEmbeddedProperty() {
|
||||
Person first = new Person(null, "Bob", "Seattle", new PersonContacts("ddd@example.com", "+1 111 1111 11 11"));
|
||||
Person second = new Person(null, "Alex", "LA", new PersonContacts("aaa@example.com", "+2 222 2222 22 22"));
|
||||
Person third = new Person(null, "Sarah", "NY", new PersonContacts("ggg@example.com", "+3 333 3333 33 33"));
|
||||
|
||||
personRepository.saveAll(List.of(first, second, third));
|
||||
|
||||
Iterable<Person> fetchedPersons = personRepository.findAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "personContacts.email")));
|
||||
|
||||
Assertions.assertThat(fetchedPersons).hasSize(3);
|
||||
Assertions.assertThat(fetchedPersons).containsExactly(second, first, third);
|
||||
}
|
||||
|
||||
@Test // GH-1286
|
||||
public void sortingWorksCorrectlyIfColumnHasDotInItsName() {
|
||||
|
||||
WithDotColumn first = new WithDotColumn(null, "Salt Lake City");
|
||||
WithDotColumn second = new WithDotColumn(null, "Istanbul");
|
||||
WithDotColumn third = new WithDotColumn(null, "Tokyo");
|
||||
|
||||
withDotColumnRepo.saveAll(List.of(first, second, third));
|
||||
|
||||
Iterable<WithDotColumn> fetchedPersons = withDotColumnRepo.findAll(Sort.by(new Sort.Order(Sort.Direction.ASC, "address")));
|
||||
|
||||
Assertions.assertThat(fetchedPersons).hasSize(3);
|
||||
Assertions.assertThat(fetchedPersons).containsExactly(second, first, third);
|
||||
}
|
||||
|
||||
private static DummyEntity createDummyEntity() {
|
||||
DummyEntity entity = new DummyEntity();
|
||||
|
||||
@@ -246,6 +299,43 @@ public class JdbcRepositoryEmbeddedIntegrationTests {
|
||||
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
|
||||
|
||||
interface PersonRepository extends PagingAndSortingRepository<Person, Long>, CrudRepository<Person, Long> {}
|
||||
|
||||
interface WithDotColumnRepo extends PagingAndSortingRepository<WithDotColumn, Integer>, CrudRepository<WithDotColumn, Integer> {}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
static class WithDotColumn {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
@Column("address.city")
|
||||
private String address;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Table("SORT_EMBEDDED_ENTITY")
|
||||
static class Person {
|
||||
@Id
|
||||
private Long id;
|
||||
private String firstName;
|
||||
private String address;
|
||||
|
||||
@Embedded.Nullable
|
||||
private PersonContacts personContacts;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
static class PersonContacts {
|
||||
private String email;
|
||||
private String phoneNumber;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class DummyEntity {
|
||||
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
DROP TABLE dummy_entity;
|
||||
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);
|
||||
|
||||
DROP TABLE SORT_EMBEDDED_ENTITY;
|
||||
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));
|
||||
|
||||
DROP TABLE WITH_DOT_COLUMN;
|
||||
CREATE TABLE WITH_DOT_COLUMN (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, "address.city" VARCHAR(255));
|
||||
@@ -1 +1,3 @@
|
||||
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)
|
||||
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);
|
||||
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));
|
||||
CREATE TABLE WITH_DOT_COLUMN (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, "address.city" VARCHAR(255));
|
||||
@@ -1 +1,3 @@
|
||||
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)
|
||||
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);
|
||||
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));
|
||||
CREATE TABLE WITH_DOT_COLUMN (id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, "address.city" VARCHAR(255));
|
||||
@@ -1 +1,3 @@
|
||||
CREATE TABLE dummy_entity (id BIGINT AUTO_INCREMENT PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
|
||||
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));
|
||||
CREATE TABLE WITH_DOT_COLUMN (id BIGINT AUTO_INCREMENT PRIMARY KEY, `address.city` VARCHAR(255));
|
||||
@@ -1,2 +1,8 @@
|
||||
DROP TABLE IF EXISTS dummy_entity;
|
||||
CREATE TABLE dummy_entity (id BIGINT IDENTITY PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
|
||||
|
||||
DROP TABLE IF EXISTS SORT_EMBEDDED_ENTITY;
|
||||
CREATE TABLE SORT_EMBEDDED_ENTITY (id BIGINT IDENTITY PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
|
||||
|
||||
DROP TABLE IF EXISTS WITH_DOT_COLUMN;
|
||||
CREATE TABLE WITH_DOT_COLUMN (id BIGINT IDENTITY PRIMARY KEY, "address.city" VARCHAR(255));
|
||||
@@ -1 +1,3 @@
|
||||
CREATE TABLE dummy_entity (id BIGINT AUTO_INCREMENT PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
|
||||
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));
|
||||
CREATE TABLE WITH_DOT_COLUMN (id BIGINT AUTO_INCREMENT PRIMARY KEY, `address.city` VARCHAR(255));
|
||||
@@ -7,3 +7,20 @@ CREATE TABLE DUMMY_ENTITY (
|
||||
PREFIX_TEST VARCHAR2(100),
|
||||
PREFIX_PREFIX2_ATTR NUMBER
|
||||
);
|
||||
|
||||
DROP TABLE SORT_EMBEDDED_ENTITY CASCADE CONSTRAINTS PURGE;
|
||||
|
||||
CREATE TABLE SORT_EMBEDDED_ENTITY (
|
||||
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
|
||||
first_name VARCHAR(100),
|
||||
address VARCHAR(255),
|
||||
email VARCHAR(255),
|
||||
phone_number VARCHAR(255)
|
||||
);
|
||||
|
||||
DROP TABLE WITH_DOT_COLUMN CASCADE CONSTRAINTS PURGE;
|
||||
|
||||
CREATE TABLE WITH_DOT_COLUMN (
|
||||
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
|
||||
"address.city" VARCHAR(255)
|
||||
);
|
||||
@@ -1,2 +1,8 @@
|
||||
DROP TABLE dummy_entity;
|
||||
CREATE TABLE dummy_entity (id SERIAL PRIMARY KEY, TEST VARCHAR(100), PREFIX2_ATTR BIGINT, PREFIX_TEST VARCHAR(100), PREFIX_PREFIX2_ATTR BIGINT);
|
||||
|
||||
DROP TABLE "SORT_EMBEDDED_ENTITY";
|
||||
CREATE TABLE "SORT_EMBEDDED_ENTITY" (id SERIAL PRIMARY KEY, first_name VARCHAR(100), address VARCHAR(255), email VARCHAR(255), phone_number VARCHAR(255));
|
||||
|
||||
DROP TABLE WITH_DOT_COLUMN;
|
||||
CREATE TABLE WITH_DOT_COLUMN (id SERIAL PRIMARY KEY, "address.city" VARCHAR(255));
|
||||
Reference in New Issue
Block a user