Skip to content

Commit 528a15d

Browse files
committed
Generating Sql and using it to read aggregates works for:
- Simple Entities - single references - single collections (List, Set, Map) - conversions get applied on reading - integrated for the supported databases and findById and findAll. - integration tests work for Postgres, DB2, MS SqlServer Next Todos: - Fix integration tests with Oracle which fail because the magic join doesn't return all expected rows. - Support for the missing find* methods in SingleSelectDataAccessStrategy See #1446
1 parent bc0010b commit 528a15d

File tree

70 files changed

+6577
-128
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+6577
-128
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<!-- test utilities-->
4747
<awaitility.version>4.2.0</awaitility.version>
4848
<archunit.version>1.0.1</archunit.version>
49+
<jsqlparser.version>4.5</jsqlparser.version>
4950
</properties>
5051

5152
<inceptionYear>2017</inceptionYear>

spring-data-jdbc/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@
269269
<scope>test</scope>
270270
</dependency>
271271

272+
<dependency>
273+
<groupId>com.github.jsqlparser</groupId>
274+
<artifactId>jsqlparser</artifactId>
275+
<version>${jsqlparser.version}</version>
276+
</dependency>
277+
272278
</dependencies>
273279

274280

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 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+
17+
package org.springframework.data.jdbc.core.convert;
18+
19+
import java.util.Iterator;
20+
import java.util.List;
21+
import java.util.Map;
22+
23+
import org.springframework.data.jdbc.core.convert.sqlgeneration.AggregateToStructure;
24+
import org.springframework.data.jdbc.core.convert.sqlgeneration.AliasFactory;
25+
import org.springframework.data.jdbc.core.convert.sqlgeneration.AnalyticSqlGenerator;
26+
import org.springframework.data.jdbc.core.convert.sqlgeneration.StructureToSelect;
27+
import org.springframework.data.mapping.PersistentPropertyPath;
28+
import org.springframework.data.relational.core.dialect.Dialect;
29+
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
30+
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
31+
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
32+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
33+
import org.springframework.util.Assert;
34+
35+
public class AggregateReader<T> {
36+
37+
private final RelationalMappingContext mappingContext;
38+
private final RelationalPersistentEntity<T> aggregate;
39+
private final AliasFactory aliasFactory = new AliasFactory();
40+
private final AnalyticSqlGenerator sqlGenerator;
41+
private final JdbcConverter converter;
42+
private final NamedParameterJdbcOperations jdbcTemplate;
43+
44+
AggregateReader(RelationalMappingContext mappingContext, Dialect dialect, JdbcConverter converter,
45+
NamedParameterJdbcOperations jdbcTemplate, RelationalPersistentEntity<T> aggregate) {
46+
47+
this.mappingContext = mappingContext;
48+
49+
this.aggregate = aggregate;
50+
this.converter = converter;
51+
this.jdbcTemplate = jdbcTemplate;
52+
53+
this.sqlGenerator = new AnalyticSqlGenerator(dialect, new AggregateToStructure(mappingContext),
54+
new StructureToSelect(aliasFactory));
55+
}
56+
57+
public List<T> findAll() {
58+
59+
String sql = sqlGenerator.findAll(aggregate);
60+
61+
PathToColumnMapping pathToColumn = createPathToColumnMapping(aliasFactory);
62+
AggregateResultSetExtractor<T> extractor = new AggregateResultSetExtractor<>(mappingContext, aggregate, converter,
63+
pathToColumn);
64+
65+
Iterable<T> result = jdbcTemplate.query(sql, extractor);
66+
67+
return (List<T>) result;
68+
}
69+
70+
public T findById(Object id) {
71+
72+
PathToColumnMapping pathToColumn = createPathToColumnMapping(aliasFactory);
73+
AggregateResultSetExtractor<T> extractor = new AggregateResultSetExtractor<>(mappingContext, aggregate, converter,
74+
pathToColumn);
75+
76+
String sql = sqlGenerator.findById(aggregate);
77+
// TODO: apply writing converter
78+
id = converter.writeValue(id, aggregate.getIdProperty().getTypeInformation());
79+
80+
Iterator<T> result = jdbcTemplate.query(sql, Map.of("id", id), extractor).iterator();
81+
82+
return result.hasNext() ? result.next() : null;
83+
}
84+
85+
private PathToColumnMapping createPathToColumnMapping(AliasFactory aliasFactory) {
86+
return new PathToColumnMapping() {
87+
@Override
88+
public String column(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
89+
90+
RelationalPersistentProperty leafProperty = propertyPath.getRequiredLeafProperty();
91+
Object aliasLookUpKey;
92+
if (leafProperty.isEntity()) {
93+
aliasLookUpKey = mappingContext.getRequiredPersistentEntity(leafProperty.getActualType());
94+
} else {
95+
aliasLookUpKey = propertyPath;
96+
}
97+
String alias = aliasFactory.getAlias(aliasLookUpKey);
98+
Assert.notNull(alias, () -> "alias for >" + aliasLookUpKey + "<must not be null");
99+
return alias;
100+
}
101+
102+
@Override
103+
public String keyColumn(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
104+
return aliasFactory.getKeyAlias(propertyPath);
105+
}
106+
};
107+
}
108+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 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+
17+
package org.springframework.data.jdbc.core.convert;
18+
19+
import java.util.HashMap;
20+
21+
import org.springframework.data.relational.core.dialect.Dialect;
22+
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
23+
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
24+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
25+
26+
public class AggregateReaderFactory {
27+
28+
private final RelationalMappingContext mappingContext;
29+
private final Dialect dialect;
30+
private final JdbcConverter converter;
31+
private final NamedParameterJdbcOperations jdbcTemplate;
32+
33+
private final HashMap<RelationalPersistentEntity<?>, AggregateReader> cash = new HashMap<>();
34+
35+
public AggregateReaderFactory(RelationalMappingContext mappingContext, Dialect dialect, JdbcConverter converter,
36+
NamedParameterJdbcOperations jdbcTemplate) {
37+
this.mappingContext = mappingContext;
38+
this.dialect = dialect;
39+
this.converter = converter;
40+
this.jdbcTemplate = jdbcTemplate;
41+
}
42+
43+
<T> AggregateReader<T> createAggregateReaderFor(RelationalPersistentEntity<T> entity) {
44+
return cash.computeIfAbsent(entity, e -> new AggregateReader<>(mappingContext, dialect, converter, jdbcTemplate, e));
45+
}
46+
}

0 commit comments

Comments
 (0)