Skip to content

Commit

Permalink
Support Single Query Loading for aggregates with more than one collec…
Browse files Browse the repository at this point in the history
…tion.

Closes #1448
Original pull request: #1622
  • Loading branch information
schauder authored and mp911de committed Sep 27, 2023
1 parent 8fa9e3e commit 9e41d25
Show file tree
Hide file tree
Showing 14 changed files with 453 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* Query Loading.
*
* @author Mark Paluch
* @author Jens Schauder
* @since 3.2
*/
class SingleQueryFallbackDataAccessStrategy extends DelegatingDataAccessStrategy {
Expand Down Expand Up @@ -120,23 +121,25 @@ private boolean isSingleSelectQuerySupported(Class<?> entityType) {

private boolean entityQualifiesForSingleQueryLoading(Class<?> entityType) {

boolean referenceFound = false;
for (PersistentPropertyPath<RelationalPersistentProperty> path : converter.getMappingContext()
.findPersistentPropertyPaths(entityType, __ -> true)) {
RelationalPersistentProperty property = path.getLeafProperty();
if (property.isEntity()) {

// single references are currently not supported
if (!(property.isMap() || property.isCollectionLike())) {
return false;
}

// embedded entities are currently not supported
if (property.isEmbedded()) {
return false;
}

// only a single reference is currently supported
if (referenceFound) {
// nested references are currently not supported
if (path.getLength() > 1) {
return false;
}

referenceFound = true;
}
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import static org.springframework.data.jdbc.testing.TestConfiguration.*;
import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -37,13 +39,15 @@
import java.util.stream.IntStream;

import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.annotation.Id;
Expand All @@ -70,6 +74,7 @@
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.query.Query;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
Expand All @@ -95,6 +100,7 @@ abstract class AbstractJdbcAggregateTemplateIntegrationTests {
@Autowired JdbcAggregateOperations template;
@Autowired NamedParameterJdbcOperations jdbcTemplate;
@Autowired RelationalMappingContext mappingContext;
@Autowired NamedParameterJdbcOperations jdbc;

LegoSet legoSet = createLegoSet("Star Destroyer");

Expand Down Expand Up @@ -1202,6 +1208,115 @@ void readEnumArray() {
assertThat(template.findById(entity.id, EnumArrayOwner.class).digits).isEqualTo(new Color[] { Color.BLUE });
}

@Test // GH-1448
void multipleCollections() {

MultipleCollections aggregate = new MultipleCollections();
aggregate.name = "aggregate";

aggregate.listElements.add(new ListElement("one"));
aggregate.listElements.add(new ListElement("two"));
aggregate.listElements.add(new ListElement("three"));

aggregate.setElements.add(new SetElement("one"));
aggregate.setElements.add(new SetElement("two"));

aggregate.mapElements.put("alpha", new MapElement("one"));
aggregate.mapElements.put("beta", new MapElement("two"));
aggregate.mapElements.put("gamma", new MapElement("three"));
aggregate.mapElements.put("delta", new MapElement("four"));

template.save(aggregate);

MultipleCollections reloaded = template.findById(aggregate.id, MultipleCollections.class);

assertSoftly(softly -> {

softly.assertThat(reloaded.name).isEqualTo(aggregate.name);

softly.assertThat(reloaded.listElements).containsExactly(aggregate.listElements.get(0),
aggregate.listElements.get(1), aggregate.listElements.get(2));

softly.assertThat(reloaded.setElements)
.containsExactlyInAnyOrder(aggregate.setElements.toArray(new SetElement[0]));

softly.assertThat(reloaded.mapElements.get("alpha")).isEqualTo(new MapElement("one"));
softly.assertThat(reloaded.mapElements.get("beta")).isEqualTo(new MapElement("two"));
softly.assertThat(reloaded.mapElements.get("gamma")).isEqualTo(new MapElement("three"));
softly.assertThat(reloaded.mapElements.get("delta")).isEqualTo(new MapElement("four"));
});
}

@Test // GH-1448
void multipleCollectionsWithEmptySet() {

MultipleCollections aggregate = new MultipleCollections();
aggregate.name = "aggregate";

aggregate.listElements.add(new ListElement("one"));
aggregate.listElements.add(new ListElement("two"));
aggregate.listElements.add(new ListElement("three"));

aggregate.mapElements.put("alpha", new MapElement("one"));
aggregate.mapElements.put("beta", new MapElement("two"));
aggregate.mapElements.put("gamma", new MapElement("three"));
aggregate.mapElements.put("delta", new MapElement("four"));

template.save(aggregate);

MultipleCollections reloaded = template.findById(aggregate.id, MultipleCollections.class);

assertSoftly(softly -> {

softly.assertThat(reloaded.name).isEqualTo(aggregate.name);

softly.assertThat(reloaded.listElements).containsExactly(aggregate.listElements.get(0),
aggregate.listElements.get(1), aggregate.listElements.get(2));

softly.assertThat(reloaded.setElements)
.containsExactlyInAnyOrder(aggregate.setElements.toArray(new SetElement[0]));

softly.assertThat(reloaded.mapElements.get("alpha")).isEqualTo(new MapElement("one"));
softly.assertThat(reloaded.mapElements.get("beta")).isEqualTo(new MapElement("two"));
softly.assertThat(reloaded.mapElements.get("gamma")).isEqualTo(new MapElement("three"));
softly.assertThat(reloaded.mapElements.get("delta")).isEqualTo(new MapElement("four"));
});
}

@Test // GH-1448
void multipleCollectionsWithEmptyList() {

MultipleCollections aggregate = new MultipleCollections();
aggregate.name = "aggregate";

aggregate.setElements.add(new SetElement("one"));
aggregate.setElements.add(new SetElement("two"));

aggregate.mapElements.put("alpha", new MapElement("one"));
aggregate.mapElements.put("beta", new MapElement("two"));
aggregate.mapElements.put("gamma", new MapElement("three"));
aggregate.mapElements.put("delta", new MapElement("four"));

template.save(aggregate);

MultipleCollections reloaded = template.findById(aggregate.id, MultipleCollections.class);

assertSoftly(softly -> {

softly.assertThat(reloaded.name).isEqualTo(aggregate.name);

softly.assertThat(reloaded.listElements).containsExactly();

softly.assertThat(reloaded.setElements)
.containsExactlyInAnyOrder(aggregate.setElements.toArray(new SetElement[0]));

softly.assertThat(reloaded.mapElements.get("alpha")).isEqualTo(new MapElement("one"));
softly.assertThat(reloaded.mapElements.get("beta")).isEqualTo(new MapElement("two"));
softly.assertThat(reloaded.mapElements.get("gamma")).isEqualTo(new MapElement("three"));
softly.assertThat(reloaded.mapElements.get("delta")).isEqualTo(new MapElement("four"));
});
}

private <T extends Number> void saveAndUpdateAggregateWithVersion(VersionedAggregate aggregate,
Function<Number, T> toConcreteNumber) {
saveAndUpdateAggregateWithVersion(aggregate, toConcreteNumber, 0);
Expand Down Expand Up @@ -1932,6 +2047,24 @@ static class WithInsertOnly {
@InsertOnlyProperty String insertOnly;
}

@Table
static class MultipleCollections {
@Id Long id;
String name;
List<ListElement> listElements = new ArrayList<>();
Set<SetElement> setElements = new HashSet<>();
Map<String, MapElement> mapElements = new HashMap<>();
}

record ListElement(String name) {
}

record SetElement(String name) {
}

record MapElement(String name) {
}

@Configuration
@Import(TestConfiguration.class)
static class Config {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ DROP TABLE WITH_ID_ONLY;

DROP TABLE WITH_INSERT_ONLY;

DROP TABLE MULTIPLE_COLLECTIONS;
DROP TABLE MAP_ELEMENT;
DROP TABLE LIST_ELEMENT;
DROP TABLE SET_ELEMENT;

CREATE TABLE LEGO_SET
(
"id1" BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
Expand Down Expand Up @@ -373,4 +378,30 @@ CREATE TABLE WITH_INSERT_ONLY
(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
INSERT_ONLY VARCHAR(100)
);
);

CREATE TABLE MULTIPLE_COLLECTIONS
(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
NAME VARCHAR(100)
);

CREATE TABLE SET_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
NAME VARCHAR(100)
);

CREATE TABLE LIST_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY INT,
NAME VARCHAR(100)
);

CREATE TABLE MAP_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY VARCHAR(10),
NAME VARCHAR(100)
);
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,30 @@ CREATE TABLE WITH_INSERT_ONLY
(
ID SERIAL PRIMARY KEY,
INSERT_ONLY VARCHAR(100)
);
);

CREATE TABLE MULTIPLE_COLLECTIONS
(
ID SERIAL PRIMARY KEY,
NAME VARCHAR(100)
);

CREATE TABLE SET_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
NAME VARCHAR(100)
);

CREATE TABLE LIST_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY INT,
NAME VARCHAR(100)
);

CREATE TABLE MAP_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY VARCHAR(10),
NAME VARCHAR(100)
);
Original file line number Diff line number Diff line change
Expand Up @@ -341,4 +341,30 @@ CREATE TABLE WITH_INSERT_ONLY
CREATE TABLE WITH_ID_ONLY
(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY
)
);

CREATE TABLE MULTIPLE_COLLECTIONS
(
ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY,
NAME VARCHAR(100)
);

CREATE TABLE SET_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
NAME VARCHAR(100)
);

CREATE TABLE LIST_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY INT,
NAME VARCHAR(100)
);

CREATE TABLE MAP_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY VARCHAR(10),
NAME VARCHAR(100)
);
Original file line number Diff line number Diff line change
Expand Up @@ -314,4 +314,30 @@ CREATE TABLE WITH_INSERT_ONLY
(
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
INSERT_ONLY VARCHAR(100)
);
);

CREATE TABLE MULTIPLE_COLLECTIONS
(
ID BIGINT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(100)
);

CREATE TABLE SET_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
NAME VARCHAR(100)
);

CREATE TABLE LIST_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY INT,
NAME VARCHAR(100)
);

CREATE TABLE MAP_ELEMENT
(
MULTIPLE_COLLECTIONS BIGINT,
MULTIPLE_COLLECTIONS_KEY VARCHAR(10),
NAME VARCHAR(100)
);
Loading

0 comments on commit 9e41d25

Please sign in to comment.