DATAJDBC-227 - Polishing.
Return List<DbChange<?>> in RelationalEntityWriters so AggregateChange can be assembled in less places to reduce the number of abstractions per method. Change ResultOrException in FunctionCollector to static class so instances no longer retain a reference to their enclosing class. Fix DbAction generics in AggregateChange. Replace simple constructors with Lombok usage. Javadoc, typos, formatting. Original pull request: #79.
This commit is contained in:
@@ -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 <T> void insert(T instance, Class<T> domainType, Map<String, Object> 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 <S> boolean update(S instance, Class<S> 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<RelationalPersistentProperty> propertyPath) {
|
||||
collectVoid(das -> das.delete(rootId, propertyPath));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteAll(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> void deleteAll(Class<T> 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<RelationalPersistentProperty> 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> T findById(Object id, Class<T> domainType) {
|
||||
return collect(das -> das.findById(id, domainType));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> 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 <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> 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 <T> Iterable<T> 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 <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
return collect(das -> das.existsById(id, domainType));
|
||||
@@ -112,5 +160,4 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <T> the type of the instance to save.
|
||||
* @return wether the update actually updated a row.
|
||||
* @return whether the update actually updated a row.
|
||||
*/
|
||||
<T> boolean update(T instance, Class<T> 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 <T> type of the domain type.
|
||||
*/
|
||||
@@ -105,7 +105,7 @@ public interface DataAccessStrategy {
|
||||
<T> T findById(Object id, Class<T> 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 <T> 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 <T> 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 <T> the type of the entity.
|
||||
|
||||
@@ -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<T> persistentEntity = getRequiredPersistentEntity(domainType);
|
||||
Map<String, Object> 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<String, Object> 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);
|
||||
}
|
||||
|
||||
@@ -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 <T> void interpret(Insert<T> 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 <T> void interpret(InsertRoot<T> 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 <T> void interpret(Update<T> 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 <T> void interpret(UpdateRoot<T> 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 <T> void interpret(Merge<T> 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 <T> void interpret(Delete<T> 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 <T> void interpret(DeleteRoot<T> 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 <T> void interpret(DeleteAll<T> 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 <T> void interpret(DeleteAllRoot<T> deleteAllRoot) {
|
||||
accessStrategy.deleteAll(deleteAllRoot.getEntityType());
|
||||
@@ -111,7 +146,8 @@ class DefaultJdbcInterpreter implements Interpreter {
|
||||
return additionalColumnValues;
|
||||
}
|
||||
|
||||
private <T> void addDependingOnInformation(DbAction.WithDependingOn<T> action, Map<String, Object> additionalColumnValues) {
|
||||
private <T> void addDependingOnInformation(DbAction.WithDependingOn<T> action,
|
||||
Map<String, Object> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.springframework.dao.DataAccessException;
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
*/
|
||||
class FunctionCollector<T> implements Collector<DataAccessStrategy, FunctionCollector<T>.ResultOrException, T> {
|
||||
class FunctionCollector<T> implements Collector<DataAccessStrategy, FunctionCollector.ResultOrException<T>, T> {
|
||||
|
||||
private final Function<DataAccessStrategy, T> method;
|
||||
|
||||
@@ -44,13 +44,21 @@ class FunctionCollector<T> implements Collector<DataAccessStrategy, FunctionColl
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#supplier()
|
||||
*/
|
||||
@Override
|
||||
public Supplier<ResultOrException> supplier() {
|
||||
public Supplier<ResultOrException<T>> supplier() {
|
||||
return ResultOrException::new;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#accumulator()
|
||||
*/
|
||||
@Override
|
||||
public BiConsumer<ResultOrException, DataAccessStrategy> accumulator() {
|
||||
public BiConsumer<ResultOrException<T>, DataAccessStrategy> accumulator() {
|
||||
|
||||
return (roe, das) -> {
|
||||
|
||||
@@ -65,16 +73,24 @@ class FunctionCollector<T> implements Collector<DataAccessStrategy, FunctionColl
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#combiner()
|
||||
*/
|
||||
@Override
|
||||
public BinaryOperator<ResultOrException> combiner() {
|
||||
public BinaryOperator<ResultOrException<T>> combiner() {
|
||||
|
||||
return (roe1, roe2) -> {
|
||||
throw new UnsupportedOperationException("Can't combine method calls");
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#finisher()
|
||||
*/
|
||||
@Override
|
||||
public Function<ResultOrException, T> finisher() {
|
||||
public Function<ResultOrException<T>, T> finisher() {
|
||||
|
||||
return roe -> {
|
||||
|
||||
@@ -86,6 +102,10 @@ class FunctionCollector<T> implements Collector<DataAccessStrategy, FunctionColl
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.util.stream.Collector#characteristics()
|
||||
*/
|
||||
@Override
|
||||
public Set<Characteristics> characteristics() {
|
||||
return Collections.emptySet();
|
||||
@@ -95,7 +115,7 @@ class FunctionCollector<T> implements Collector<DataAccessStrategy, FunctionColl
|
||||
* Stores intermediate results. I.e. a list of exceptions caught so far, any actual result and the fact, if there
|
||||
* actually is an result.
|
||||
*/
|
||||
class ResultOrException {
|
||||
static class ResultOrException<T> {
|
||||
|
||||
private T result;
|
||||
private final List<Exception> exceptions = new LinkedList<>();
|
||||
|
||||
@@ -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 <T> void insert(T instance, Class<T> domainType, Map<String, Object> 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 <S> boolean update(S instance, Class<S> 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<RelationalPersistentProperty> 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 <T> void deleteAll(Class<T> 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<RelationalPersistentProperty> 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> T findById(Object id, Class<T> 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 <T> Iterable<T> findAll(Class<T> 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 <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> 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 <T> Iterable<T> 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 <T> boolean existsById(Object id, Class<T> 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<RelationalPersistentProperty> propertyPath) {
|
||||
private static String toDashPath(PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
|
||||
return propertyPath.toDotPath().replaceAll("\\.", "-");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T> {
|
||||
/** Aggregate root, to which the change applies, if available */
|
||||
private final T entity;
|
||||
|
||||
private final List<DbAction> actions = new ArrayList<>();
|
||||
private final List<DbAction<?>> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Object, AggregateChange<?>> {
|
||||
@@ -47,7 +48,7 @@ public class RelationalEntityDeleteWriter implements EntityWriter<Object, Aggreg
|
||||
* Fills the provided {@link AggregateChange} with the necessary {@link DbAction}s to delete the aggregate root
|
||||
* identified by {@code id}. If {@code id} is {@code null} it is interpreted as "Delete all aggregates of the type
|
||||
* indicated by the aggregateChange".
|
||||
*
|
||||
*
|
||||
* @param id May be {@code null}.
|
||||
* @param aggregateChange Must not be {@code null}.
|
||||
*/
|
||||
@@ -55,49 +56,50 @@ public class RelationalEntityDeleteWriter implements EntityWriter<Object, Aggreg
|
||||
public void write(@Nullable Object id, AggregateChange<?> 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<DbAction<?>> deleteAll(Class<?> entityType) {
|
||||
|
||||
List<DbAction> actions = new ArrayList<>();
|
||||
List<DbAction<?>> 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 <T> void deleteById(Object id, AggregateChange<T> aggregateChange) {
|
||||
private <T> List<DbAction<?>> deleteById(Object id, AggregateChange<T> aggregateChange) {
|
||||
|
||||
deleteReferencedEntities(id, aggregateChange);
|
||||
List<DbAction<?>> 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<DbAction<?>> deleteReferencedEntities(Object id, AggregateChange<?> aggregateChange) {
|
||||
|
||||
List<DbAction> actions = new ArrayList<>();
|
||||
List<DbAction<?>> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object, AggregateChange<?>> {
|
||||
@@ -48,10 +50,16 @@ public class RelationalEntityWriter implements EntityWriter<Object, AggregateCha
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.EntityWriter#write(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void write(Object root, AggregateChange<?> aggregateChange) {
|
||||
|
||||
new WritingContext(root, aggregateChange).write();
|
||||
List<DbAction<?>> actions = new WritingContext(root, aggregateChange).write();
|
||||
|
||||
actions.forEach(aggregateChange::addAction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +68,8 @@ public class RelationalEntityWriter implements EntityWriter<Object, AggregateCha
|
||||
private class WritingContext {
|
||||
|
||||
private final Object root;
|
||||
private final AggregateChange<?> aggregateChange;
|
||||
private final Object entity;
|
||||
private final Class<?> entityType;
|
||||
private final PersistentPropertyPaths<?, RelationalPersistentProperty> paths;
|
||||
private final Map<PathNode, DbAction> previousActions = new HashMap<>();
|
||||
private Map<PersistentPropertyPath<RelationalPersistentProperty>, List<PathNode>> nodesCache = new HashMap<>();
|
||||
@@ -68,89 +77,99 @@ public class RelationalEntityWriter implements EntityWriter<Object, AggregateCha
|
||||
WritingContext(Object root, AggregateChange<?> 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<DbAction<?>> write() {
|
||||
|
||||
List<DbAction<?>> 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<DbAction<?>> insertReferenced() {
|
||||
|
||||
paths.forEach(this::insertAll);
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
paths.forEach(path -> actions.addAll(insertAll(path)));
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private void insertAll(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
private List<DbAction<?>> insertAll(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
from(path).forEach(node -> {
|
||||
|
||||
DbAction.Insert<Object> 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<Object, Object> 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<DbAction<?>> deleteReferenced() {
|
||||
|
||||
ArrayList<DbAction> deletes = new ArrayList<>();
|
||||
List<DbAction<?>> 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<RelationalPersistentProperty> 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<Object, AggregateCha
|
||||
private List<WritingContext.PathNode> from(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
List<WritingContext.PathNode> 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<Object, AggregateCha
|
||||
nodesCache.put(path, nodes);
|
||||
|
||||
return nodes;
|
||||
|
||||
}
|
||||
|
||||
private List<PathNode> createNodes(PersistentPropertyPath<RelationalPersistentProperty> path,
|
||||
@@ -200,12 +219,12 @@ public class RelationalEntityWriter implements EntityWriter<Object, AggregateCha
|
||||
if (path.getRequiredLeafProperty().isQualified()) {
|
||||
|
||||
if (path.getRequiredLeafProperty().isMap()) {
|
||||
((Map<?, ?>) 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<Object, AggregateCha
|
||||
}
|
||||
|
||||
/**
|
||||
* represents a single entity in an aggregate along with its property path from the root entity and the chain of
|
||||
* Represents a single entity in an aggregate along with its property path from the root entity and the chain of
|
||||
* objects to traverse a long this path.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
private class PathNode {
|
||||
|
||||
private final PersistentPropertyPath<RelationalPersistentProperty> path;
|
||||
@Nullable private final PathNode parent;
|
||||
private final @Nullable PathNode parent;
|
||||
private final Object value;
|
||||
|
||||
private PathNode(PersistentPropertyPath<RelationalPersistentProperty> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user