Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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;
Expand Down Expand Up @@ -783,12 +784,55 @@ private List<OrderByField> extractOrderByFields(Sort sort) {
}

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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,45 @@
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;
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;
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
Expand All @@ -66,10 +79,21 @@ DummyEntityRepository dummyEntityRepository() {
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() {
Expand Down Expand Up @@ -220,6 +244,35 @@ public void saveWithNullValueEmbeddable() {
"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();

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

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 {

Expand Down
Original file line number Diff line number Diff line change
@@ -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));
Original file line number Diff line number Diff line change
@@ -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));
Original file line number Diff line number Diff line change
@@ -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));
Original file line number Diff line number Diff line change
@@ -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));
Original file line number Diff line number Diff line change
@@ -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));
Original file line number Diff line number Diff line change
@@ -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));
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);
Original file line number Diff line number Diff line change
@@ -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));