diff --git a/src/main/java/org/springframework/data/jdbc/core/CascadingDataAccessStrategy.java b/src/main/java/org/springframework/data/jdbc/core/CascadingDataAccessStrategy.java index 1bfbde3c..52424e6c 100644 --- a/src/main/java/org/springframework/data/jdbc/core/CascadingDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/core/CascadingDataAccessStrategy.java @@ -39,61 +39,109 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy { this.strategies = new ArrayList<>(strategies); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map) + */ @Override public void insert(T instance, Class domainType, Map additionalParameters) { collectVoid(das -> das.insert(instance, domainType, additionalParameters)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#update(java.lang.Object, java.lang.Class) + */ @Override public boolean update(S instance, Class domainType) { return collect(das -> das.update(instance, domainType)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, java.lang.Class) + */ @Override public void delete(Object id, Class domainType) { collectVoid(das -> das.delete(id, domainType)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, org.springframework.data.mapping.PersistentPropertyPath) + */ @Override public void delete(Object rootId, PersistentPropertyPath propertyPath) { collectVoid(das -> das.delete(rootId, propertyPath)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteAll(java.lang.Class) + */ @Override public void deleteAll(Class domainType) { collectVoid(das -> das.deleteAll(domainType)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteAll(org.springframework.data.mapping.PersistentPropertyPath) + */ @Override public void deleteAll(PersistentPropertyPath propertyPath) { collectVoid(das -> das.deleteAll(propertyPath)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#count(java.lang.Class) + */ @Override public long count(Class domainType) { return collect(das -> das.count(domainType)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class) + */ @Override public T findById(Object id, Class domainType) { return collect(das -> das.findById(id, domainType)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class) + */ @Override public Iterable findAll(Class domainType) { return collect(das -> das.findAll(domainType)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findAllById(java.lang.Iterable, java.lang.Class) + */ @Override public Iterable findAllById(Iterable ids, Class domainType) { return collect(das -> das.findAllById(ids, domainType)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findAllByProperty(java.lang.Object, org.springframework.data.relational.core.mapping.RelationalPersistentProperty) + */ @Override public Iterable findAllByProperty(Object rootId, RelationalPersistentProperty property) { return collect(das -> das.findAllByProperty(rootId, property)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#existsById(java.lang.Object, java.lang.Class) + */ @Override public boolean existsById(Object id, Class domainType) { return collect(das -> das.existsById(id, domainType)); @@ -112,5 +160,4 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy { return null; }); } - } diff --git a/src/main/java/org/springframework/data/jdbc/core/DataAccessStrategy.java b/src/main/java/org/springframework/data/jdbc/core/DataAccessStrategy.java index 7c1175f3..637bdfd8 100644 --- a/src/main/java/org/springframework/data/jdbc/core/DataAccessStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/core/DataAccessStrategy.java @@ -33,7 +33,7 @@ public interface DataAccessStrategy { /** * Inserts a the data of a single entity. Referenced entities don't get handled. - * + * * @param instance the instance to be stored. Must not be {@code null}. * @param domainType the type of the instance. Must not be {@code null}. * @param additionalParameters name-value pairs of additional parameters. Especially ids of parent entities that need @@ -48,7 +48,7 @@ public interface DataAccessStrategy { * @param instance the instance to save. Must not be {@code null}. * @param domainType the type of the instance to save. Must not be {@code null}. * @param the type of the instance to save. - * @return wether the update actually updated a row. + * @return whether the update actually updated a row. */ boolean update(T instance, Class domainType); @@ -64,7 +64,7 @@ public interface DataAccessStrategy { /** * Deletes all entities reachable via {@literal propertyPath} from the instance identified by {@literal rootId}. - * + * * @param rootId Id of the root object on which the {@literal propertyPath} is based. Must not be {@code null}. * @param propertyPath Leading from the root object to the entities to be deleted. Must not be {@code null}. */ @@ -72,7 +72,7 @@ public interface DataAccessStrategy { /** * Deletes all entities of the given domain type. - * + * * @param domainType the domain type for which to delete all entries. Must not be {@code null}. * @param type of the domain type. */ @@ -105,7 +105,7 @@ public interface DataAccessStrategy { T findById(Object id, Class domainType); /** - * Loads alls entities of the given type. + * Loads all entities of the given type. * * @param domainType the type of entities to load. Must not be {@code null}. * @param the type of entities to load. @@ -116,7 +116,7 @@ public interface DataAccessStrategy { /** * Loads all entities that match one of the ids passed as an argument. It is not guaranteed that the number of ids * passed in matches the number of entities returned. - * + * * @param ids the Ids of the entities to load. Must not be {@code null}. * @param domainType the type of entities to laod. Must not be {@code null}. * @param type of entities to load. @@ -134,7 +134,7 @@ public interface DataAccessStrategy { /** * returns if a row with the given id exists for the given type. - * + * * @param id the id of the entity for which to check. Must not be {@code null}. * @param domainType the type of the entity to check for. Must not be {@code null}. * @param the type of the entity. diff --git a/src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java b/src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java index 639daf34..9cea41e2 100644 --- a/src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/core/DefaultDataAccessStrategy.java @@ -19,6 +19,7 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -83,6 +84,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { KeyHolder holder = new GeneratedKeyHolder(); RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType); + Map parameters = new LinkedHashMap<>(additionalParameters); MapSqlParameterSource parameterSource = getPropertyMap(instance, persistentEntity); @@ -93,14 +95,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { Assert.notNull(idProperty, "Since we have a non-null idValue, we must have an idProperty as well."); - additionalParameters.put(idProperty.getColumnName(), + parameters.put(idProperty.getColumnName(), converter.writeValue(idValue, ClassTypeInformation.from(idProperty.getColumnType()))); } - additionalParameters.forEach(parameterSource::addValue); + parameters.forEach(parameterSource::addValue); operations.update( // - sql(domainType).getInsert(additionalParameters.keySet()), // + sql(domainType).getInsert(parameters.keySet()), // parameterSource, // holder // ); @@ -345,23 +347,23 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { } catch (InvalidDataAccessApiUsageException e) { // Postgres returns a value for each column Map keys = holder.getKeys(); - return Optional.ofNullable( // - keys == null || persistentEntity.getIdProperty() == null // - ? null // - : keys.get(persistentEntity.getIdColumn())); + + if (keys == null || persistentEntity.getIdProperty() == null) { + return Optional.empty(); + } + + return Optional.ofNullable(keys.get(persistentEntity.getIdColumn())); } } - public EntityRowMapper getEntityRowMapper(Class domainType) { + private EntityRowMapper getEntityRowMapper(Class domainType) { return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), context, converter, accessStrategy); } - @SuppressWarnings("unchecked") private RowMapper getMapEntityRowMapper(RelationalPersistentProperty property) { String keyColumn = property.getKeyColumn(); - - Assert.notNull(keyColumn, () -> "keyColumn must not be null for " + property); + Assert.notNull(keyColumn, () -> "KeyColumn must not be null for " + property); return new MapEntityRowMapper<>(getEntityRowMapper(property.getActualType()), keyColumn); } diff --git a/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java b/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java index a79de277..b6fa521d 100644 --- a/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java +++ b/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java @@ -15,6 +15,9 @@ */ package org.springframework.data.jdbc.core; +import lombok.RequiredArgsConstructor; + +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -40,39 +43,55 @@ import org.springframework.lang.Nullable; * interactions. * * @author Jens Schauder + * @author Mark Paluch * @since 1.0 */ +@RequiredArgsConstructor class DefaultJdbcInterpreter implements Interpreter { private final RelationalMappingContext context; private final DataAccessStrategy accessStrategy; - DefaultJdbcInterpreter(RelationalMappingContext context, DataAccessStrategy accessStrategy) { - - this.context = context; - this.accessStrategy = accessStrategy; - } - + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.Insert) + */ @Override public void interpret(Insert insert) { accessStrategy.insert(insert.getEntity(), insert.getEntityType(), createAdditionalColumnValues(insert)); } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.InsertRoot) + */ @Override public void interpret(InsertRoot insert) { - accessStrategy.insert(insert.getEntity(), insert.getEntityType(), new HashMap<>()); + accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Collections.emptyMap()); } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.Update) + */ @Override public void interpret(Update update) { accessStrategy.update(update.getEntity(), update.getEntityType()); } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.UpdateRoot) + */ @Override public void interpret(UpdateRoot update) { accessStrategy.update(update.getEntity(), update.getEntityType()); } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.Merge) + */ @Override public void interpret(Merge merge) { @@ -82,21 +101,37 @@ class DefaultJdbcInterpreter implements Interpreter { } } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.Delete) + */ @Override public void interpret(Delete delete) { accessStrategy.delete(delete.getRootId(), delete.getPropertyPath()); } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.DeleteRoot) + */ @Override public void interpret(DeleteRoot delete) { accessStrategy.delete(delete.getRootId(), delete.getEntityType()); } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.DeleteAll) + */ @Override public void interpret(DeleteAll delete) { accessStrategy.deleteAll(delete.getPropertyPath()); } + /* + * (non-Javadoc) + * @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.DeleteAllRoot) + */ @Override public void interpret(DeleteAllRoot deleteAllRoot) { accessStrategy.deleteAll(deleteAllRoot.getEntityType()); @@ -111,7 +146,8 @@ class DefaultJdbcInterpreter implements Interpreter { return additionalColumnValues; } - private void addDependingOnInformation(DbAction.WithDependingOn action, Map additionalColumnValues) { + private void addDependingOnInformation(DbAction.WithDependingOn action, + Map additionalColumnValues) { DbAction.WithEntity dependingOn = action.getDependingOn(); @@ -125,7 +161,8 @@ class DefaultJdbcInterpreter implements Interpreter { } @Nullable - private Object getIdFromEntityDependingOn(DbAction.WithEntity dependingOn, RelationalPersistentEntity persistentEntity) { + private Object getIdFromEntityDependingOn(DbAction.WithEntity dependingOn, + RelationalPersistentEntity persistentEntity) { return persistentEntity.getIdentifierAccessor(dependingOn.getEntity()).getIdentifier(); } diff --git a/src/main/java/org/springframework/data/jdbc/core/FunctionCollector.java b/src/main/java/org/springframework/data/jdbc/core/FunctionCollector.java index 81d05a24..c4579244 100644 --- a/src/main/java/org/springframework/data/jdbc/core/FunctionCollector.java +++ b/src/main/java/org/springframework/data/jdbc/core/FunctionCollector.java @@ -36,7 +36,7 @@ import org.springframework.dao.DataAccessException; * @author Jens Schauder * @since 1.0 */ -class FunctionCollector implements Collector.ResultOrException, T> { +class FunctionCollector implements Collector, T> { private final Function method; @@ -44,13 +44,21 @@ class FunctionCollector implements Collector supplier() { + public Supplier> supplier() { return ResultOrException::new; } + /* + * (non-Javadoc) + * @see java.util.stream.Collector#accumulator() + */ @Override - public BiConsumer accumulator() { + public BiConsumer, DataAccessStrategy> accumulator() { return (roe, das) -> { @@ -65,16 +73,24 @@ class FunctionCollector implements Collector combiner() { + public BinaryOperator> combiner() { return (roe1, roe2) -> { throw new UnsupportedOperationException("Can't combine method calls"); }; } + /* + * (non-Javadoc) + * @see java.util.stream.Collector#finisher() + */ @Override - public Function finisher() { + public Function, T> finisher() { return roe -> { @@ -86,6 +102,10 @@ class FunctionCollector implements Collector characteristics() { return Collections.emptySet(); @@ -95,7 +115,7 @@ class FunctionCollector implements Collector { private T result; private final List exceptions = new LinkedList<>(); diff --git a/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java b/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java index 0c577fd1..f301a57b 100644 --- a/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java @@ -124,12 +124,20 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { this.namespaceStrategy = namespaceStrategy; } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map) + */ @Override public void insert(T instance, Class domainType, Map additionalParameters) { sqlSession().insert(namespace(domainType) + ".insert", new MyBatisContext(null, instance, domainType, additionalParameters)); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#update(java.lang.Object, java.lang.Class) + */ @Override public boolean update(S instance, Class domainType) { @@ -137,6 +145,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { new MyBatisContext(null, instance, domainType, Collections.emptyMap())) != 0; } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, java.lang.Class) + */ @Override public void delete(Object id, Class domainType) { @@ -144,6 +156,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { new MyBatisContext(id, null, domainType, Collections.emptyMap())); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, org.springframework.data.mapping.PersistentPropertyPath) + */ @Override public void delete(Object rootId, PersistentPropertyPath propertyPath) { @@ -153,6 +169,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { Collections.emptyMap())); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteAll(java.lang.Class) + */ @Override public void deleteAll(Class domainType) { @@ -162,6 +182,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { ); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteAll(org.springframework.data.mapping.PersistentPropertyPath) + */ @Override public void deleteAll(PersistentPropertyPath propertyPath) { @@ -174,24 +198,40 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { ); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class) + */ @Override public T findById(Object id, Class domainType) { return sqlSession().selectOne(namespace(domainType) + ".findById", new MyBatisContext(id, null, domainType, Collections.emptyMap())); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class) + */ @Override public Iterable findAll(Class domainType) { return sqlSession().selectList(namespace(domainType) + ".findAll", new MyBatisContext(null, null, domainType, Collections.emptyMap())); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findAllById(java.lang.Iterable, java.lang.Class) + */ @Override public Iterable findAllById(Iterable ids, Class domainType) { return sqlSession().selectList(namespace(domainType) + ".findAllById", new MyBatisContext(ids, null, domainType, Collections.emptyMap())); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#findAllByProperty(java.lang.Object, org.springframework.data.relational.core.mapping.RelationalPersistentProperty) + */ @Override public Iterable findAllByProperty(Object rootId, RelationalPersistentProperty property) { return sqlSession().selectList( @@ -199,12 +239,20 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap())); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#existsById(java.lang.Object, java.lang.Class) + */ @Override public boolean existsById(Object id, Class domainType) { return sqlSession().selectOne(namespace(domainType) + ".existsById", new MyBatisContext(id, null, domainType, Collections.emptyMap())); } + /* + * (non-Javadoc) + * @see org.springframework.data.jdbc.core.DataAccessStrategy#count(java.lang.Class) + */ @Override public long count(Class domainType) { return sqlSession().selectOne(namespace(domainType) + ".count", @@ -219,7 +267,7 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { return this.sqlSession; } - private String toDashPath(PersistentPropertyPath propertyPath) { + private static String toDashPath(PersistentPropertyPath propertyPath) { return propertyPath.toDotPath().replaceAll("\\.", "-"); } } diff --git a/src/main/java/org/springframework/data/relational/core/conversion/AggregateChange.java b/src/main/java/org/springframework/data/relational/core/conversion/AggregateChange.java index 9b261052..331beb1b 100644 --- a/src/main/java/org/springframework/data/relational/core/conversion/AggregateChange.java +++ b/src/main/java/org/springframework/data/relational/core/conversion/AggregateChange.java @@ -25,6 +25,7 @@ import java.util.List; * Represents the change happening to the aggregate (as used in the context of Domain Driven Design) as a whole. * * @author Jens Schauder + * @author Mark Paluch * @since 1.0 */ @RequiredArgsConstructor @@ -39,13 +40,13 @@ public class AggregateChange { /** Aggregate root, to which the change applies, if available */ private final T entity; - private final List actions = new ArrayList<>(); + private final List> actions = new ArrayList<>(); public void executeWith(Interpreter interpreter) { actions.forEach(a -> a.executeWith(interpreter)); } - public void addAction(DbAction action) { + public void addAction(DbAction action) { actions.add(action); } diff --git a/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java b/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java index 7bd160b7..acc38c30 100644 --- a/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java +++ b/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityDeleteWriter.java @@ -30,6 +30,7 @@ import org.springframework.util.Assert; * be executed against the database to recreate the appropriate state in the database. * * @author Jens Schauder + * @author Mark Paluch * @since 1.0 */ public class RelationalEntityDeleteWriter implements EntityWriter> { @@ -47,7 +48,7 @@ public class RelationalEntityDeleteWriter implements EntityWriter aggregateChange) { if (id == null) { - deleteAll(aggregateChange); + deleteAll(aggregateChange.getEntityType()).forEach(aggregateChange::addAction); } else { - deleteById(id, aggregateChange); + deleteById(id, aggregateChange).forEach(aggregateChange::addAction); } } - private void deleteAll(AggregateChange aggregateChange) { + private List> deleteAll(Class entityType) { - List actions = new ArrayList<>(); + List> actions = new ArrayList<>(); - context.findPersistentPropertyPaths(aggregateChange.getEntityType(), PersistentProperty::isEntity) + context.findPersistentPropertyPaths(entityType, PersistentProperty::isEntity) .forEach(p -> actions.add(new DbAction.DeleteAll<>(p))); Collections.reverse(actions); - actions.forEach(aggregateChange::addAction); + DbAction.DeleteAllRoot result = new DbAction.DeleteAllRoot<>(entityType); + actions.add(result); - DbAction.DeleteAllRoot result = new DbAction.DeleteAllRoot<>(aggregateChange.getEntityType()); - aggregateChange.addAction(result); + return actions; } - private void deleteById(Object id, AggregateChange aggregateChange) { + private List> deleteById(Object id, AggregateChange aggregateChange) { - deleteReferencedEntities(id, aggregateChange); + List> actions = new ArrayList<>(deleteReferencedEntities(id, aggregateChange)); + actions.add(new DbAction.DeleteRoot<>(aggregateChange.getEntityType(), id)); - aggregateChange.addAction(new DbAction.DeleteRoot<>(aggregateChange.getEntityType(), id)); + return actions; } /** - * add {@link DbAction.Delete} actions to the {@link AggregateChange} for deleting all referenced entities. + * Add {@link DbAction.Delete} actions to the {@link AggregateChange} for deleting all referenced entities. * * @param id id of the aggregate root, of which the referenced entities get deleted. * @param aggregateChange the change object to which the actions should get added. Must not be {@code null} */ - private void deleteReferencedEntities(Object id, AggregateChange aggregateChange) { + private List> deleteReferencedEntities(Object id, AggregateChange aggregateChange) { - List actions = new ArrayList<>(); + List> actions = new ArrayList<>(); context.findPersistentPropertyPaths(aggregateChange.getEntityType(), PersistentProperty::isEntity) .forEach(p -> actions.add(new DbAction.Delete<>(id, p))); Collections.reverse(actions); - actions.forEach(aggregateChange::addAction); + return actions; } } diff --git a/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityWriter.java b/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityWriter.java index 3c94a7aa..78757fa2 100644 --- a/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityWriter.java +++ b/src/main/java/org/springframework/data/relational/core/conversion/RelationalEntityWriter.java @@ -15,8 +15,8 @@ */ package org.springframework.data.relational.core.conversion; -import lombok.NonNull; -import lombok.Value; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.util.ArrayList; import java.util.Collection; @@ -31,6 +31,7 @@ import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.PersistentPropertyPaths; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; +import org.springframework.data.util.Pair; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -38,6 +39,7 @@ import org.springframework.util.Assert; * Converts an aggregate represented by its root into an {@link AggregateChange}. * * @author Jens Schauder + * @author Mark Paluch * @since 1.0 */ public class RelationalEntityWriter implements EntityWriter> { @@ -48,10 +50,16 @@ public class RelationalEntityWriter implements EntityWriter aggregateChange) { - new WritingContext(root, aggregateChange).write(); + List> actions = new WritingContext(root, aggregateChange).write(); + + actions.forEach(aggregateChange::addAction); } /** @@ -60,7 +68,8 @@ public class RelationalEntityWriter implements EntityWriter aggregateChange; + private final Object entity; + private final Class entityType; private final PersistentPropertyPaths paths; private final Map previousActions = new HashMap<>(); private Map, List> nodesCache = new HashMap<>(); @@ -68,89 +77,99 @@ public class RelationalEntityWriter implements EntityWriter aggregateChange) { this.root = root; - this.aggregateChange = aggregateChange; - - paths = context.findPersistentPropertyPaths(aggregateChange.getEntityType(), PersistentProperty::isEntity); - + this.entity = aggregateChange.getEntity(); + this.entityType = aggregateChange.getEntityType(); + this.paths = context.findPersistentPropertyPaths(entityType, PersistentProperty::isEntity); } - private void write() { + private List> write() { + List> actions = new ArrayList<>(); if (isNew(root)) { - setRootAction(new DbAction.InsertRoot<>(aggregateChange.getEntity())); - insertReferenced(); + actions.add(setRootAction(new DbAction.InsertRoot<>(entity))); + actions.addAll(insertReferenced()); } else { - deleteReferenced(); - setRootAction(new DbAction.UpdateRoot<>(aggregateChange.getEntity())); - insertReferenced(); + actions.addAll(deleteReferenced()); + actions.add(setRootAction(new DbAction.UpdateRoot<>(entity))); + actions.addAll(insertReferenced()); } + + return actions; } //// Operations on all paths - private void insertReferenced() { + private List> insertReferenced() { - paths.forEach(this::insertAll); + List> actions = new ArrayList<>(); + paths.forEach(path -> actions.addAll(insertAll(path))); + + return actions; } - private void insertAll(PersistentPropertyPath path) { + private List> insertAll(PersistentPropertyPath path) { + + List> actions = new ArrayList<>(); from(path).forEach(node -> { DbAction.Insert insert; if (node.path.getRequiredLeafProperty().isQualified()) { - KeyValue value = (KeyValue) node.getValue(); - insert = new DbAction.Insert<>(value.value, path, getAction(node.parent)); - insert.getAdditionalValues().put(node.path.getRequiredLeafProperty().getKeyColumn(), value.key); + Pair value = (Pair) node.getValue(); + insert = new DbAction.Insert<>(value.getSecond(), path, getAction(node.parent)); + insert.getAdditionalValues().put(node.path.getRequiredLeafProperty().getKeyColumn(), value.getFirst()); } else { insert = new DbAction.Insert<>(node.getValue(), path, getAction(node.parent)); } previousActions.put(node, insert); - aggregateChange.addAction(insert); + actions.add(insert); }); + + return actions; } - private void deleteReferenced() { + private List> deleteReferenced() { - ArrayList deletes = new ArrayList<>(); + List> deletes = new ArrayList<>(); paths.forEach(path -> deletes.add(0, deleteReferenced(path))); - deletes.forEach(aggregateChange::addAction); + return deletes; } /// Operations on a single path private DbAction.Delete deleteReferenced(PersistentPropertyPath path) { - Object id = context.getRequiredPersistentEntity(aggregateChange.getEntityType()) - .getIdentifierAccessor(aggregateChange.getEntity()).getIdentifier(); + Object id = context.getRequiredPersistentEntity(entityType).getIdentifierAccessor(entity).getIdentifier(); return new DbAction.Delete<>(id, path); } //// methods not directly related to the creation of DbActions - private void setRootAction(DbAction insert) { + private DbAction setRootAction(DbAction dbAction) { - previousActions.put(null, insert); - aggregateChange.addAction(insert); + previousActions.put(null, dbAction); + return dbAction; } @Nullable private DbAction.WithEntity getAction(@Nullable PathNode parent) { DbAction action = previousActions.get(parent); + if (action != null) { Assert.isInstanceOf(DbAction.WithEntity.class, action, "dependsOn action is not a WithEntity, but " + action.getClass().getSimpleName()); return (DbAction.WithEntity) action; } + return null; } @@ -161,11 +180,12 @@ public class RelationalEntityWriter implements EntityWriter from(PersistentPropertyPath path) { List nodes = new ArrayList<>(); + if (path.getLength() == 1) { Object value = context // - .getRequiredPersistentEntity(aggregateChange.getEntityType()) // - .getPropertyAccessor(aggregateChange.getEntity()) // + .getRequiredPersistentEntity(entityType) // + .getPropertyAccessor(entity) // .getProperty(path.getRequiredLeafProperty()); nodes.addAll(createNodes(path, null, value)); @@ -185,7 +205,6 @@ public class RelationalEntityWriter implements EntityWriter createNodes(PersistentPropertyPath path, @@ -200,12 +219,12 @@ public class RelationalEntityWriter implements EntityWriter) value).forEach((k, v) -> nodes.add(new PathNode(path, parentNode, new KeyValue(k, v)))); + ((Map) value).forEach((k, v) -> nodes.add(new PathNode(path, parentNode, Pair.of(k, v)))); } else { List listValue = (List) value; for (int k = 0; k < listValue.size(); k++) { - nodes.add(new PathNode(path, parentNode, new KeyValue(k, listValue.get(k)))); + nodes.add(new PathNode(path, parentNode, Pair.of(k, listValue.get(k)))); } } } else if (path.getRequiredLeafProperty().isCollectionLike()) { // collection value @@ -218,37 +237,16 @@ public class RelationalEntityWriter implements EntityWriter path; - @Nullable private final PathNode parent; + private final @Nullable PathNode parent; private final Object value; - - private PathNode(PersistentPropertyPath path, @Nullable PathNode parent, - Object value) { - - this.path = path; - this.parent = parent; - this.value = value; - } - - public Object getValue() { - return value; - } - } } - - /** - * Holds key and value of a {@link Map.Entry} but without any ties to {@link Map} implementations. - */ - @Value - private static class KeyValue { - @NonNull Object key; - @NonNull Object value; - } - } diff --git a/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java b/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java index 58da3f33..7d52275d 100644 --- a/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java +++ b/src/test/java/org/springframework/data/jdbc/core/MyBatisDataAccessStrategyUnitTests.java @@ -50,7 +50,6 @@ public class MyBatisDataAccessStrategyUnitTests { RelationalMappingContext context = this.context; return PropertyPathUtils.toPath(path, source, context); - } @Before