Skip to content

Commit b1c68d9

Browse files
schaudermp911de
authored andcommitted
DATAJDBC-359 - Support for arbitrary chains of entities with or without Id.
Introduced a `ReadingContext` in the `EntityRowMapper` to avoid passing the `ResultSet` and the `path` all over the place. Added a dependency test to Spring Data Relational and fixed the test failure by moving the PersistentPropertyPathExtension to core.mapping. Original pull request: #150.
1 parent aea39d9 commit b1c68d9

File tree

53 files changed

+1857
-989
lines changed

Some content is hidden

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

53 files changed

+1857
-989
lines changed

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

Lines changed: 16 additions & 454 deletions
Large diffs are not rendered by default.

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

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.Collections;
2121
import java.util.Map;
2222

23-
import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
2423
import org.springframework.data.mapping.PersistentPropertyPath;
2524
import org.springframework.data.relational.core.conversion.DbAction;
2625
import org.springframework.data.relational.core.conversion.DbAction.Delete;
@@ -33,11 +32,12 @@
3332
import org.springframework.data.relational.core.conversion.DbAction.Update;
3433
import org.springframework.data.relational.core.conversion.DbAction.UpdateRoot;
3534
import org.springframework.data.relational.core.conversion.Interpreter;
35+
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
3636
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
3737
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
3838
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
3939
import org.springframework.data.relational.domain.Identifier;
40-
import org.springframework.lang.Nullable;
40+
import org.springframework.util.Assert;
4141

4242
/**
4343
* {@link Interpreter} for {@link DbAction}s using a {@link DataAccessStrategy} for performing actual database
@@ -144,38 +144,67 @@ public <T> void interpret(DeleteAllRoot<T> deleteAllRoot) {
144144

145145
private Identifier getParentKeys(DbAction.WithDependingOn<?> action) {
146146

147-
DbAction.WithEntity<?> dependingOn = action.getDependingOn();
147+
Object id = getParentId(action);
148148

149-
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(dependingOn.getEntityType());
150-
151-
Object id = getIdFromEntityDependingOn(dependingOn, persistentEntity);
152149
JdbcIdentifierBuilder identifier = JdbcIdentifierBuilder //
153-
.forBackReferences(action.getPropertyPath(), id);
150+
.forBackReferences(new PersistentPropertyPathExtension(context, action.getPropertyPath()), id);
154151

155152
for (Map.Entry<PersistentPropertyPath<RelationalPersistentProperty>, Object> qualifier : action.getQualifiers()
156153
.entrySet()) {
157-
identifier = identifier.withQualifier(qualifier.getKey(), qualifier.getValue());
154+
identifier = identifier.withQualifier(new PersistentPropertyPathExtension(context, qualifier.getKey()),
155+
qualifier.getValue());
158156
}
159157

160158
return identifier.build();
161159
}
162160

163-
@Nullable
164-
private Object getIdFromEntityDependingOn(DbAction.WithEntity<?> dependingOn,
165-
RelationalPersistentEntity<?> persistentEntity) {
161+
private Object getParentId(DbAction.WithDependingOn<?> action) {
162+
163+
PersistentPropertyPathExtension path = new PersistentPropertyPathExtension(context, action.getPropertyPath());
164+
PersistentPropertyPathExtension idPath = path.getIdDefiningParentPath();
165+
166+
DbAction.WithEntity idOwningAction = getIdOwningAction(action, idPath);
167+
168+
return getIdFrom(idOwningAction);
169+
}
170+
171+
@SuppressWarnings("unchecked")
172+
private DbAction.WithEntity getIdOwningAction(DbAction.WithEntity action, PersistentPropertyPathExtension idPath) {
173+
174+
if (!(action instanceof DbAction.WithDependingOn)) {
175+
176+
Assert.state(idPath.getLength() == 0,
177+
"When the id path is not empty the id providing action should be of type WithDependingOn");
178+
179+
return action;
180+
}
181+
182+
DbAction.WithDependingOn withDependingOn = (DbAction.WithDependingOn) action;
166183

167-
Object entity = dependingOn.getEntity();
184+
if (idPath.matches(withDependingOn.getPropertyPath())) {
185+
return action;
186+
}
187+
188+
return getIdOwningAction(withDependingOn.getDependingOn(), idPath);
189+
}
168190

169-
if (dependingOn instanceof DbAction.WithGeneratedId) {
191+
private Object getIdFrom(DbAction.WithEntity idOwningAction) {
170192

171-
Object generatedId = ((DbAction.WithGeneratedId<?>) dependingOn).getGeneratedId();
193+
if (idOwningAction instanceof DbAction.WithGeneratedId) {
194+
195+
Object generatedId = ((DbAction.WithGeneratedId<?>) idOwningAction).getGeneratedId();
172196

173197
if (generatedId != null) {
174198
return generatedId;
175199
}
176200
}
177201

178-
return persistentEntity.getIdentifierAccessor(entity).getIdentifier();
179-
}
202+
RelationalPersistentEntity<?> persistentEntity = context
203+
.getRequiredPersistentEntity(idOwningAction.getEntityType());
204+
Object identifier = persistentEntity.getIdentifierAccessor(idOwningAction.getEntity()).getIdentifier();
180205

206+
Assert.state(identifier != null, "Couldn't get obtain a required id value");
207+
208+
return identifier;
209+
}
181210
}
Lines changed: 9 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2019 the original author or authors.
2+
* Copyright 2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,209 +15,20 @@
1515
*/
1616
package org.springframework.data.jdbc.core;
1717

