DATAJDBC-130 - Support entities with List valued properties.

This commit is contained in:
Jens Schauder
2018-02-07 12:08:42 +01:00
committed by Greg Turnquist
parent 7ed2ed7d7e
commit f5de195768
13 changed files with 440 additions and 37 deletions

View File

@@ -200,11 +200,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
Class<?> actualType = property.getActualType();
String findAllByProperty = sql(actualType).getFindAllByProperty(property.getReverseColumnName(),
property.getKeyColumn());
property.getKeyColumn(), property.isOrdered());
MapSqlParameterSource parameter = new MapSqlParameterSource(property.getReverseColumnName(), rootId);
return (Iterable<T>) operations.query(findAllByProperty, parameter, property.isQualified() //
return (Iterable<T>) operations.query(findAllByProperty, parameter, property.isMap() //
? getMapEntityRowMapper(property) //
: getEntityRowMapper(actualType));
}

View File

@@ -19,10 +19,8 @@ import lombok.NonNull;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ClassGeneratingEntityInstantiator;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
@@ -45,6 +43,8 @@ import org.springframework.jdbc.core.RowMapper;
*/
public class EntityRowMapper<T> implements RowMapper<T> {
private static final Converter ITERABLE_OF_ENTRY_TO_MAP_CONVERTER = new IterableOfEntryToMapConverter();
private final JdbcPersistentEntity<T> entity;
private final EntityInstantiator instantiator = new ClassGeneratingEntityInstantiator();
private final ConversionService conversions;
@@ -79,13 +79,12 @@ public class EntityRowMapper<T> implements RowMapper<T> {
for (JdbcPersistentProperty property : entity) {
if (Set.class.isAssignableFrom(property.getType())) {
if (property.isCollectionLike()) {
propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property));
} else if (Map.class.isAssignableFrom(property.getType())) {
} else if (property.isMap()) {
Iterable<Object> allByProperty = accessStrategy.findAllByProperty(id, property);
IterableOfEntryToMapConverter converter = new IterableOfEntryToMapConverter();
propertyAccessor.setProperty(property, converter.convert(allByProperty));
propertyAccessor.setProperty(property, ITERABLE_OF_ENTRY_TO_MAP_CONVERTER.convert(allByProperty));
} else {
propertyAccessor.setProperty(property, readFrom(resultSet, property, ""));
}

View File

@@ -95,17 +95,22 @@ class SqlGenerator {
* a referencing entity.
*
* @param columnName name of the column of the FK back to the referencing entity.
* @param keyColumn if the property is of type {@link Map} this column contains the map key.
* @param keyColumn if the property is of type {@link Map} this column contains the map key.
* @param ordered whether the SQL statement should include an ORDER BY for the keyColumn. If this is {@literal true}, the keyColumn must not be {@literal null}.
* @return a SQL String.
*/
String getFindAllByProperty(String columnName, String keyColumn) {
String getFindAllByProperty(String columnName, String keyColumn, boolean ordered) {
Assert.isTrue(keyColumn != null || !ordered, "If the SQL statement should be ordered a keyColumn to order by must be provided.");
String baseSelect = (keyColumn != null) //
? createSelectBuilder().column(cb -> cb.tableAlias(entity.getTableName()).column(keyColumn).as(keyColumn))
.build()
.build()
: getFindAll();
return String.format("%s WHERE %s = :%s", baseSelect, columnName, columnName);
String orderBy = ordered ? " ORDER BY " + keyColumn : "";
return String.format("%s WHERE %s = :%s%s", baseSelect, columnName, columnName, orderBy);
}
String getExists() {

View File

@@ -16,12 +16,6 @@
package org.springframework.data.jdbc.core.conversion;
import lombok.RequiredArgsConstructor;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Stream;
import org.springframework.data.jdbc.core.conversion.DbAction.Insert;
import org.springframework.data.jdbc.core.conversion.DbAction.Update;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
@@ -33,6 +27,13 @@ import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.util.StreamUtils;
import org.springframework.util.ClassUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Stream;
/**
* Converts an entity that is about to be saved into {@link DbAction}s inside a {@link AggregateChange} that need to be
* executed against the database to recreate the appropriate state in the database.
@@ -157,6 +158,10 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
Class<?> type = p.getType();
if (List.class.isAssignableFrom(type)) {
return listPropertyAsStream(p, propertyAccessor);
}
if (Collection.class.isAssignableFrom(type)) {
return collectionPropertyAsStream(p, propertyAccessor);
}
@@ -178,6 +183,21 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
: ((Collection<Object>) property).stream();
}
private Stream<Object> listPropertyAsStream(JdbcPersistentProperty p, PersistentPropertyAccessor propertyAccessor) {
Object property = propertyAccessor.getProperty(p);
if (property == null) return Stream.empty();
List<Object> listProperty = (List<Object>) property;
HashMap<Integer, Object> map = new HashMap<>();
for (int i = 0; i < listProperty.size(); i++) {
map.put(i, listProperty.get(i));
}
return map.entrySet().stream().map(e -> (Object) e);
}
private Stream<Object> mapPropertyAsStream(JdbcPersistentProperty p, PersistentPropertyAccessor propertyAccessor) {
Object property = propertyAccessor.getProperty(p);

View File

@@ -15,12 +15,6 @@
*/
package org.springframework.data.jdbc.mapping.model;
import java.time.ZonedDateTime;
import java.time.temporal.Temporal;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
@@ -29,6 +23,13 @@ import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.time.ZonedDateTime;
import java.time.temporal.Temporal;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Meta data about a property to be used by repository implementations.
*
@@ -114,7 +115,16 @@ public class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProper
@Override
public boolean isQualified() {
return isMap();
return isMap() || isListLike();
}
private boolean isListLike() {
return isCollectionLike() && !Set.class.isAssignableFrom(this.getType());
}
@Override
public boolean isOrdered() {
return isListLike();
}
private Class columnTypeIfEntity(Class type) {

View File

@@ -51,4 +51,9 @@ public interface JdbcPersistentProperty extends PersistentProperty<JdbcPersisten
* Returns if this property is a qualified property, i.e. a property referencing multiple elements that can get picked by a key or an index.
*/
boolean isQualified();
/**
* Returns whether this property is an ordered property.
*/
boolean isOrdered();
}