DATAJDBC-137 - Fixes warnings and improves Javadoc.
All packages now use @NoNullApi. All warnings related to that fixed, except a few cases where upstream annotations are simply wrong. Added null checks. Fixed generic types where possible. Improved Javadoc. Code Formatting.
This commit is contained in:
@@ -65,7 +65,7 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
public void deleteAll(PropertyPath propertyPath) {
|
||||
collectVoid(das -> das.deleteAll(propertyPath));
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy {
|
||||
}
|
||||
|
||||
private <T> T collect(Function<DataAccessStrategy, T> function) {
|
||||
return strategies.stream().collect(new FunctionCollector<T>(function));
|
||||
return strategies.stream().collect(new FunctionCollector<>(function));
|
||||
}
|
||||
|
||||
private void collectVoid(Consumer<DataAccessStrategy> consumer) {
|
||||
|
||||
@@ -19,45 +19,108 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Abstraction for accesses to the database that should be implementable with a single SQL statement and relates to a
|
||||
* single entity as opposed to {@link JdbcEntityOperations} which provides interactions related to complete aggregates.
|
||||
* Abstraction for accesses to the database that should be implementable with a single SQL statement per method and
|
||||
* relates to a single entity as opposed to {@link JdbcAggregateOperations} which provides interactions related to
|
||||
* complete aggregates.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
*/
|
||||
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
|
||||
* to get referenced are contained in this map. Must not be {@code null}.
|
||||
* @param <T> the type of the instance.
|
||||
*/
|
||||
<T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters);
|
||||
|
||||
/**
|
||||
* Updates the data of a single entity in the database. Referenced entities don't get handled.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
<T> void update(T instance, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* deletes a single row identified by the id, from the table identified by the domainType. Does not handle cascading
|
||||
* deletes.
|
||||
*
|
||||
* @param id the id of the row to be deleted. Must not be {@code null}.
|
||||
* @param domainType the type of entity to be deleted. Implicitely determines the table to operate on. Must not be
|
||||
* {@code null}.
|
||||
*/
|
||||
void delete(Object id, Class<?> domainType);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param propertyPath Leading from the root object to the entities to be deleted.
|
||||
* @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}.
|
||||
*/
|
||||
void delete(Object rootId, PropertyPath propertyPath);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
<T> void deleteAll(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Deletes all entities reachable via {@literal propertyPath} from any instance.
|
||||
*
|
||||
* @param propertyPath Leading from the root object to the entities to be deleted.
|
||||
* @param propertyPath Leading from the root object to the entities to be deleted. Must not be {@code null}.
|
||||
*/
|
||||
<T> void deleteAll(PropertyPath propertyPath);
|
||||
void deleteAll(PropertyPath propertyPath);
|
||||
|
||||
/**
|
||||
* Counts the rows in the table representing the given domain type.
|
||||
*
|
||||
* @param domainType the domain type for which to count the elements. Must not be {@code null}.
|
||||
* @return the count. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
long count(Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Loads a single entity identified by type and id.
|
||||
*
|
||||
* @param id the id of the entity to load. Must not be {@code null}.
|
||||
* @param domainType the domain type of the entity. Must not be {@code null}.
|
||||
* @param <T> the type of the entity.
|
||||
* @return Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
<T> T findById(Object id, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Loads alls 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.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAll(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return the loaded entities. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType);
|
||||
|
||||
/**
|
||||
@@ -68,5 +131,13 @@ public interface DataAccessStrategy {
|
||||
*/
|
||||
<T> Iterable<T> findAllByProperty(Object rootId, JdbcPersistentProperty property);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return {@code true} if a matching row exists, otherwise {@code false}.
|
||||
*/
|
||||
<T> boolean existsById(Object id, Class<T> domainType);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -88,6 +89,9 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
JdbcPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
if (idValue != null) {
|
||||
|
||||
Assert.notNull(idProperty, "Since we have a non-null idValue, we must have an idProperty as well.");
|
||||
|
||||
additionalParameters.put(idProperty.getColumnName(), convert(idValue, idProperty.getColumnType()));
|
||||
}
|
||||
|
||||
@@ -167,7 +171,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
* @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteAll(org.springframework.data.mapping.PropertyPath)
|
||||
*/
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
public void deleteAll(PropertyPath propertyPath) {
|
||||
operations.getJdbcOperations().update(sql(propertyPath.getOwningType().getType()).createDeleteAllSql(propertyPath));
|
||||
}
|
||||
|
||||
@@ -177,13 +181,19 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
*/
|
||||
@Override
|
||||
public long count(Class<?> domainType) {
|
||||
return operations.getJdbcOperations().queryForObject(sql(domainType).getCount(), Long.class);
|
||||
|
||||
Long result = operations.getJdbcOperations().queryForObject(sql(domainType).getCount(), Long.class);
|
||||
|
||||
Assert.notNull(result, "The result of a count query must not be null.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findById(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
|
||||
@@ -191,7 +201,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
|
||||
try {
|
||||
return operations.queryForObject(findOneSql, parameter, getEntityRowMapper(domainType));
|
||||
return operations.queryForObject(findOneSql, parameter, (RowMapper<T>) getEntityRowMapper(domainType));
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
@@ -201,15 +211,17 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAll(java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
return operations.query(sql(domainType).getFindAll(), getEntityRowMapper(domainType));
|
||||
return operations.query(sql(domainType).getFindAll(), (RowMapper<T>) getEntityRowMapper(domainType));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.DataAccessStrategy#findAllById(java.lang.Iterable, java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
|
||||
@@ -223,7 +235,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
.collect(Collectors.toList()) //
|
||||
);
|
||||
|
||||
return operations.query(findAllInListSql, parameter, getEntityRowMapper(domainType));
|
||||
return operations.query(findAllInListSql, parameter, (RowMapper<T>) getEntityRowMapper(domainType));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -242,9 +254,10 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
|
||||
MapSqlParameterSource parameter = new MapSqlParameterSource(property.getReverseColumnName(), rootId);
|
||||
|
||||
return (Iterable<T>) operations.query(findAllByProperty, parameter, property.isMap() //
|
||||
? getMapEntityRowMapper(property) //
|
||||
: getEntityRowMapper(actualType));
|
||||
return operations.query(findAllByProperty, parameter, //
|
||||
(RowMapper<T>) (property.isMap() //
|
||||
? this.getMapEntityRowMapper(property) //
|
||||
: this.getEntityRowMapper(actualType)));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -257,7 +270,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
String existsSql = sql(domainType).getExists();
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
|
||||
return operations.queryForObject(existsSql, parameter, Boolean.class);
|
||||
Boolean result = operations.queryForObject(existsSql, parameter, Boolean.class);
|
||||
|
||||
Assert.notNull(result, "The result of an exists query must not be null");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private <S> MapSqlParameterSource getPropertyMap(final S instance, JdbcPersistentEntity<S> persistentEntity) {
|
||||
@@ -277,6 +294,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private <S, ID> ID getIdValueOrNull(S instance, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
ID idValue = (ID) persistentEntity.getIdentifierAccessor(instance).getIdentifier();
|
||||
@@ -284,7 +302,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
return isIdPropertyNullOrScalarZero(idValue, persistentEntity) ? null : idValue;
|
||||
}
|
||||
|
||||
private static <S, ID> boolean isIdPropertyNullOrScalarZero(ID idValue, JdbcPersistentEntity<S> persistentEntity) {
|
||||
private static <S, ID> boolean isIdPropertyNullOrScalarZero(@Nullable ID idValue,
|
||||
JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
JdbcPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
return idValue == null //
|
||||
@@ -319,22 +338,29 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
return Optional.ofNullable(holder.getKey());
|
||||
} catch (InvalidDataAccessApiUsageException e) {
|
||||
// Postgres returns a value for each column
|
||||
return Optional.ofNullable(holder.getKeys().get(persistentEntity.getIdColumn()));
|
||||
Map<String, Object> keys = holder.getKeys();
|
||||
return Optional.ofNullable(keys == null ? null : keys.get(persistentEntity.getIdColumn()));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> EntityRowMapper<T> getEntityRowMapper(Class<T> domainType) {
|
||||
public EntityRowMapper<?> getEntityRowMapper(Class<?> domainType) {
|
||||
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), context, instantiators, accessStrategy);
|
||||
}
|
||||
|
||||
private RowMapper getMapEntityRowMapper(JdbcPersistentProperty property) {
|
||||
return new MapEntityRowMapper(getEntityRowMapper(property.getActualType()), property.getKeyColumn());
|
||||
@SuppressWarnings("unchecked")
|
||||
private RowMapper<?> getMapEntityRowMapper(JdbcPersistentProperty property) {
|
||||
|
||||
String keyColumn = property.getKeyColumn();
|
||||
|
||||
Assert.notNull(keyColumn, () -> "keyColumn must not be null for " + property);
|
||||
|
||||
return new MapEntityRowMapper<>(getEntityRowMapper(property.getActualType()), keyColumn);
|
||||
}
|
||||
|
||||
private <T> MapSqlParameterSource createIdParameterSource(Object id, Class<T> domainType) {
|
||||
|
||||
return new MapSqlParameterSource("id",
|
||||
convert(id, getRequiredPersistentEntity(domainType).getRequiredIdProperty().getColumnType()));
|
||||
Class<?> columnType = getRequiredPersistentEntity(domainType).getRequiredIdProperty().getColumnType();
|
||||
return new MapSqlParameterSource("id", convert(id, columnType));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -342,7 +368,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
return (JdbcPersistentEntity<S>) context.getRequiredPersistentEntity(domainType);
|
||||
}
|
||||
|
||||
private <V> V convert(Object from, Class<V> to) {
|
||||
@Nullable
|
||||
private <V> V convert(@Nullable Object from, Class<V> to) {
|
||||
|
||||
if (from == null) {
|
||||
return null;
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.conversion.Interpreter;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -103,6 +104,7 @@ class DefaultJdbcInterpreter implements Interpreter {
|
||||
additionalColumnValues.put(columnName, identifier);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object getIdFromEntityDependingOn(DbAction dependingOn, JdbcPersistentEntity<?> persistentEntity) {
|
||||
return persistentEntity.getIdentifierAccessor(dependingOn.getEntity()).getIdentifier();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
public void deleteAll(PropertyPath propertyPath) {
|
||||
delegate.deleteAll(propertyPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,13 @@ import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Maps a ResultSet to an entity of type {@code T}, including entities referenced.
|
||||
* Maps a {@link ResultSet} to an entity of type {@code T}, including entities referenced.
|
||||
*
|
||||
* This {@link RowMapper} might trigger additional SQL statements in order to load other members of the same aggregate.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
@@ -72,7 +76,7 @@ public class EntityRowMapper<T> implements RowMapper<T> {
|
||||
* @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
|
||||
*/
|
||||
@Override
|
||||
public T mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
|
||||
public T mapRow(ResultSet resultSet, int rowNumber) {
|
||||
|
||||
T result = createInstance(entity, resultSet, "");
|
||||
|
||||
@@ -83,9 +87,9 @@ public class EntityRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
for (JdbcPersistentProperty property : entity) {
|
||||
|
||||
if (property.isCollectionLike()) {
|
||||
if (property.isCollectionLike() && id != null) {
|
||||
propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property));
|
||||
} else if (property.isMap()) {
|
||||
} else if (property.isMap() && id != null) {
|
||||
|
||||
Iterable<Object> allByProperty = accessStrategy.findAllByProperty(id, property);
|
||||
propertyAccessor.setProperty(property, ITERABLE_OF_ENTRY_TO_MAP_CONVERTER.convert(allByProperty));
|
||||
@@ -105,6 +109,7 @@ public class EntityRowMapper<T> implements RowMapper<T> {
|
||||
* @param prefix to be used for all column names accessed by this method. Must not be {@code null}.
|
||||
* @return the value read from the {@link ResultSet}. May be {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
private Object readFrom(ResultSet resultSet, JdbcPersistentProperty property, String prefix) {
|
||||
|
||||
try {
|
||||
@@ -120,6 +125,7 @@ public class EntityRowMapper<T> implements RowMapper<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <S> S readEntityFrom(ResultSet rs, PersistentProperty<?> property) {
|
||||
|
||||
String prefix = property.getName() + "_";
|
||||
@@ -166,7 +172,9 @@ public class EntityRowMapper<T> implements RowMapper<T> {
|
||||
@Override
|
||||
public <T> T getParameterValue(Parameter<T, JdbcPersistentProperty> parameter) {
|
||||
|
||||
String column = prefix + entity.getRequiredPersistentProperty(parameter.getName()).getColumnName();
|
||||
String parameterName = parameter.getName();
|
||||
Assert.notNull(parameterName, "A constructor parameter name must not be null to be used with Spring Data JDBC");
|
||||
String column = prefix + entity.getRequiredPersistentProperty(parameterName).getColumnName();
|
||||
|
||||
try {
|
||||
return conversionService.convert(resultSet.getObject(column), parameter.getType().getType());
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
class IterableOfEntryToMapConverter implements ConditionalConverter, Converter<Iterable<?>, Map<?, ?>> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
@Override
|
||||
public Map<?, ?> convert(Iterable<?> source) {
|
||||
@@ -58,7 +59,7 @@ class IterableOfEntryToMapConverter implements ConditionalConverter, Converter<I
|
||||
*
|
||||
* @param sourceType {@link TypeDescriptor} to convert from.
|
||||
* @param targetType {@link TypeDescriptor} to convert to.
|
||||
* @return
|
||||
* @return if the sourceType can be converted to a Map.
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Specifies a operations one can perform on a database, based on an <em>Domain Type</em>.
|
||||
*
|
||||
@@ -23,21 +25,84 @@ package org.springframework.data.jdbc.core;
|
||||
*/
|
||||
public interface JdbcAggregateOperations {
|
||||
|
||||
/**
|
||||
* Saves an instance of an aggregate, including all the members of the aggregate.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be saved. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
*/
|
||||
<T> void save(T instance);
|
||||
|
||||
/**
|
||||
* Deletes a single Aggregate including all entities contained in that aggregate.
|
||||
*
|
||||
* @param id the id of the aggregate root of the aggregate to be deleted. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root.
|
||||
* @param <T> the type of the aggregate root.
|
||||
*/
|
||||
<T> void deleteById(Object id, Class<T> domainType);
|
||||
|
||||
<T> void delete(T entity, Class<T> domainType);
|
||||
/**
|
||||
* Delete an aggregate identified by it's aggregate root.
|
||||
*
|
||||
* @param aggregateRoot to delete. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
*/
|
||||
<T> void delete(T aggregateRoot, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Delete all aggregates of a given type.
|
||||
*
|
||||
* @param domainType type of the aggregate roots to be deleted. Must not be {@code null}.
|
||||
*/
|
||||
void deleteAll(Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Counts the number of aggregates of a given type.
|
||||
*
|
||||
* @param domainType the type of the aggregates to be counted.
|
||||
* @return the number of instances stored in the database. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
long count(Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Load an aggregate from the database.
|
||||
*
|
||||
* @param id the id of the aggregate to load. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the loaded aggregate. Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
<T> T findById(Object id, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Load all aggregates of a given type that are identified by the given ids.
|
||||
*
|
||||
* @param ids of the aggregate roots identifying the aggregates to load. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate roots. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate roots. Must not be {@code null}.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Load all aggregates of a given type.
|
||||
*
|
||||
* @param domainType the type of the aggregate roots. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate roots. Must not be {@code null}.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAll(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Checks if an aggregate identified by type and id exists in the database.
|
||||
*
|
||||
* @param id the id of the aggregate root.
|
||||
* @param domainType the type of the aggregate root.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return whether the aggregate exists.
|
||||
*/
|
||||
<T> boolean existsById(Object id, Class<T> domainType);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.data.jdbc.core.mapping.event.BeforeSaveEvent;
|
||||
import org.springframework.data.jdbc.core.mapping.event.Identifier;
|
||||
import org.springframework.data.jdbc.core.mapping.event.Identifier.Specified;
|
||||
import org.springframework.data.mapping.IdentifierAccessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -68,7 +69,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
@Override
|
||||
public <T> void save(T instance) {
|
||||
|
||||
Assert.notNull(instance, "Agggregate instance must not be null!");
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
JdbcPersistentEntity<?> entity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(instance);
|
||||
@@ -83,8 +84,12 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
change.executeWith(interpreter);
|
||||
|
||||
Object identifier = identifierAccessor.getIdentifier();
|
||||
|
||||
Assert.notNull(identifier, "After saving the identifier must not be null");
|
||||
|
||||
publisher.publishEvent(new AfterSaveEvent( //
|
||||
Identifier.of(identifierAccessor.getIdentifier()), //
|
||||
Identifier.of(identifier), //
|
||||
instance, //
|
||||
change //
|
||||
));
|
||||
@@ -127,12 +132,12 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void delete(S entity, Class<S> domainType) {
|
||||
public <S> void delete(S aggregateRoot, Class<S> domainType) {
|
||||
|
||||
IdentifierAccessor identifierAccessor = context.getRequiredPersistentEntity(domainType)
|
||||
.getIdentifierAccessor(entity);
|
||||
.getIdentifierAccessor(aggregateRoot);
|
||||
|
||||
deleteTree(identifierAccessor.getRequiredIdentifier(), entity, domainType);
|
||||
deleteTree(identifierAccessor.getRequiredIdentifier(), aggregateRoot, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,7 +152,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
change.executeWith(interpreter);
|
||||
}
|
||||
|
||||
private void deleteTree(Object id, Object entity, Class<?> domainType) {
|
||||
private void deleteTree(Object id, @Nullable Object entity, Class<?> domainType) {
|
||||
|
||||
AggregateChange change = createDeletingChange(id, entity, domainType);
|
||||
|
||||
@@ -169,7 +174,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private AggregateChange createDeletingChange(Object id, Object entity, Class<?> domainType) {
|
||||
private AggregateChange createDeletingChange(Object id, @Nullable Object entity, Class<?> domainType) {
|
||||
|
||||
AggregateChange<?> aggregateChange = new AggregateChange(Kind.DELETE, domainType, entity);
|
||||
jdbcEntityDeleteWriter.write(id, aggregateChange);
|
||||
@@ -187,7 +192,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
for (T e : all) {
|
||||
|
||||
JdbcPersistentEntity<?> entity = context.getPersistentEntity(e.getClass());
|
||||
JdbcPersistentEntity<?> entity = context.getRequiredPersistentEntity(e.getClass());
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(e);
|
||||
|
||||
publishAfterLoad(identifierAccessor.getRequiredIdentifier(), e);
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* A {@link RowMapper} that maps a row to a {@link Map.Entry} so an {@link Iterable} of those can be converted to a
|
||||
@@ -36,13 +36,17 @@ class MapEntityRowMapper<T> implements RowMapper<Map.Entry<Object, T>> {
|
||||
private final RowMapper<T> delegate;
|
||||
private final String keyColumn;
|
||||
|
||||
/**
|
||||
* @param delegate rowmapper used as a delegate for obtaining the map values.
|
||||
* @param keyColumn the name of the key column.
|
||||
*/
|
||||
MapEntityRowMapper(RowMapper<T> delegate, String keyColumn) {
|
||||
|
||||
this.delegate = delegate;
|
||||
this.keyColumn = keyColumn;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NonNull
|
||||
@Override
|
||||
public Map.Entry<Object, T> mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
return new HashMap.SimpleEntry<>(rs.getObject(keyColumn), delegate.mapRow(rs, rowNum));
|
||||
|
||||
@@ -36,29 +36,60 @@ class SelectBuilder {
|
||||
private final List<Join> joins = new ArrayList<>();
|
||||
private final List<WhereCondition> conditions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates a {@link SelectBuilder} using the given table name.
|
||||
*
|
||||
* @param tableName the table name. Must not be {@code null}.
|
||||
*/
|
||||
SelectBuilder(String tableName) {
|
||||
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a column to the select list.
|
||||
*
|
||||
* @param columnSpec a function that specifies the column to add. The passed in {@link Column.ColumnBuilder} allows to
|
||||
* specify details like alias and the source table. Must not be {@code null}.
|
||||
* @return {@code this}.
|
||||
*/
|
||||
SelectBuilder column(Function<Column.ColumnBuilder, Column.ColumnBuilder> columnSpec) {
|
||||
|
||||
columns.add(columnSpec.apply(Column.builder()).build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a where clause to the select
|
||||
*
|
||||
* @param whereSpec a function specifying the details of the where clause by manipulating the passed in
|
||||
* {@link WhereConditionBuilder}. Must not be {@code null}.
|
||||
* @return {@code this}.
|
||||
*/
|
||||
SelectBuilder where(Function<WhereConditionBuilder, WhereConditionBuilder> whereSpec) {
|
||||
|
||||
conditions.add(whereSpec.apply(new WhereConditionBuilder(this)).build());
|
||||
conditions.add(whereSpec.apply(new WhereConditionBuilder()).build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a join to the select.
|
||||
*
|
||||
* @param joinSpec a function specifying the details of the join by manipulating the passed in
|
||||
* {@link Join.JoinBuilder}. Must not be {@code null}.
|
||||
* @return {@code this}.
|
||||
*/
|
||||
SelectBuilder join(Function<Join.JoinBuilder, Join.JoinBuilder> joinSpec) {
|
||||
|
||||
joins.add(joinSpec.apply(Join.builder()).build());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the actual SQL statement.
|
||||
*
|
||||
* @return a SQL statement. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
String build() {
|
||||
|
||||
return selectFrom() + joinClause() + whereClause();
|
||||
@@ -73,7 +104,7 @@ class SelectBuilder {
|
||||
return conditions.stream() //
|
||||
.map(WhereCondition::toSql) //
|
||||
.collect(Collectors.joining("AND", " WHERE ", "") //
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
private String joinClause() {
|
||||
@@ -102,14 +133,11 @@ class SelectBuilder {
|
||||
|
||||
private String fromTable;
|
||||
private String fromColumn;
|
||||
private final SelectBuilder selectBuilder;
|
||||
|
||||
private String operation = "=";
|
||||
private String expression;
|
||||
|
||||
WhereConditionBuilder(SelectBuilder selectBuilder) {
|
||||
this.selectBuilder = selectBuilder;
|
||||
}
|
||||
WhereConditionBuilder() {}
|
||||
|
||||
WhereConditionBuilder eq() {
|
||||
|
||||
|
||||
@@ -15,16 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -34,6 +24,17 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Generates SQL statements to be used by {@link SimpleJdbcRepository}
|
||||
*
|
||||
@@ -81,10 +82,21 @@ class SqlGenerator {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query for selecting all simple properties of an entitty, including those for one-to-one relationhships.
|
||||
* Results are filtered using an {@code IN}-clause on the id column.
|
||||
*
|
||||
* @return a SQL statement. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
String getFindAllInList() {
|
||||
return findAllInListSql.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a query for selecting all simple properties of an entitty, including those for one-to-one relationhships.
|
||||
*
|
||||
* @return a SQL statement. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
String getFindAll() {
|
||||
return findAllSql.get();
|
||||
}
|
||||
@@ -96,17 +108,19 @@ 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 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}.
|
||||
* @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 {@code true},
|
||||
* the keyColumn must not be {@code null}.
|
||||
* @return a SQL String.
|
||||
*/
|
||||
String getFindAllByProperty(String columnName, String keyColumn, boolean ordered) {
|
||||
String getFindAllByProperty(String columnName, @Nullable String keyColumn, boolean ordered) {
|
||||
|
||||
Assert.isTrue(keyColumn != null || !ordered, "If the SQL statement should be ordered a keyColumn to order by must be provided.");
|
||||
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();
|
||||
|
||||
String orderBy = ordered ? " ORDER BY " + keyColumn : "";
|
||||
@@ -170,7 +184,7 @@ class SqlGenerator {
|
||||
if (!property.isEntity() //
|
||||
|| Collection.class.isAssignableFrom(property.getType()) //
|
||||
|| Map.class.isAssignableFrom(property.getType()) //
|
||||
) {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -266,7 +280,7 @@ class SqlGenerator {
|
||||
return String.format("DELETE FROM %s WHERE %s = :id", entity.getTableName(), entity.getIdColumn());
|
||||
}
|
||||
|
||||
String createDeleteAllSql(PropertyPath path) {
|
||||
String createDeleteAllSql(@Nullable PropertyPath path) {
|
||||
|
||||
if (path == null) {
|
||||
return String.format("DELETE FROM %s", entity.getTableName());
|
||||
@@ -301,7 +315,7 @@ class SqlGenerator {
|
||||
return String.format("DELETE FROM %s WHERE %s", entityToDelete.getTableName(), condition);
|
||||
}
|
||||
|
||||
private String cascadeConditions(String innerCondition, PropertyPath path) {
|
||||
private String cascadeConditions(String innerCondition, @Nullable PropertyPath path) {
|
||||
|
||||
if (path == null) {
|
||||
return innerCondition;
|
||||
|
||||
@@ -49,7 +49,19 @@ public class AggregateChange<T> {
|
||||
actions.add(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* The kind of action to be performed on an aggregate.
|
||||
*/
|
||||
public enum Kind {
|
||||
SAVE, DELETE
|
||||
/**
|
||||
* A {@code SAVE} of an aggregate typically involves an {@code insert} or {@code update} on the aggregate root plus
|
||||
* {@code insert}s, {@code update}s, and {@code delete}s on the other elements of an aggregate.
|
||||
*/
|
||||
SAVE,
|
||||
|
||||
/**
|
||||
* A {@code DELETE} of an aggregate typically involves a {@code delete} on all contained entities.
|
||||
*/
|
||||
DELETE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import lombok.ToString;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -36,7 +37,7 @@ public abstract class DbAction<T> {
|
||||
/**
|
||||
* {@link Class} of the entity of which the database representation is affected by this action.
|
||||
*/
|
||||
private final Class<T> entityType;
|
||||
final Class<T> entityType;
|
||||
|
||||
/**
|
||||
* The entity of which the database representation is affected by this action. Might be {@literal null}.
|
||||
@@ -62,7 +63,10 @@ public abstract class DbAction<T> {
|
||||
*/
|
||||
private final DbAction dependingOn;
|
||||
|
||||
private DbAction(Class<T> entityType, T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
private DbAction(Class<T> entityType, @Nullable T entity, @Nullable JdbcPropertyPath propertyPath,
|
||||
@Nullable DbAction dependingOn) {
|
||||
|
||||
Assert.notNull(entityType, "entityType must not be null");
|
||||
|
||||
this.entityType = entityType;
|
||||
this.entity = entity;
|
||||
@@ -70,20 +74,63 @@ public abstract class DbAction<T> {
|
||||
this.dependingOn = dependingOn;
|
||||
}
|
||||
|
||||
public static <T> Insert<T> insert(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
/**
|
||||
* Creates an {@link Insert} action for the given parameters.
|
||||
*
|
||||
* @param entity the entity to insert into the database. Must not be {@code null}.
|
||||
* @param propertyPath property path from the aggregate root to the entity to be saved. Must not be {@code null}.
|
||||
* @param dependingOn a {@link DbAction} the to be created insert may depend on. Especially the insert of a parent
|
||||
* entity. May be {@code null}.
|
||||
* @param <T> the type of the entity to be inserted.
|
||||
* @return a {@link DbAction} representing the insert.
|
||||
*/
|
||||
public static <T> Insert<T> insert(T entity, JdbcPropertyPath propertyPath, @Nullable DbAction dependingOn) {
|
||||
return new Insert<>(entity, propertyPath, dependingOn);
|
||||
}
|
||||
|
||||
public static <T> Update<T> update(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
/**
|
||||
* Creates an {@link Update} action for the given parameters.
|
||||
*
|
||||
* @param entity the entity to update in the database. Must not be {@code null}.
|
||||
* @param propertyPath property path from the aggregate root to the entity to be saved. Must not be {@code null}.
|
||||
* @param dependingOn a {@link DbAction} the to be created update may depend on. Especially the insert of a parent
|
||||
* entity. May be {@code null}.
|
||||
* @param <T> the type of the entity to be updated.
|
||||
* @return a {@link DbAction} representing the update.
|
||||
*/
|
||||
public static <T> Update<T> update(T entity, JdbcPropertyPath propertyPath, @Nullable DbAction dependingOn) {
|
||||
return new Update<>(entity, propertyPath, dependingOn);
|
||||
}
|
||||
|
||||
public static <T> Delete<T> delete(Object id, Class<T> type, T entity, JdbcPropertyPath propertyPath,
|
||||
DbAction dependingOn) {
|
||||
/**
|
||||
* Creates a {@link Delete} action for the given parameters.
|
||||
*
|
||||
* @param id the id of the aggregate root for which referenced entities to be deleted. May not be {@code null}.
|
||||
* @param type the type of the entity to be deleted. Must not be {@code null}.
|
||||
* @param entity the aggregate roo to be deleted. May be {@code null}.
|
||||
* @param propertyPath the property path from the aggregate root to the entity to be deleted.
|
||||
* @param dependingOn an action this action might depend on.
|
||||
* @param <T> the type of the entity to be deleted.
|
||||
* @return a {@link DbAction} representing the deletion of the entity with given type and id.
|
||||
*/
|
||||
public static <T> Delete<T> delete(Object id, Class<T> type, @Nullable T entity,
|
||||
@Nullable JdbcPropertyPath propertyPath, @Nullable DbAction dependingOn) {
|
||||
return new Delete<>(id, type, entity, propertyPath, dependingOn);
|
||||
}
|
||||
|
||||
public static <T> DeleteAll<T> deleteAll(Class<T> type, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
/**
|
||||
* Creates a {@link DeleteAll} action for the given parameters.
|
||||
*
|
||||
* @param type the type of entities to be deleted. May be {@code null}.
|
||||
* @param propertyPath the property path describing the relation of the entity to be deleted to the aggregate it
|
||||
* belongs to. May be {@code null} for the aggregate root.
|
||||
* @param dependingOn an action this action might depend on. May be {@code null}.
|
||||
* @param <T> the type of the entity to be deleted.
|
||||
* @return a {@link DbAction} representing the deletion of all entities of a given type belonging to a specific type
|
||||
* of aggregate root.
|
||||
*/
|
||||
public static <T> DeleteAll<T> deleteAll(Class<T> type, @Nullable JdbcPropertyPath propertyPath,
|
||||
@Nullable DbAction dependingOn) {
|
||||
return new DeleteAll<>(type, propertyPath, dependingOn);
|
||||
}
|
||||
|
||||
@@ -116,7 +163,7 @@ public abstract class DbAction<T> {
|
||||
abstract static class InsertOrUpdate<T> extends DbAction<T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
InsertOrUpdate(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
InsertOrUpdate(T entity, JdbcPropertyPath propertyPath, @Nullable DbAction dependingOn) {
|
||||
super((Class<T>) entity.getClass(), entity, propertyPath, dependingOn);
|
||||
}
|
||||
}
|
||||
@@ -128,7 +175,7 @@ public abstract class DbAction<T> {
|
||||
*/
|
||||
public static class Insert<T> extends InsertOrUpdate<T> {
|
||||
|
||||
private Insert(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
private Insert(T entity, JdbcPropertyPath propertyPath, @Nullable DbAction dependingOn) {
|
||||
super(entity, propertyPath, dependingOn);
|
||||
}
|
||||
|
||||
@@ -145,7 +192,7 @@ public abstract class DbAction<T> {
|
||||
*/
|
||||
public static class Update<T> extends InsertOrUpdate<T> {
|
||||
|
||||
private Update(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
private Update(T entity, JdbcPropertyPath propertyPath, @Nullable DbAction dependingOn) {
|
||||
super(entity, propertyPath, dependingOn);
|
||||
}
|
||||
|
||||
@@ -169,7 +216,8 @@ public abstract class DbAction<T> {
|
||||
*/
|
||||
private final Object rootId;
|
||||
|
||||
private Delete(Object rootId, Class<T> type, T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
private Delete(@Nullable Object rootId, Class<T> type, @Nullable T entity, @Nullable JdbcPropertyPath propertyPath,
|
||||
@Nullable DbAction dependingOn) {
|
||||
|
||||
super(type, entity, propertyPath, dependingOn);
|
||||
|
||||
@@ -191,7 +239,7 @@ public abstract class DbAction<T> {
|
||||
*/
|
||||
public static class DeleteAll<T> extends DbAction<T> {
|
||||
|
||||
private DeleteAll(Class<T> entityType, JdbcPropertyPath propertyPath, DbAction dependingOn) {
|
||||
private DeleteAll(Class<T> entityType, @Nullable JdbcPropertyPath propertyPath, @Nullable DbAction dependingOn) {
|
||||
super(entityType, null, propertyPath, dependingOn);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ package org.springframework.data.jdbc.core.conversion;
|
||||
* @since 1.0
|
||||
*/
|
||||
public class DbActionExecutionException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* @param action the {@link DbAction} which triggered the exception. Must not be {@code null}.
|
||||
* @param cause the underlying exception. May not be {@code null}.
|
||||
*/
|
||||
public DbActionExecutionException(DbAction<?> action, Throwable cause) {
|
||||
super( //
|
||||
String.format("Failed to execute %s for instance %s of type %s with path %s", //
|
||||
|
||||
@@ -21,11 +21,22 @@ import org.springframework.data.jdbc.core.conversion.DbAction.Insert;
|
||||
import org.springframework.data.jdbc.core.conversion.DbAction.Update;
|
||||
|
||||
/**
|
||||
* An {@link Interpreter} gets called by a {@link AggregateChange} for each {@link DbAction} and is tasked with
|
||||
* executing that action against a database. While the {@link DbAction} is just an abstract representation of a database
|
||||
* action it's the task of an interpreter to actually execute it. This typically involves creating some SQL and running
|
||||
* it using JDBC, but it may also use some third party technology like MyBatis or jOOQ to do this.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface Interpreter {
|
||||
|
||||
/**
|
||||
* Interpret an {@link Update}. Interpreting normally means "executing".
|
||||
*
|
||||
* @param update the {@link Update} to be executed
|
||||
* @param <T> the type of entity to work on.
|
||||
*/
|
||||
<T> void interpret(Update<T> update);
|
||||
|
||||
<T> void interpret(Insert<T> insert);
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
package org.springframework.data.jdbc.core.conversion;
|
||||
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Converts an entity that is about to be deleted into {@link DbAction}s inside a {@link AggregateChange} that need to be
|
||||
* executed against the database to recreate the appropriate state in the database.
|
||||
* Converts an entity that is about to be deleted into {@link DbAction}s inside a {@link AggregateChange} that need to
|
||||
* be executed against the database to recreate the appropriate state in the database.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
@@ -30,8 +31,16 @@ public class JdbcEntityDeleteWriter extends JdbcEntityWriterSupport {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
@Override
|
||||
public void write(Object id, AggregateChange aggregateChange) {
|
||||
public void write(@Nullable Object id, AggregateChange<?> aggregateChange) {
|
||||
|
||||
if (id == null) {
|
||||
deleteAll(aggregateChange);
|
||||
@@ -40,7 +49,7 @@ public class JdbcEntityDeleteWriter extends JdbcEntityWriterSupport {
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteAll(AggregateChange aggregateChange) {
|
||||
private void deleteAll(AggregateChange<?> aggregateChange) {
|
||||
|
||||
context.referencedEntities(aggregateChange.getEntityType(), null)
|
||||
.forEach(p -> aggregateChange.addAction(DbAction.deleteAll(p.getLeafType(), new JdbcPropertyPath(p), null)));
|
||||
@@ -48,10 +57,16 @@ public class JdbcEntityDeleteWriter extends JdbcEntityWriterSupport {
|
||||
aggregateChange.addAction(DbAction.deleteAll(aggregateChange.getEntityType(), null, null));
|
||||
}
|
||||
|
||||
private void deleteById(Object id, AggregateChange aggregateChange) {
|
||||
private <T> void deleteById(Object id, AggregateChange<T> aggregateChange) {
|
||||
|
||||
deleteReferencedEntities(id, aggregateChange);
|
||||
|
||||
aggregateChange.addAction(DbAction.delete(id, aggregateChange.getEntityType(), aggregateChange.getEntity(), null, null));
|
||||
aggregateChange.addAction(DbAction.delete( //
|
||||
id, //
|
||||
aggregateChange.getEntityType(), //
|
||||
aggregateChange.getEntity(), //
|
||||
null, //
|
||||
null //
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,24 +52,27 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an aggregate represented by its aggregate root into a list of {@link DbAction}s and adds them to the
|
||||
* {@link AggregateChange} passed in as an argument.
|
||||
*
|
||||
* @param aggregateRoot the aggregate root to be written to the {@link AggregateChange} Must not be {@code null}.
|
||||
* @param aggregateChange the {@link AggregateChange} to which the {@link DbAction}s get written.
|
||||
*/
|
||||
@Override
|
||||
public void write(Object o, AggregateChange aggregateChange) {
|
||||
write(o, aggregateChange, null);
|
||||
}
|
||||
public void write(Object aggregateRoot, AggregateChange aggregateChange) {
|
||||
|
||||
private void write(Object o, AggregateChange aggregateChange, DbAction dependingOn) {
|
||||
|
||||
Class<?> type = o.getClass();
|
||||
Class<?> type = aggregateRoot.getClass();
|
||||
JdbcPropertyPath propertyPath = JdbcPropertyPath.from("", type);
|
||||
|
||||
PersistentEntity<?, JdbcPersistentProperty> persistentEntity = context.getRequiredPersistentEntity(type);
|
||||
|
||||
if (persistentEntity.isNew(o)) {
|
||||
if (persistentEntity.isNew(aggregateRoot)) {
|
||||
|
||||
Insert<Object> insert = DbAction.insert(o, propertyPath, dependingOn);
|
||||
Insert<Object> insert = DbAction.insert(aggregateRoot, propertyPath, null);
|
||||
aggregateChange.addAction(insert);
|
||||
|
||||
referencedEntities(o) //
|
||||
referencedEntities(aggregateRoot) //
|
||||
.forEach( //
|
||||
propertyAndValue -> //
|
||||
insertReferencedEntities( //
|
||||
@@ -77,19 +80,19 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
|
||||
aggregateChange, //
|
||||
propertyPath.nested(propertyAndValue.property.getName()), //
|
||||
insert) //
|
||||
);
|
||||
);
|
||||
} else {
|
||||
|
||||
JdbcPersistentEntity<?> entity = context.getPersistentEntity(type);
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(o);
|
||||
JdbcPersistentEntity<?> entity = context.getRequiredPersistentEntity(type);
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(aggregateRoot);
|
||||
|
||||
deleteReferencedEntities(identifierAccessor.getRequiredIdentifier(), aggregateChange);
|
||||
|
||||
Update<Object> update = DbAction.update(o, propertyPath, dependingOn);
|
||||
Update<Object> update = DbAction.update(aggregateRoot, propertyPath, null);
|
||||
aggregateChange.addAction(update);
|
||||
|
||||
referencedEntities(o).forEach(propertyAndValue -> insertReferencedEntities(propertyAndValue, aggregateChange,
|
||||
propertyPath.nested(propertyAndValue.property.getName()), update));
|
||||
referencedEntities(aggregateRoot).forEach(propertyAndValue -> insertReferencedEntities(propertyAndValue,
|
||||
aggregateChange, propertyPath.nested(propertyAndValue.property.getName()), update));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +116,7 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
|
||||
aggregateChange, //
|
||||
propertyPath.nested(pav.property.getName()), //
|
||||
dependingOn) //
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
private Stream<PropertyAndValue> referencedEntities(Object o) {
|
||||
@@ -125,7 +128,7 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
|
||||
.flatMap( //
|
||||
p -> referencedEntity(p, persistentEntity.getPropertyAccessor(o)) //
|
||||
.map(e -> new PropertyAndValue(p, e)) //
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
private Stream<Object> referencedEntity(JdbcPersistentProperty p, PersistentPropertyAccessor propertyAccessor) {
|
||||
@@ -155,6 +158,7 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
|
||||
return singlePropertyAsStream(p, propertyAccessor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Stream<Object> collectionPropertyAsStream(JdbcPersistentProperty p,
|
||||
PersistentPropertyAccessor propertyAccessor) {
|
||||
|
||||
@@ -165,6 +169,7 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
|
||||
: ((Collection<Object>) property).stream();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Stream<Object> listPropertyAsStream(JdbcPersistentProperty p, PersistentPropertyAccessor propertyAccessor) {
|
||||
|
||||
Object property = propertyAccessor.getProperty(p);
|
||||
@@ -180,6 +185,7 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
|
||||
return listProperty.stream().map(e -> new KeyValue(index.getAndIncrement(), e));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Stream<Object> mapPropertyAsStream(JdbcPersistentProperty p, PersistentPropertyAccessor propertyAccessor) {
|
||||
|
||||
Object property = propertyAccessor.getProperty(p);
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core.conversion;
|
||||
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Common infrastructure needed by different implementations of {@link EntityWriter}<Object, AggregateChange>.
|
||||
@@ -24,23 +25,26 @@ import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
*/
|
||||
abstract class JdbcEntityWriterSupport implements EntityWriter<Object, AggregateChange> {
|
||||
abstract class JdbcEntityWriterSupport implements EntityWriter<Object, AggregateChange<?>> {
|
||||
protected final JdbcMappingContext context;
|
||||
|
||||
JdbcEntityWriterSupport(JdbcMappingContext context) {
|
||||
|
||||
Assert.notNull(context, "Context must not be null");
|
||||
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* add {@link org.springframework.data.jdbc.core.conversion.DbAction.Delete} actions to the {@link AggregateChange} for
|
||||
* deleting all referenced entities.
|
||||
* add {@link org.springframework.data.jdbc.core.conversion.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 {@literal null}
|
||||
* @param aggregateChange the change object to which the actions should get added. Must not be {@code null}
|
||||
*/
|
||||
void deleteReferencedEntities(Object id, AggregateChange aggregateChange) {
|
||||
void deleteReferencedEntities(Object id, AggregateChange<?> aggregateChange) {
|
||||
|
||||
context.referencedEntities(aggregateChange.getEntityType(), null)
|
||||
.forEach(p -> aggregateChange.addAction(DbAction.delete(id, p.getLeafType(), null, new JdbcPropertyPath(p), null)));
|
||||
context.referencedEntities(aggregateChange.getEntityType(), null).forEach(
|
||||
p -> aggregateChange.addAction(DbAction.delete(id, p.getLeafType(), null, new JdbcPropertyPath(p), null)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
package org.springframework.data.jdbc.core.conversion;
|
||||
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A replacement for {@link org.springframework.data.mapping.PropertyPath} as long as it doesn't support objects with
|
||||
* empty path.
|
||||
*
|
||||
* See https://jira.spring.io/browse/DATACMNS-1204.
|
||||
* empty path. See https://jira.spring.io/browse/DATACMNS-1204.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
@@ -34,12 +33,16 @@ public class JdbcPropertyPath {
|
||||
|
||||
JdbcPropertyPath(PropertyPath path) {
|
||||
|
||||
Assert.notNull(path, "path must not be null if rootType is not set");
|
||||
|
||||
this.path = path;
|
||||
this.rootType = null;
|
||||
}
|
||||
|
||||
private JdbcPropertyPath(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "type must not be null if path is not set");
|
||||
|
||||
this.path = null;
|
||||
this.rootType = type;
|
||||
}
|
||||
@@ -54,7 +57,10 @@ public class JdbcPropertyPath {
|
||||
}
|
||||
|
||||
public JdbcPropertyPath nested(String name) {
|
||||
return path == null ? new JdbcPropertyPath(PropertyPath.from(name, rootType)) : new JdbcPropertyPath(path.nested(name));
|
||||
|
||||
return path == null ? //
|
||||
new JdbcPropertyPath(PropertyPath.from(name, rootType)) //
|
||||
: new JdbcPropertyPath(path.nested(name));
|
||||
}
|
||||
|
||||
public PropertyPath getPath() {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.core.conversion;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -29,6 +29,7 @@ import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -43,9 +44,6 @@ class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProperty<Jdbc
|
||||
implements JdbcPersistentProperty {
|
||||
|
||||
private static final Map<Class<?>, Class<?>> javaToDbType = new LinkedHashMap<>();
|
||||
private final JdbcMappingContext context;
|
||||
|
||||
private final Lazy<Optional<String>> columnName;
|
||||
|
||||
static {
|
||||
javaToDbType.put(Enum.class, String.class);
|
||||
@@ -53,6 +51,9 @@ class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProperty<Jdbc
|
||||
javaToDbType.put(Temporal.class, Date.class);
|
||||
}
|
||||
|
||||
private final JdbcMappingContext context;
|
||||
private final Lazy<Optional<String>> columnName;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnnotationBasedPersistentProperty}.
|
||||
*
|
||||
@@ -85,6 +86,7 @@ class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProperty<Jdbc
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentProperty#getColumnName()
|
||||
*/
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return columnName.get().orElseGet(() -> context.getNamingStrategy().getColumnName(this));
|
||||
}
|
||||
@@ -123,15 +125,16 @@ class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProperty<Jdbc
|
||||
return isMap() || isListLike();
|
||||
}
|
||||
|
||||
private boolean isListLike() {
|
||||
return isCollectionLike() && !Set.class.isAssignableFrom(this.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOrdered() {
|
||||
return isListLike();
|
||||
}
|
||||
|
||||
private boolean isListLike() {
|
||||
return isCollectionLike() && !Set.class.isAssignableFrom(this.getType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Class columnTypeIfEntity(Class type) {
|
||||
|
||||
JdbcPersistentEntity<?> persistentEntity = context.getPersistentEntity(type);
|
||||
|
||||
@@ -18,12 +18,23 @@ package org.springframework.data.jdbc.core.mapping;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
|
||||
/**
|
||||
* Interface to register custom conversions.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
*/
|
||||
public interface ConversionCustomizer {
|
||||
|
||||
public static ConversionCustomizer NONE = __ -> {};
|
||||
/**
|
||||
* Noop instance to be used as a default.
|
||||
*/
|
||||
ConversionCustomizer NONE = __ -> {};
|
||||
|
||||
/**
|
||||
* Gets called in order to allow the customization of the {@link org.springframework.core.convert.ConversionService}.
|
||||
* Typically used by registering additional conversions.
|
||||
*
|
||||
* @param conversions the conversions that get customized.
|
||||
*/
|
||||
void customize(GenericConversionService conversions);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -57,8 +58,8 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
|
||||
));
|
||||
|
||||
@Getter private final NamingStrategy namingStrategy;
|
||||
private final GenericConversionService conversions = getDefaultConversionService();
|
||||
@Getter private SimpleTypeHolder simpleTypeHolder;
|
||||
private GenericConversionService conversions = getDefaultConversionService();
|
||||
|
||||
/**
|
||||
* Creates a new {@link JdbcMappingContext}.
|
||||
@@ -88,6 +89,14 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
|
||||
setSimpleTypeHolder(new SimpleTypeHolder(CUSTOM_SIMPLE_TYPES, true));
|
||||
}
|
||||
|
||||
private static GenericConversionService getDefaultConversionService() {
|
||||
|
||||
DefaultConversionService conversionService = new DefaultConversionService();
|
||||
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);
|
||||
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSimpleTypeHolder(SimpleTypeHolder simpleTypes) {
|
||||
super.setSimpleTypeHolder(simpleTypes);
|
||||
@@ -98,7 +107,7 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
|
||||
* returns all {@link PropertyPath}s reachable from the root type in the order needed for deleting, i.e. the deepest
|
||||
* reference first.
|
||||
*/
|
||||
public List<PropertyPath> referencedEntities(Class<?> rootType, PropertyPath path) {
|
||||
public List<PropertyPath> referencedEntities(Class<?> rootType, @Nullable PropertyPath path) {
|
||||
|
||||
List<PropertyPath> paths = new ArrayList<>();
|
||||
|
||||
@@ -142,12 +151,4 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
|
||||
public ConversionService getConversions() {
|
||||
return conversions;
|
||||
}
|
||||
|
||||
private static GenericConversionService getDefaultConversionService() {
|
||||
|
||||
DefaultConversionService conversionService = new DefaultConversionService();
|
||||
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);
|
||||
|
||||
return conversionService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package org.springframework.data.jdbc.core.mapping;
|
||||
import org.springframework.data.mapping.model.MutablePersistentEntity;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.data.mapping.PersistentEntity} interface with additional methods for JDBC/RDBMS related
|
||||
* metadata.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @since 1.0
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
package org.springframework.data.jdbc.core.mapping;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link PersistentProperty} for JDBC.
|
||||
* A {@link PersistentProperty} with methods for additional JDBC/RDBMS related meta data.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
@@ -36,7 +37,7 @@ public interface JdbcPersistentProperty extends PersistentProperty<JdbcPersisten
|
||||
/**
|
||||
* The type to be used to store this property in the database.
|
||||
*
|
||||
* @return a {@link Class} that is suitable for usage with JDBC drivers
|
||||
* @return a {@link Class} that is suitable for usage with JDBC drivers.
|
||||
*/
|
||||
Class<?> getColumnType();
|
||||
|
||||
@@ -45,10 +46,12 @@ public interface JdbcPersistentProperty extends PersistentProperty<JdbcPersisten
|
||||
|
||||
String getReverseColumnName();
|
||||
|
||||
@Nullable
|
||||
String getKeyColumn();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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();
|
||||
|
||||
|
||||
@@ -62,7 +62,8 @@ public interface NamingStrategy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults to return the given {@link JdbcPersistentProperty}'s name with the parts of a camel case name separated by '_';
|
||||
* Defaults to return the given {@link JdbcPersistentProperty}'s name with the parts of a camel case name separated by
|
||||
* '_';
|
||||
*/
|
||||
default String getColumnName(JdbcPersistentProperty property) {
|
||||
|
||||
@@ -83,7 +84,7 @@ public interface NamingStrategy {
|
||||
*/
|
||||
default String getReverseColumnName(JdbcPersistentProperty property) {
|
||||
|
||||
Assert.notNull(property,"Property must not be null.");
|
||||
Assert.notNull(property, "Property must not be null.");
|
||||
|
||||
return property.getOwner().getTableName();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public class AfterDeleteEvent extends JdbcEventWithId {
|
||||
/**
|
||||
* @param id of the entity.
|
||||
* @param instance the deleted entity if it is available.
|
||||
* @param change the {@link AggregateChange} encoding the planned actions to be performed on the database.
|
||||
* @param change the {@link AggregateChange} encoding the actions that were performed on the database as part of the
|
||||
* delete operation.
|
||||
*/
|
||||
public AfterDeleteEvent(Specified id, Optional<Object> instance, AggregateChange change) {
|
||||
super(id, instance, change);
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.springframework.data.jdbc.core.conversion.AggregateChange;
|
||||
import org.springframework.data.jdbc.core.mapping.event.Identifier.Specified;
|
||||
|
||||
/**
|
||||
* Subclasses of this get published after a new instance or a changed instance was saved in the database.
|
||||
* Gets published after a new instance or a changed instance was saved in the database.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
@@ -29,9 +29,9 @@ public class AfterSaveEvent extends JdbcEventWithIdAndEntity {
|
||||
private static final long serialVersionUID = 8982085767296982848L;
|
||||
|
||||
/**
|
||||
* @param id identifier of
|
||||
* @param instance the newly saved entity.
|
||||
* @param change the {@link AggregateChange} encoding the planned actions to be performed on the database.
|
||||
* @param id identifier of the saved entity.
|
||||
* @param instance the saved entity.
|
||||
* @param change the {@link AggregateChange} encoding the actions performed on the database as part of the delete.
|
||||
*/
|
||||
public AfterSaveEvent(Specified id, Object instance, AggregateChange change) {
|
||||
super(id, instance, change);
|
||||
|
||||
@@ -21,7 +21,8 @@ import org.springframework.data.jdbc.core.conversion.AggregateChange;
|
||||
import org.springframework.data.jdbc.core.mapping.event.Identifier.Specified;
|
||||
|
||||
/**
|
||||
* Gets published when an entity is about to get deleted.
|
||||
* Gets published when an entity is about to get deleted. The contained {@link AggregateChange} is mutable and may be
|
||||
* changed in order to change the actions that get performed on the database as part of the delete operation.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
|
||||
@@ -18,7 +18,8 @@ package org.springframework.data.jdbc.core.mapping.event;
|
||||
import org.springframework.data.jdbc.core.conversion.AggregateChange;
|
||||
|
||||
/**
|
||||
* Subclasses of this get published before an entity gets saved to the database.
|
||||
* Gets published before an entity gets saved to the database. The contained {@link AggregateChange} is mutable and may
|
||||
* be changed in order to change the actions that get performed on the database as part of the save operation.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
@@ -30,7 +31,7 @@ public class BeforeSaveEvent extends JdbcEventWithEntity {
|
||||
/**
|
||||
* @param id of the entity to be saved.
|
||||
* @param instance the entity about to get saved.
|
||||
* @param change
|
||||
* @param change the {@link AggregateChange} that is going to get applied to the database.
|
||||
*/
|
||||
public BeforeSaveEvent(Identifier id, Object instance, AggregateChange change) {
|
||||
super(id, instance, change);
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core.mapping.event;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -40,29 +41,22 @@ public interface Identifier {
|
||||
return SpecifiedIdentifier.of(identifier);
|
||||
}
|
||||
|
||||
static Identifier ofNullable(Object identifier) {
|
||||
return identifier == null ? Unset.UNSET : of(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Identifier} for the given optional source value.
|
||||
* Produces an {@link Identifier} of appropriate type depending the argument being {@code null} or not.
|
||||
*
|
||||
* @param identifier must not be {@literal null}.
|
||||
* @return
|
||||
* @param identifier May be {@code null}.
|
||||
* @return an {@link Identifier}.
|
||||
*/
|
||||
static Identifier of(Optional<? extends Object> identifier) {
|
||||
|
||||
Assert.notNull(identifier, "Identifier must not be null!");
|
||||
|
||||
return identifier.map(it -> (Identifier) Identifier.of(it)).orElse(Unset.UNSET);
|
||||
static Identifier ofNullable(@Nullable Object identifier) {
|
||||
return identifier == null ? Unset.UNSET : of(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier value.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
* @return will never be {@code null}.
|
||||
*/
|
||||
Optional<? extends Object> getOptionalValue();
|
||||
Optional<?> getOptionalValue();
|
||||
|
||||
/**
|
||||
* A specified identifier that exposes a definitely present identifier value.
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.jdbc.core.mapping.event;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* an event signalling JDBC processing. It offers access to an {@link Identifier} of the aggregate root affected by the
|
||||
* event.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 1.0
|
||||
@@ -25,16 +27,16 @@ import java.util.Optional;
|
||||
public interface JdbcEvent {
|
||||
|
||||
/**
|
||||
* The identifier of the entity, triggering this event. Also available via {@link #getSource()}.
|
||||
* The identifier of the aggregate root, triggering this event.
|
||||
*
|
||||
* @return the source of the event as an {@link Identifier}.
|
||||
* @return the source of the event as an {@link Identifier}. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
Identifier getId();
|
||||
|
||||
/**
|
||||
* Returns the entity the event was triggered for.
|
||||
* Returns the aggregate root the event was triggered for.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
* @return will never be {@code null}.
|
||||
*/
|
||||
Optional<Object> getOptionalEntity();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public class JdbcEventWithEntity extends SimpleJdbcEvent implements WithEntity {
|
||||
|
||||
private static final long serialVersionUID = 4891455396602090638L;
|
||||
|
||||
public JdbcEventWithEntity(Identifier id, Object entity, AggregateChange change) {
|
||||
JdbcEventWithEntity(Identifier id, Object entity, AggregateChange change) {
|
||||
super(id, Optional.of(entity), change);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jdbc.core.conversion.AggregateChange;
|
||||
import org.springframework.data.jdbc.core.mapping.event.Identifier.Specified;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link SimpleJdbcEvent} guaranteed to have an identifier.
|
||||
@@ -32,7 +33,7 @@ public class JdbcEventWithId extends SimpleJdbcEvent implements WithId {
|
||||
|
||||
private final Specified id;
|
||||
|
||||
public JdbcEventWithId(Specified id, Optional<Object> entity, AggregateChange change) {
|
||||
public JdbcEventWithId(Specified id, Optional<Object> entity, @Nullable AggregateChange change) {
|
||||
|
||||
super(id, entity, change);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jdbc.core.conversion.AggregateChange;
|
||||
import org.springframework.data.jdbc.core.mapping.event.Identifier.Specified;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link SimpleJdbcEvent} which is guaranteed to have an identifier and an entity.
|
||||
@@ -33,7 +34,7 @@ public class JdbcEventWithIdAndEntity extends JdbcEventWithId implements WithEnt
|
||||
|
||||
private static final long serialVersionUID = -3194462549552515519L;
|
||||
|
||||
public JdbcEventWithIdAndEntity(Specified id, Object entity, AggregateChange change) {
|
||||
public JdbcEventWithIdAndEntity(Specified id, Object entity, @Nullable AggregateChange change) {
|
||||
super(id, Optional.of(entity), change);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.data.jdbc.core.conversion.AggregateChange;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* The common superclass for all events published by JDBC repositories. {@link #getSource} contains the
|
||||
@@ -35,7 +36,7 @@ class SimpleJdbcEvent extends ApplicationEvent implements JdbcEvent {
|
||||
private final Object entity;
|
||||
private final AggregateChange change;
|
||||
|
||||
SimpleJdbcEvent(Identifier id, Optional<Object> entity, AggregateChange change) {
|
||||
SimpleJdbcEvent(Identifier id, Optional<Object> entity, @Nullable AggregateChange change) {
|
||||
|
||||
super(id);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.core.mapping.event;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -31,14 +32,14 @@ import org.springframework.data.jdbc.core.mapping.event.Identifier.Specified;
|
||||
@Value(staticConstructor = "of")
|
||||
class SpecifiedIdentifier implements Specified {
|
||||
|
||||
Object value;
|
||||
@NonNull Object value;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.mapping.event.Identifier#getOptionalValue()
|
||||
*/
|
||||
@Override
|
||||
public Optional<? extends Object> getOptionalValue() {
|
||||
public Optional<?> getOptionalValue() {
|
||||
return Optional.of(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.core.mapping.event;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.core.mapping;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.domain.support;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -17,10 +17,12 @@ package org.springframework.data.jdbc.mybatis;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link MyBatisContext} instances get passed to MyBatis mapped statements as arguments, making Ids, instances, domainType and other attributes available to the statements.
|
||||
*
|
||||
* All methods might return {@literal null} depending on the kind of values available on invocation.
|
||||
* {@link MyBatisContext} instances get passed to MyBatis mapped statements as arguments, making Ids, instances,
|
||||
* domainType and other attributes available to the statements. All methods might return {@literal null} depending on
|
||||
* the kind of values available on invocation.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 1.0
|
||||
@@ -32,7 +34,7 @@ public class MyBatisContext {
|
||||
private final Class domainType;
|
||||
private final Map<String, Object> additonalValues;
|
||||
|
||||
public MyBatisContext(Object id, Object instance, Class domainType, Map<String, Object> additonalValues) {
|
||||
public MyBatisContext(@Nullable Object id, @Nullable Object instance, Class domainType, Map<String, Object> additonalValues) {
|
||||
|
||||
this.id = id;
|
||||
this.instance = instance;
|
||||
@@ -40,18 +42,43 @@ public class MyBatisContext {
|
||||
this.additonalValues = additonalValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* The ID of the entity to query/act upon.
|
||||
*
|
||||
* @return Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The entity to act upon. This is {@code null} for queries, since the object doesn't exist before the query.
|
||||
*
|
||||
* @return Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
public Object getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* The domain type of the entity to query or act upon.
|
||||
*
|
||||
* @return Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
public Class getDomainType() {
|
||||
return domainType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value for the given key. Used to communicate ids of parent entities.
|
||||
*
|
||||
* @param key Must not be {@code null}.
|
||||
* @return Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
public Object get(String key) {
|
||||
return additonalValues.get(key);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
public void deleteAll(PropertyPath propertyPath) {
|
||||
|
||||
Class<?> baseType = propertyPath.getOwningType().getType();
|
||||
Class<?> leafType = propertyPath.getLeafProperty().getTypeInformation().getType();
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.mybatis;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* A map from a type to a {@link RowMapper} to be used for extracting that type from {@link java.sql.ResultSet}s.
|
||||
@@ -39,5 +40,6 @@ public interface RowMapperMap {
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
<T> RowMapper<? extends T> rowMapperFor(Class<T> type);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.util.Map;
|
||||
|
||||
import org.springframework.data.jdbc.repository.RowMapperMap;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link RowMapperMap} that allows for registration of {@link RowMapper}s via a fluent Api.
|
||||
@@ -42,9 +44,21 @@ public class ConfigurableRowMapperMap implements RowMapperMap {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returs a {@link RowMapper} for the given type if such a {@link RowMapper} is present. If an exact match is found
|
||||
* that is returned. If not a {@link RowMapper} is returned that produces subtypes of the requested type. If no such
|
||||
* {@link RowMapper} is found the method returns {@code null}.
|
||||
*
|
||||
* @param type the type to be produced by the returned {@link RowMapper}. Must not be {@code null}.
|
||||
* @param <T> the type to be produced by the returned {@link RowMapper}.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public <T> RowMapper<? extends T> rowMapperFor(Class<T> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null");
|
||||
|
||||
RowMapper<? extends T> candidate = (RowMapper<? extends T>) rowMappers.get(type);
|
||||
|
||||
if (candidate == null) {
|
||||
|
||||
@@ -43,22 +43,17 @@ public @interface EnableJdbcAuditing {
|
||||
/**
|
||||
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
|
||||
*
|
||||
* @return
|
||||
* @see AuditorAware
|
||||
*/
|
||||
String auditorAwareRef() default "";
|
||||
|
||||
/**
|
||||
* Configures whether the creation and modification dates are set.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean setDates() default true;
|
||||
|
||||
/**
|
||||
* Configures whether the entity shall be marked as modified on creation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean modifyOnCreate() default true;
|
||||
|
||||
@@ -66,7 +61,6 @@ public @interface EnableJdbcAuditing {
|
||||
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link java.time.LocalDateTime} to be
|
||||
* used for setting creation and modification dates.
|
||||
*
|
||||
* @return
|
||||
* @see DateTimeProvider
|
||||
*/
|
||||
String dateTimeProviderRef() default "";
|
||||
|
||||
@@ -22,8 +22,6 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jdbc.core.mapping.ConversionCustomizer;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.core.mapping.NamingStrategy;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
|
||||
/**
|
||||
* Beans that must be registered for Spring Data JDBC to work.
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.repository.config;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.repository.query;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -60,6 +60,7 @@ public class JdbcQueryMethod extends QueryMethod {
|
||||
*
|
||||
* @return May be {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
public Class<?> getRowMapperClass() {
|
||||
return getMergedAnnotationAttribute("rowMapperClass");
|
||||
}
|
||||
@@ -75,6 +76,7 @@ public class JdbcQueryMethod extends QueryMethod {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private <T> T getMergedAnnotationAttribute(String attribute) {
|
||||
|
||||
Query queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -102,6 +103,10 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
JdbcPersistentEntity<?> entity = context.getPersistentEntity(aClass);
|
||||
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (EntityInformation<T, ID>) new PersistentEntityInformation<>(entity);
|
||||
}
|
||||
|
||||
@@ -131,7 +136,7 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider)
|
||||
*/
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable QueryLookupStrategy.Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
if (key != null //
|
||||
|
||||
@@ -75,8 +75,6 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
|
||||
|
||||
/**
|
||||
* Creates the actual {@link RepositoryFactorySupport} instance.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport doCreateRepositoryFactory() {
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -51,7 +52,7 @@ class JdbcRepositoryQuery implements RepositoryQuery {
|
||||
* @param defaultRowMapper can be {@literal null} (only in case of a modifying query).
|
||||
*/
|
||||
JdbcRepositoryQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
|
||||
RowMapper<?> defaultRowMapper) {
|
||||
@Nullable RowMapper<?> defaultRowMapper) {
|
||||
|
||||
Assert.notNull(queryMethod, "Query method must not be null!");
|
||||
Assert.notNull(operations, "NamedParameterJdbcOperations must not be null!");
|
||||
@@ -127,7 +128,8 @@ class JdbcRepositoryQuery implements RepositoryQuery {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static RowMapper<?> createRowMapper(JdbcQueryMethod queryMethod, RowMapper<?> defaultRowMapper) {
|
||||
@Nullable
|
||||
private static RowMapper<?> createRowMapper(JdbcQueryMethod queryMethod, @Nullable RowMapper<?> defaultRowMapper) {
|
||||
|
||||
Class<?> rowMapperClass = queryMethod.getRowMapperClass();
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
Reference in New Issue
Block a user