18-
import java.sql.ResultSet;
19-
import java.sql.SQLException;
20-
import java.util.Map;
21-
22-
import org.springframework.core.convert.converter.Converter;
23-
import org.springframework.data.mapping.MappingException;
24-
import org.springframework.data.mapping.PersistentEntity;
25-
import org.springframework.data.mapping.PersistentPropertyAccessor;
26-
import org.springframework.data.mapping.PreferredConstructor;
27-
import org.springframework.data.relational.core.conversion.RelationalConverter;
28-
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
18+
import org.springframework.data.jdbc.core.convert.JdbcConverter;
2919
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
30-
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
31-
import org.springframework.jdbc.core.RowMapper;
32-
import org.springframework.lang.Nullable;
33-
import org.springframework.util.Assert;
3420

3521
/**
36-
* Maps a {@link ResultSet} to an entity of type {@code T}, including entities referenced. This {@link RowMapper} might
37-
* trigger additional SQL statements in order to load other members of the same aggregate.
38-
*
3922
* @author Jens Schauder
40-
* @author Oliver Gierke
41-
* @author Mark Paluch
42-
* @author Maciej Walkowiak
43-
* @author Bastian Wilhelm
23+
*
24+
* @deprecated Use {@link org.springframework.data.jdbc.core.convert.EntityRowMapper} instead.
4425
*/
45-
public class EntityRowMapper<T> implements RowMapper<T> {
46-
47-
private static final Converter<Iterable<?>, Map<?, ?>> ITERABLE_OF_ENTRY_TO_MAP_CONVERTER = new IterableOfEntryToMapConverter();
48-
49-
private final RelationalPersistentEntity<T> entity;
50-
51-
private final RelationalConverter converter;
52-
private final RelationalMappingContext context;
53-
private final DataAccessStrategy accessStrategy;
54-
private final RelationalPersistentProperty idProperty;
55-
56-
public EntityRowMapper(RelationalPersistentEntity<T> entity, RelationalMappingContext context,
57-
RelationalConverter converter, DataAccessStrategy accessStrategy) {
58-
59-
this.entity = entity;
60-
this.converter = converter;
61-
this.context = context;
62-
this.accessStrategy = accessStrategy;
63-
this.idProperty = entity.getIdProperty();
64-
}
65-
66-
/*
67-
* (non-Javadoc)
68-
* @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
69-
*/
70-
@Override
71-
public T mapRow(ResultSet resultSet, int rowNumber) {
72-
73-
String prefix = "";
74-
75-
RelationalPersistentProperty idProperty = entity.getIdProperty();
76-
77-
Object idValue = null;
78-
if (idProperty != null) {
79-
idValue = readFrom(resultSet, idProperty, prefix);
80-
}
81-
82-
T result = createInstance(entity, resultSet, idValue, prefix);
83-
84-
return entity.requiresPropertyPopulation() //
85-
? populateProperties(result, resultSet) //
86-
: result;
87-
}
88-
89-
private T populateProperties(T result, ResultSet resultSet) {
90-
91-
PersistentPropertyAccessor<T> propertyAccessor = converter.getPropertyAccessor(entity, result);
92-
93-
Object id = idProperty == null ? null : readFrom(resultSet, idProperty, "");
94-
95-
PreferredConstructor<T, RelationalPersistentProperty> persistenceConstructor = entity.getPersistenceConstructor();
96-
97-
for (RelationalPersistentProperty property : entity) {
98-
99-
if (persistenceConstructor != null && persistenceConstructor.isConstructorParameter(property)) {
100-
continue;
101-
}
102-
103-
propertyAccessor.setProperty(property, readOrLoadProperty(resultSet, id, property, ""));
104-
}
105-
106-
return propertyAccessor.getBean();
107-
}
108-
109-
@Nullable
110-
private Object readOrLoadProperty(ResultSet resultSet, @Nullable Object id, RelationalPersistentProperty property,
111-
String prefix) {
112-
113-
if (property.isCollectionLike() && property.isEntity() && id != null) {
114-
return accessStrategy.findAllByProperty(id, property);
115-
} else if (property.isMap() && id != null) {
116-
return ITERABLE_OF_ENTRY_TO_MAP_CONVERTER.convert(accessStrategy.findAllByProperty(id, property));
117-
} else if (property.isEmbedded()) {
118-
return readEmbeddedEntityFrom(resultSet, id, property, prefix);
119-
} else {
120-
return readFrom(resultSet, property, prefix);
121-
}
122-
}
123-
124-
/**
125-
* Read a single value or a complete Entity from the {@link ResultSet} passed as an argument.
126-
*
127-
* @param resultSet the {@link ResultSet} to extract the value from. Must not be {@code null}.
128-
* @param property the {@link RelationalPersistentProperty} for which the value is intended. Must not be {@code null}.
129-
* @param prefix to be used for all column names accessed by this method. Must not be {@code null}.
130-
* @return the value read from the {@link ResultSet}. May be {@code null}.
131-
*/
132-
@Nullable
133-
private Object readFrom(ResultSet resultSet, RelationalPersistentProperty property, String prefix) {
134-
135-
if (property.isEntity()) {
136-
return readEntityFrom(resultSet, property, prefix);
137-
}
26+
@Deprecated
27+
public class EntityRowMapper<T> extends org.springframework.data.jdbc.core.convert.EntityRowMapper<T> {
13828

139-
Object value = getObjectFromResultSet(resultSet, prefix + property.getColumnName());
140-
return converter.readValue(value, property.getTypeInformation());
141-
142-
}
143-
144-
private Object readEmbeddedEntityFrom(ResultSet rs, @Nullable Object id, RelationalPersistentProperty property,
145-
String prefix) {
146-
147-
String newPrefix = prefix + property.getEmbeddedPrefix();
148-
149-
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(property.getActualType());
150-
151-
Object instance = createInstance(entity, rs, null, newPrefix);
152-
153-
@SuppressWarnings("unchecked")
154-
PersistentPropertyAccessor<?> accessor = converter.getPropertyAccessor((PersistentEntity<Object, ?>) entity,
155-
instance);
156-
157-
for (RelationalPersistentProperty p : entity) {
158-
accessor.setProperty(p, readOrLoadProperty(rs, id, p, newPrefix));
159-
}
160-
161-
return instance;
162-
}
163-
164-
@Nullable
165-
private <S> S readEntityFrom(ResultSet rs, RelationalPersistentProperty property, String prefix) {
166-
167-
String newPrefix = prefix + property.getName() + "_";
168-
169-
@SuppressWarnings("unchecked")
170-
RelationalPersistentEntity<S> entity = (RelationalPersistentEntity<S>) context
171-
.getRequiredPersistentEntity(property.getActualType());
172-
173-
RelationalPersistentProperty idProperty = entity.getIdProperty();
174-
175-
Object idValue = null;
176-
177-
if (idProperty != null) {
178-
idValue = readFrom(rs, idProperty, newPrefix);
179-
}
180-
181-
if ((idProperty != null //
182-
? idValue //
183-
: getObjectFromResultSet(rs, newPrefix + property.getReverseColumnName()) //
184-
) == null) {
185-
return null;
186-
}
187-
188-
S instance = createInstance(entity, rs, idValue, newPrefix);
189-
190-
PersistentPropertyAccessor<S> accessor = converter.getPropertyAccessor(entity, instance);
191-
192-
for (RelationalPersistentProperty p : entity) {
193-
accessor.setProperty(p, readOrLoadProperty(rs, idValue, p, newPrefix));
194-
}
195-
196-
return instance;
29+
public EntityRowMapper(RelationalPersistentEntity<T> entity, JdbcConverter converter,
30+
DataAccessStrategy accessStrategy) {
31+
super(entity, converter, accessStrategy);
19732
}
19833

199-
@Nullable
200-
private Object getObjectFromResultSet(ResultSet rs, String backreferenceName) {
201-
202-
try {
203-
return rs.getObject(backreferenceName);
204-
} catch (SQLException o_O) {
205-
throw new MappingException(String.format("Could not read value %s from result set!", backreferenceName), o_O);
206-
}
207-
}
208-
209-
private <S> S createInstance(RelationalPersistentEntity<S> entity, ResultSet rs, @Nullable Object idValue,
210-
String prefix) {
211-
212-
return converter.createInstance(entity, parameter -> {
213-
214-
String parameterName = parameter.getName();
215-
216-
Assert.notNull(parameterName, "A constructor parameter name must not be null to be used with Spring Data JDBC");
217-
218-
RelationalPersistentProperty property = entity.getRequiredPersistentProperty(parameterName);
219-
220-
return readOrLoadProperty(rs, idValue, property, prefix);
221-
});
222-
}
22334
}

0 commit comments

Comments
 (0)