DATAJDBC-123 - MyBatis integration on DbAction level.
If MyBatis-spring is available and a SqlSessionFactory in the Application Context we look for matching mapped sql statements instead of using the default generated SQL. Introduced DataAccessStrategy as a new abstraction level, operating on a single entity. JdbcEntityOperations only contain operations related to complete Aggregates. Thereby also solving DATAJDBC-132. Integration tests ending in HsqlIntegrationTest will only get executed using HsqlDb. Related issue: DATAJDBC-132.
This commit is contained in:
committed by
Greg Turnquist
parent
fd2ae05795
commit
806bb24ff5
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
|
||||
/**
|
||||
* Delegates each methods to the {@link DataAccessStrategy}s passed to the constructor in turn until the first that does
|
||||
* not throw an exception.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class CascadingDataAccessStrategy implements DataAccessStrategy {
|
||||
|
||||
private final List<DataAccessStrategy> strategies;
|
||||
|
||||
public CascadingDataAccessStrategy(List<DataAccessStrategy> strategies) {
|
||||
this.strategies = new ArrayList<>(strategies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
|
||||
collectVoid(das -> das.insert(instance, domainType, additionalParameters));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void update(S instance, Class<S> domainType) {
|
||||
collectVoid(das -> das.update(instance, domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object id, Class<?> domainType) {
|
||||
collectVoid(das -> das.delete(id, domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object rootId, PropertyPath propertyPath) {
|
||||
collectVoid(das -> das.delete(rootId, propertyPath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(Class<T> domainType) {
|
||||
collectVoid(das -> das.deleteAll(domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
collectVoid(das -> das.deleteAll(propertyPath));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Class<?> domainType) {
|
||||
return collect(das -> das.count(domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
return collect(das -> das.findById(id, domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
return collect(das -> das.findAll(domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
return collect(das -> das.findAllById(ids, domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllByProperty(Object rootId, JdbcPersistentProperty property) {
|
||||
return collect(das -> das.findAllByProperty(rootId, property));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
return collect(das -> das.existsById(id, domainType));
|
||||
}
|
||||
|
||||
private <T> T collect(Function<DataAccessStrategy, T> function) {
|
||||
return strategies.stream().collect(new FunctionCollector<>(function));
|
||||
}
|
||||
|
||||
private void collectVoid(Consumer<DataAccessStrategy> consumer) {
|
||||
|
||||
collect(das -> {
|
||||
consumer.accept(das);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public interface DataAccessStrategy {
|
||||
|
||||
<T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters);
|
||||
|
||||
<S> void update(S instance, Class<S> domainType);
|
||||
|
||||
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.
|
||||
*/
|
||||
void delete(Object rootId, PropertyPath propertyPath);
|
||||
|
||||
<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.
|
||||
*/
|
||||
<T> void deleteAll(PropertyPath propertyPath);
|
||||
|
||||
long count(Class<?> domainType);
|
||||
|
||||
<T> T findById(Object id, Class<T> domainType);
|
||||
|
||||
<T> Iterable<T> findAll(Class<T> domainType);
|
||||
|
||||
<T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Finds all entities reachable via {@literal property} from the instance identified by {@literal rootId}.
|
||||
*
|
||||
* @param rootId Id of the root object on which the {@literal propertyPath} is based.
|
||||
* @param property Leading from the root object to the entities to be found.
|
||||
*/
|
||||
<T> Iterable<T> findAllByProperty(Object rootId, JdbcPersistentProperty property);
|
||||
|
||||
<T> boolean existsById(Object id, Class<T> domainType);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.data.convert.Jsr310Converters;
|
||||
import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.support.JdbcUtil;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
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.util.Assert;
|
||||
|
||||
/**
|
||||
* Generates and executes actual SQL statements.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class DefaultDataAccessStrategy implements DataAccessStrategy {
|
||||
|
||||
private static final String ENTITY_NEW_AFTER_INSERT = "Entity [%s] still 'new' after insert. Please set either"
|
||||
+ " the id property in a BeforeInsert event handler, or ensure the database creates a value and your "
|
||||
+ "JDBC driver returns it.";
|
||||
|
||||
private final SqlGeneratorSource sqlGeneratorSource;
|
||||
private final NamedParameterJdbcOperations operations;
|
||||
private final JdbcMappingContext context;
|
||||
private final ConversionService conversions = getDefaultConversionService();
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
|
||||
public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, NamedParameterJdbcOperations operations,
|
||||
JdbcMappingContext context, DataAccessStrategy accessStrategy) {
|
||||
|
||||
this.sqlGeneratorSource = sqlGeneratorSource;
|
||||
this.operations = operations;
|
||||
this.context = context;
|
||||
this.accessStrategy = accessStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a {@link DefaultDataAccessStrategy} which references it self for resolution of recursive data accesses.
|
||||
*
|
||||
* Only suitable if this is the only access strategy in use.
|
||||
*/
|
||||
public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, NamedParameterJdbcOperations operations,
|
||||
JdbcMappingContext context) {
|
||||
|
||||
this.sqlGeneratorSource = sqlGeneratorSource;
|
||||
this.operations = operations;
|
||||
this.context = context;
|
||||
this.accessStrategy = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
|
||||
|
||||
KeyHolder holder = new GeneratedKeyHolder();
|
||||
JdbcPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
|
||||
JdbcPersistentEntityInformation<T, ?> entityInformation = context
|
||||
.getRequiredPersistentEntityInformation(domainType);
|
||||
|
||||
MapSqlParameterSource parameterSource = getPropertyMap(instance, persistentEntity);
|
||||
|
||||
Object idValue = getIdValueOrNull(instance, persistentEntity);
|
||||
JdbcPersistentProperty idProperty = persistentEntity.getRequiredIdProperty();
|
||||
parameterSource.addValue(idProperty.getColumnName(), convert(idValue, idProperty.getColumnType()),
|
||||
JdbcUtil.sqlTypeFor(idProperty.getColumnType()));
|
||||
|
||||
additionalParameters.forEach(parameterSource::addValue);
|
||||
|
||||
operations.update(sql(domainType).getInsert(idValue == null, additionalParameters.keySet()), parameterSource,
|
||||
holder);
|
||||
|
||||
setIdFromJdbc(instance, holder, persistentEntity);
|
||||
|
||||
if (entityInformation.isNew(instance)) {
|
||||
throw new IllegalStateException(String.format(ENTITY_NEW_AFTER_INSERT, persistentEntity));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void update(S instance, Class<S> domainType) {
|
||||
|
||||
JdbcPersistentEntity<S> persistentEntity = getRequiredPersistentEntity(domainType);
|
||||
|
||||
operations.update(sql(domainType).getUpdate(), getPropertyMap(instance, persistentEntity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object id, Class<?> domainType) {
|
||||
|
||||
String deleteByIdSql = sql(domainType).getDeleteById();
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
|
||||
operations.update(deleteByIdSql, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object rootId, PropertyPath propertyPath) {
|
||||
|
||||
JdbcPersistentEntity<?> rootEntity = context.getRequiredPersistentEntity(propertyPath.getOwningType());
|
||||
|
||||
JdbcPersistentProperty referencingProperty = rootEntity.getRequiredPersistentProperty(propertyPath.getSegment());
|
||||
Assert.notNull(referencingProperty, "No property found matching the PropertyPath " + propertyPath);
|
||||
|
||||
String format = sql(rootEntity.getType()).createDeleteByPath(propertyPath);
|
||||
|
||||
HashMap<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("rootId", rootId);
|
||||
operations.update(format, parameters);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(Class<T> domainType) {
|
||||
|
||||
operations.getJdbcOperations().update(sql(domainType).createDeleteAllSql(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
|
||||
operations.getJdbcOperations().update(sql(propertyPath.getOwningType().getType()).createDeleteAllSql(propertyPath));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public long count(Class<?> domainType) {
|
||||
return operations.getJdbcOperations().queryForObject(sql(domainType).getCount(), Long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
|
||||
String findOneSql = sql(domainType).getFindOne();
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
try {
|
||||
return operations.queryForObject(findOneSql, parameter, getEntityRowMapper(domainType));
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
return operations.query(sql(domainType).getFindAll(), getEntityRowMapper(domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
|
||||
String findAllInListSql = sql(domainType).getFindAllInList();
|
||||
Class<?> targetType = getRequiredPersistentEntity(domainType).getRequiredIdProperty().getColumnType();
|
||||
|
||||
MapSqlParameterSource parameter = new MapSqlParameterSource( //
|
||||
"ids", //
|
||||
StreamSupport.stream(ids.spliterator(), false) //
|
||||
.map(id -> convert(id, targetType)) //
|
||||
.collect(Collectors.toList()) //
|
||||
);
|
||||
|
||||
return operations.query(findAllInListSql, parameter, getEntityRowMapper(domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllByProperty(Object rootId, JdbcPersistentProperty property) {
|
||||
|
||||
Class<?> actualType = property.getActualType();
|
||||
String findAllByProperty = sql(actualType).getFindAllByProperty(property.getReverseColumnName());
|
||||
|
||||
MapSqlParameterSource parameter = new MapSqlParameterSource(property.getReverseColumnName(), rootId);
|
||||
|
||||
return (Iterable<T>) operations.query(findAllByProperty, parameter, getEntityRowMapper(actualType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
|
||||
String existsSql = sql(domainType).getExists();
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
return operations.queryForObject(existsSql, parameter, Boolean.class);
|
||||
}
|
||||
|
||||
private static GenericConversionService getDefaultConversionService() {
|
||||
|
||||
DefaultConversionService conversionService = new DefaultConversionService();
|
||||
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);
|
||||
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
private <S> MapSqlParameterSource getPropertyMap(final S instance, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
MapSqlParameterSource parameters = new MapSqlParameterSource();
|
||||
|
||||
persistentEntity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) property -> {
|
||||
if (!property.isEntity()) {
|
||||
Object value = persistentEntity.getPropertyAccessor(instance).getProperty(property);
|
||||
|
||||
Object convertedValue = convert(value, property.getColumnType());
|
||||
parameters.addValue(property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
|
||||
}
|
||||
});
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private <S, ID> ID getIdValueOrNull(S instance, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
EntityInformation<S, ID> entityInformation = new BasicJdbcPersistentEntityInformation<>(persistentEntity);
|
||||
|
||||
ID idValue = entityInformation.getId(instance);
|
||||
|
||||
return isIdPropertySimpleTypeAndValueZero(idValue, persistentEntity) ? null : idValue;
|
||||
}
|
||||
|
||||
private <S, ID> boolean isIdPropertySimpleTypeAndValueZero(ID idValue, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
JdbcPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
return idValue == null //
|
||||
|| idProperty == null //
|
||||
|| (idProperty.getType() == int.class && idValue.equals(0)) //
|
||||
|| (idProperty.getType() == long.class && idValue.equals(0L));
|
||||
}
|
||||
|
||||
private <S> void setIdFromJdbc(S instance, KeyHolder holder, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
JdbcPersistentEntityInformation<S, ?> entityInformation = new BasicJdbcPersistentEntityInformation<>(
|
||||
persistentEntity);
|
||||
|
||||
try {
|
||||
|
||||
getIdFromHolder(holder, persistentEntity).ifPresent(it -> {
|
||||
|
||||
Class<?> targetType = persistentEntity.getRequiredIdProperty().getType();
|
||||
Object converted = convert(it, targetType);
|
||||
entityInformation.setId(instance, converted);
|
||||
});
|
||||
|
||||
} catch (NonTransientDataAccessException e) {
|
||||
throw new UnableToSetId("Unable to set id of " + instance, e);
|
||||
}
|
||||
}
|
||||
|
||||
private <S> Optional<Object> getIdFromHolder(KeyHolder holder, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
try {
|
||||
// MySQL just returns one value with a special name
|
||||
return Optional.ofNullable(holder.getKey());
|
||||
} catch (InvalidDataAccessApiUsageException e) {
|
||||
// Postgres returns a value for each column
|
||||
return Optional.ofNullable(holder.getKeys().get(persistentEntity.getIdColumn()));
|
||||
}
|
||||
}
|
||||
|
||||
private <T> EntityRowMapper<T> getEntityRowMapper(Class<T> domainType) {
|
||||
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), conversions, context, accessStrategy);
|
||||
}
|
||||
|
||||
private <T> MapSqlParameterSource createIdParameterSource(Object id, Class<T> domainType) {
|
||||
return new MapSqlParameterSource("id",
|
||||
convert(id, getRequiredPersistentEntity(domainType).getRequiredIdProperty().getColumnType()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S> JdbcPersistentEntity<S> getRequiredPersistentEntity(Class<S> domainType) {
|
||||
return (JdbcPersistentEntity<S>) context.getRequiredPersistentEntity(domainType);
|
||||
}
|
||||
|
||||
private <V> V convert(Object from, Class<V> to) {
|
||||
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JdbcPersistentEntity<?> persistentEntity = context.getPersistentEntity(from.getClass());
|
||||
|
||||
Object id = persistentEntity == null ? null : persistentEntity.getIdentifierAccessor(from).getIdentifier();
|
||||
|
||||
return conversions.convert(id == null ? from : id, to);
|
||||
}
|
||||
|
||||
private SqlGenerator sql(Class<?> domainType) {
|
||||
return sqlGeneratorSource.getSqlGenerator(domainType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
|
||||
/**
|
||||
* {@link Interpreter} for {@link DbAction}s using a {@link JdbcEntityTemplate} for performing actual database
|
||||
* {@link Interpreter} for {@link DbAction}s using a {@link DataAccessStrategy} for performing actual database
|
||||
* interactions.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
@@ -36,16 +36,45 @@ import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
class DefaultJdbcInterpreter implements Interpreter {
|
||||
|
||||
private final JdbcMappingContext context;
|
||||
private final JdbcEntityTemplate template;
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
|
||||
DefaultJdbcInterpreter(JdbcMappingContext context, JdbcEntityTemplate template) {
|
||||
DefaultJdbcInterpreter(JdbcMappingContext context, DataAccessStrategy accessStrategy) {
|
||||
|
||||
this.context = context;
|
||||
this.template = template;
|
||||
this.accessStrategy = accessStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void interpret(Insert<T> insert) {
|
||||
accessStrategy.insert(insert.getEntity(), insert.getEntityType(), createAdditionalColumnValues(insert));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void interpret(Update<T> update) {
|
||||
accessStrategy.update(update.getEntity(), update.getEntityType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void interpret(Delete<T> delete) {
|
||||
|
||||
if (delete.getPropertyPath() == null) {
|
||||
accessStrategy.delete(delete.getRootId(), delete.getEntityType());
|
||||
} else {
|
||||
accessStrategy.delete(delete.getRootId(), delete.getPropertyPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void interpret(DeleteAll<T> delete) {
|
||||
|
||||
if (delete.getEntityType() == null) {
|
||||
accessStrategy.deleteAll(delete.getPropertyPath());
|
||||
} else {
|
||||
accessStrategy.deleteAll(delete.getEntityType());
|
||||
}
|
||||
}
|
||||
|
||||
private <T> Map<String, Object> createAdditionalColumnValues(Insert<T> insert) {
|
||||
|
||||
Map<String, Object> additionalColumnValues = new HashMap<>();
|
||||
DbAction dependingOn = insert.getDependingOn();
|
||||
@@ -59,28 +88,7 @@ class DefaultJdbcInterpreter implements Interpreter {
|
||||
|
||||
additionalColumnValues.put(columnName, identifier);
|
||||
}
|
||||
|
||||
template.insert(insert.getEntity(), insert.getEntityType(), additionalColumnValues);
|
||||
return additionalColumnValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void interpret(Update<T> update) {
|
||||
template.update(update.getEntity(), update.getEntityType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void interpret(Delete<T> delete) {
|
||||
|
||||
if (delete.getPropertyPath() == null) {
|
||||
template.doDelete(delete.getRootId(), delete.getEntityType());
|
||||
} else {
|
||||
template.doDelete(delete.getRootId(), delete.getPropertyPath());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void interpret(DeleteAll<T> delete) {
|
||||
template.doDeleteAll(delete.getEntityType(), delete.getPropertyPath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* delegates all method calls to an instance set after construction. This is useful for {@link DataAccessStrategy}s with
|
||||
* cyclical dependencies.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class DelegatingDataAccessStrategy implements DataAccessStrategy {
|
||||
|
||||
private DataAccessStrategy delegate;
|
||||
|
||||
@Override
|
||||
public <T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
|
||||
delegate.insert(instance, domainType, additionalParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void update(S instance, Class<S> domainType) {
|
||||
delegate.update(instance, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object rootId, PropertyPath propertyPath) {
|
||||
delegate.delete(rootId, propertyPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object id, Class<?> domainType) {
|
||||
delegate.delete(id, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(Class<T> domainType) {
|
||||
delegate.deleteAll(domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
delegate.deleteAll(propertyPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Class<?> domainType) {
|
||||
return delegate.count(domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
|
||||
Assert.notNull(delegate, "Delegate is null");
|
||||
|
||||
return delegate.findById(id, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
return delegate.findAll(domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
return delegate.findAllById(ids, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllByProperty(Object rootId, JdbcPersistentProperty property) {
|
||||
|
||||
Assert.notNull(delegate, "Delegate is null");
|
||||
|
||||
return delegate.findAllByProperty(rootId, property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
return delegate.existsById(id, domainType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called exactly once before calling any of the other methods.
|
||||
*
|
||||
* @param delegate Must not be {@literal null}
|
||||
*/
|
||||
public void setDelegate(DataAccessStrategy delegate) {
|
||||
|
||||
Assert.isNull(this.delegate, "The delegate must be set exactly once");
|
||||
Assert.notNull(delegate, "The delegate must not be set to null");
|
||||
|
||||
this.delegate = delegate;
|
||||
}
|
||||
}
|
||||
@@ -48,16 +48,16 @@ class EntityRowMapper<T> implements RowMapper<T> {
|
||||
private final EntityInstantiator instantiator = new ClassGeneratingEntityInstantiator();
|
||||
private final ConversionService conversions;
|
||||
private final JdbcMappingContext context;
|
||||
private final JdbcEntityOperations template;
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
private final JdbcPersistentProperty idProperty;
|
||||
|
||||
public EntityRowMapper(JdbcPersistentEntity<T> entity, ConversionService conversions, JdbcMappingContext context,
|
||||
JdbcEntityOperations template) {
|
||||
DataAccessStrategy accessStrategy) {
|
||||
|
||||
this.entity = entity;
|
||||
this.conversions = conversions;
|
||||
this.context = context;
|
||||
this.template = template;
|
||||
this.accessStrategy = accessStrategy;
|
||||
|
||||
idProperty = entity.getRequiredIdProperty();
|
||||
}
|
||||
@@ -79,7 +79,7 @@ class EntityRowMapper<T> implements RowMapper<T> {
|
||||
for (JdbcPersistentProperty property : entity) {
|
||||
|
||||
if (Set.class.isAssignableFrom(property.getType())) {
|
||||
propertyAccessor.setProperty(property, template.findAllByProperty(id, property));
|
||||
propertyAccessor.setProperty(property, accessStrategy.findAllByProperty(id, property));
|
||||
} else {
|
||||
propertyAccessor.setProperty(property, readFrom(resultSet, property, ""));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* {@link Collector} which invokes functions on the elements of a {@link java.util.stream.Stream} containing
|
||||
* {@link DataAccessStrategy}s until one function completes without throwing an exception. If all invocations throw
|
||||
* exceptions this {@link Collector} throws itself an exception, gathering all exceptions thrown.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class FunctionCollector<T> implements Collector<DataAccessStrategy, FunctionCollector<T>.ResultOrException, T> {
|
||||
|
||||
private final Function<DataAccessStrategy, T> method;
|
||||
|
||||
FunctionCollector(Function<DataAccessStrategy, T> method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<ResultOrException> supplier() {
|
||||
return ResultOrException::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiConsumer<ResultOrException, DataAccessStrategy> accumulator() {
|
||||
|
||||
return (roe, das) -> {
|
||||
|
||||
if (!roe.hasResult()) {
|
||||
|
||||
try {
|
||||
roe.setResult(method.apply(das));
|
||||
} catch (Exception ex) {
|
||||
roe.add(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryOperator<ResultOrException> combiner() {
|
||||
|
||||
return (roe1, roe2) -> {
|
||||
throw new UnsupportedOperationException("Can't combine method calls");
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<ResultOrException, T> finisher() {
|
||||
|
||||
return roe -> {
|
||||
|
||||
if (roe.hasResult)
|
||||
return roe.result;
|
||||
else
|
||||
throw new CombinedDataAccessException("Failed to perform data access with all available strategies",
|
||||
Collections.unmodifiableList(roe.exceptions));
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Characteristics> characteristics() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
private T result;
|
||||
private final List<Exception> exceptions = new LinkedList<>();
|
||||
private boolean hasResult = false;
|
||||
|
||||
private boolean hasResult() {
|
||||
return hasResult;
|
||||
}
|
||||
|
||||
private void setResult(T result) {
|
||||
this.result = result;
|
||||
hasResult = true;
|
||||
}
|
||||
|
||||
public void add(Exception ex) {
|
||||
exceptions.add(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static class CombinedDataAccessException extends DataAccessException {
|
||||
|
||||
CombinedDataAccessException(String message, List<Exception> exceptions) {
|
||||
super(combineMessage(message, exceptions), exceptions.get(exceptions.size() - 1));
|
||||
}
|
||||
|
||||
private static String combineMessage(String message, List<Exception> exceptions) {
|
||||
return message + exceptions.stream().map(Exception::getMessage).collect(Collectors.joining("\n\t", "\n\t", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
|
||||
/**
|
||||
* Specifies a operations one can perform on a database, based on an <em>Domain Type</em>.
|
||||
*
|
||||
@@ -28,10 +24,6 @@ public interface JdbcEntityOperations {
|
||||
|
||||
<T> void save(T instance, Class<T> domainType);
|
||||
|
||||
<T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameter);
|
||||
|
||||
<T> void update(T instance, Class<T> domainType);
|
||||
|
||||
<T> void deleteById(Object id, Class<T> domainType);
|
||||
|
||||
<T> void delete(T entity, Class<T> domainType);
|
||||
@@ -46,8 +38,6 @@ public interface JdbcEntityOperations {
|
||||
|
||||
<T> Iterable<T> findAll(Class<T> domainType);
|
||||
|
||||
<T> Iterable<T> findAllByProperty(Object id, JdbcPersistentProperty property);
|
||||
|
||||
<T> boolean existsById(Object id, Class<T> domainType);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,19 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.data.convert.Jsr310Converters;
|
||||
import org.springframework.data.jdbc.core.conversion.AggregateChange;
|
||||
import org.springframework.data.jdbc.core.conversion.AggregateChange.Kind;
|
||||
import org.springframework.data.jdbc.core.conversion.Interpreter;
|
||||
@@ -39,61 +29,35 @@ import org.springframework.data.jdbc.mapping.event.BeforeDelete;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeSave;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.support.JdbcUtil;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
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.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link JdbcEntityOperations} implementation, storing complete entities including references in a JDBC data store.
|
||||
* {@link JdbcEntityOperations} implementation, storing aggregates in and obtaining them from a JDBC data store.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class JdbcEntityTemplate implements JdbcEntityOperations {
|
||||
|
||||
private static final String ENTITY_NEW_AFTER_INSERT = "Entity [%s] still 'new' after insert. Please set either"
|
||||
+ " the id property in a BeforeInsert event handler, or ensure the database creates a value and your "
|
||||
+ "JDBC driver returns it.";
|
||||
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final NamedParameterJdbcOperations operations;
|
||||
private final JdbcMappingContext context;
|
||||
private final ConversionService conversions = getDefaultConversionService();
|
||||
private final Interpreter interpreter;
|
||||
private final SqlGeneratorSource sqlGeneratorSource;
|
||||
|
||||
private final JdbcEntityWriter jdbcEntityWriter;
|
||||
private final JdbcEntityDeleteWriter jdbcEntityDeleteWriter;
|
||||
|
||||
public JdbcEntityTemplate(ApplicationEventPublisher publisher, NamedParameterJdbcOperations operations,
|
||||
JdbcMappingContext context) {
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
|
||||
public JdbcEntityTemplate(ApplicationEventPublisher publisher, JdbcMappingContext context,
|
||||
DataAccessStrategy dataAccessStrategy) {
|
||||
|
||||
this.publisher = publisher;
|
||||
this.operations = operations;
|
||||
this.context = context;
|
||||
|
||||
this.jdbcEntityWriter = new JdbcEntityWriter(this.context);
|
||||
this.jdbcEntityDeleteWriter = new JdbcEntityDeleteWriter(this.context);
|
||||
this.sqlGeneratorSource = new SqlGeneratorSource(this.context);
|
||||
this.interpreter = new DefaultJdbcInterpreter(this.context, this);
|
||||
}
|
||||
|
||||
private static GenericConversionService getDefaultConversionService() {
|
||||
|
||||
DefaultConversionService conversionService = new DefaultConversionService();
|
||||
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);
|
||||
|
||||
return conversionService;
|
||||
this.jdbcEntityWriter = new JdbcEntityWriter(context);
|
||||
this.jdbcEntityDeleteWriter = new JdbcEntityDeleteWriter(context);
|
||||
this.accessStrategy = dataAccessStrategy;
|
||||
this.interpreter = new DefaultJdbcInterpreter(context, accessStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,94 +83,29 @@ public class JdbcEntityTemplate implements JdbcEntityOperations {
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
|
||||
|
||||
KeyHolder holder = new GeneratedKeyHolder();
|
||||
JdbcPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
|
||||
JdbcPersistentEntityInformation<T, ?> entityInformation = context
|
||||
.getRequiredPersistentEntityInformation(domainType);
|
||||
|
||||
MapSqlParameterSource parameterSource = getPropertyMap(instance, persistentEntity);
|
||||
|
||||
Object idValue = getIdValueOrNull(instance, persistentEntity);
|
||||
JdbcPersistentProperty idProperty = persistentEntity.getRequiredIdProperty();
|
||||
parameterSource.addValue(idProperty.getColumnName(), convert(idValue, idProperty.getColumnType()),
|
||||
JdbcUtil.sqlTypeFor(idProperty.getColumnType()));
|
||||
|
||||
additionalParameters.forEach(parameterSource::addValue);
|
||||
|
||||
operations.update(sql(domainType).getInsert(idValue == null, additionalParameters.keySet()), parameterSource,
|
||||
holder);
|
||||
|
||||
setIdFromJdbc(instance, holder, persistentEntity);
|
||||
|
||||
if (entityInformation.isNew(instance)) {
|
||||
throw new IllegalStateException(String.format(ENTITY_NEW_AFTER_INSERT, persistentEntity));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void update(S instance, Class<S> domainType) {
|
||||
|
||||
JdbcPersistentEntity<S> persistentEntity = getRequiredPersistentEntity(domainType);
|
||||
|
||||
operations.update(sql(domainType).getUpdate(), getPropertyMap(instance, persistentEntity));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public long count(Class<?> domainType) {
|
||||
return operations.getJdbcOperations().queryForObject(sql(domainType).getCount(), Long.class);
|
||||
return accessStrategy.count(domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
|
||||
String findOneSql = sql(domainType).getFindOne();
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
return operations.queryForObject(findOneSql, parameter, getEntityRowMapper(domainType));
|
||||
return accessStrategy.findById(id, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
|
||||
String existsSql = sql(domainType).getExists();
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
return operations.queryForObject(existsSql, parameter, Boolean.class);
|
||||
return accessStrategy.existsById(id, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
return operations.query(sql(domainType).getFindAll(), getEntityRowMapper(domainType));
|
||||
return accessStrategy.findAll(domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
|
||||
String findAllInListSql = sql(domainType).getFindAllInList();
|
||||
Class<?> targetType = getRequiredPersistentEntity(domainType).getRequiredIdProperty().getColumnType();
|
||||
|
||||
MapSqlParameterSource parameter = new MapSqlParameterSource( //
|
||||
"ids", //
|
||||
StreamSupport.stream(ids.spliterator(), false) //
|
||||
.map(id -> convert(id, targetType)) //
|
||||
.collect(Collectors.toList()) //
|
||||
);
|
||||
|
||||
return operations.query(findAllInListSql, parameter, getEntityRowMapper(domainType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllByProperty(Object id, JdbcPersistentProperty property) {
|
||||
|
||||
Class<?> actualType = property.getActualType();
|
||||
String findAllByProperty = sql(actualType).getFindAllByProperty(property.getReverseColumnName());
|
||||
|
||||
MapSqlParameterSource parameter = new MapSqlParameterSource(property.getReverseColumnName(), id);
|
||||
|
||||
return (Iterable<T>) operations.query(findAllByProperty, parameter, getEntityRowMapper(actualType));
|
||||
return accessStrategy.findAllById(ids, domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -240,30 +139,6 @@ public class JdbcEntityTemplate implements JdbcEntityOperations {
|
||||
change.executeWith(interpreter);
|
||||
|
||||
publisher.publishEvent(new AfterDelete(specifiedId, optionalEntity, change));
|
||||
|
||||
}
|
||||
|
||||
void doDelete(Object rootId, PropertyPath propertyPath) {
|
||||
|
||||
JdbcPersistentEntity<?> rootEntity = context.getRequiredPersistentEntity(propertyPath.getOwningType());
|
||||
|
||||
JdbcPersistentProperty referencingProperty = rootEntity.getRequiredPersistentProperty(propertyPath.getSegment());
|
||||
Assert.notNull(referencingProperty, "No property found matching the PropertyPath " + propertyPath);
|
||||
|
||||
String format = sql(rootEntity.getType()).createDeleteByPath(propertyPath);
|
||||
|
||||
HashMap<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("rootId", rootId);
|
||||
operations.update(format, parameters);
|
||||
|
||||
}
|
||||
|
||||
void doDelete(Object id, Class<?> domainType) {
|
||||
|
||||
String deleteByIdSql = sql(domainType).getDeleteById();
|
||||
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
|
||||
|
||||
operations.update(deleteByIdSql, parameter);
|
||||
}
|
||||
|
||||
private <T> AggregateChange createChange(T instance) {
|
||||
@@ -286,110 +161,4 @@ public class JdbcEntityTemplate implements JdbcEntityOperations {
|
||||
jdbcEntityDeleteWriter.write(null, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
private <T> MapSqlParameterSource createIdParameterSource(Object id, Class<T> domainType) {
|
||||
return new MapSqlParameterSource("id",
|
||||
convert(id, getRequiredPersistentEntity(domainType).getRequiredIdProperty().getColumnType()));
|
||||
}
|
||||
|
||||
private <S> MapSqlParameterSource getPropertyMap(final S instance, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
MapSqlParameterSource parameters = new MapSqlParameterSource();
|
||||
|
||||
persistentEntity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) property -> {
|
||||
if (!property.isEntity()) {
|
||||
Object value = persistentEntity.getPropertyAccessor(instance).getProperty(property);
|
||||
|
||||
Object convertedValue = convert(value, property.getColumnType());
|
||||
parameters.addValue(property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
|
||||
}
|
||||
});
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private <S, ID> ID getIdValueOrNull(S instance, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
EntityInformation<S, ID> entityInformation = new BasicJdbcPersistentEntityInformation<>(persistentEntity);
|
||||
|
||||
ID idValue = entityInformation.getId(instance);
|
||||
|
||||
return isIdPropertySimpleTypeAndValueZero(idValue, persistentEntity) ? null : idValue;
|
||||
}
|
||||
|
||||
private <S> void setIdFromJdbc(S instance, KeyHolder holder, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
JdbcPersistentEntityInformation<S, ?> entityInformation = new BasicJdbcPersistentEntityInformation<>(
|
||||
persistentEntity);
|
||||
|
||||
try {
|
||||
|
||||
getIdFromHolder(holder, persistentEntity).ifPresent(it -> {
|
||||
|
||||
Class<?> targetType = persistentEntity.getRequiredIdProperty().getType();
|
||||
Object converted = convert(it, targetType);
|
||||
entityInformation.setId(instance, converted);
|
||||
});
|
||||
|
||||
} catch (NonTransientDataAccessException e) {
|
||||
throw new UnableToSetId("Unable to set id of " + instance, e);
|
||||
}
|
||||
}
|
||||
|
||||
private <S> Optional<Object> getIdFromHolder(KeyHolder holder, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
try {
|
||||
// MySQL just returns one value with a special name
|
||||
return Optional.ofNullable(holder.getKey());
|
||||
} catch (InvalidDataAccessApiUsageException e) {
|
||||
// Postgres returns a value for each column
|
||||
return Optional.ofNullable(holder.getKeys().get(persistentEntity.getIdColumn()));
|
||||
}
|
||||
}
|
||||
|
||||
private <V> V convert(Object from, Class<V> to) {
|
||||
|
||||
if (from == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JdbcPersistentEntity<?> persistentEntity = context.getPersistentEntity(from.getClass());
|
||||
|
||||
Object id = persistentEntity == null ? null : persistentEntity.getIdentifierAccessor(from).getIdentifier();
|
||||
|
||||
return conversions.convert(id == null ? from : id, to);
|
||||
}
|
||||
|
||||
private <S, ID> boolean isIdPropertySimpleTypeAndValueZero(ID idValue, JdbcPersistentEntity<S> persistentEntity) {
|
||||
|
||||
JdbcPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
return idValue == null //
|
||||
|| idProperty == null //
|
||||
|| (idProperty.getType() == int.class && idValue.equals(0)) //
|
||||
|| (idProperty.getType() == long.class && idValue.equals(0L));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S> JdbcPersistentEntity<S> getRequiredPersistentEntity(Class<S> domainType) {
|
||||
return (JdbcPersistentEntity<S>) context.getRequiredPersistentEntity(domainType);
|
||||
}
|
||||
|
||||
private SqlGenerator sql(Class<?> domainType) {
|
||||
return sqlGeneratorSource.getSqlGenerator(domainType);
|
||||
}
|
||||
|
||||
private <T> EntityRowMapper<T> getEntityRowMapper(Class<T> domainType) {
|
||||
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), conversions, context, this);
|
||||
}
|
||||
|
||||
<T> void doDeleteAll(Class<T> domainType, PropertyPath propertyPath) {
|
||||
|
||||
operations.getJdbcOperations()
|
||||
.update(sql(propertyPath == null ? domainType : propertyPath.getOwningType().getType())
|
||||
.createDeleteAllSql(propertyPath));
|
||||
}
|
||||
|
||||
public NamedParameterJdbcOperations getOperations() {
|
||||
return operations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.mybatis;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@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
|
||||
*/
|
||||
public class MyBatisContext {
|
||||
|
||||
private final Object id;
|
||||
private final Object instance;
|
||||
private final Class domainType;
|
||||
private final Map<String, Object> additonalValues;
|
||||
|
||||
public MyBatisContext(Object id, Object instance, Class domainType, Map<String, Object> additonalValues) {
|
||||
|
||||
this.id = id;
|
||||
this.instance = instance;
|
||||
this.domainType = domainType;
|
||||
this.additonalValues = additonalValues;
|
||||
}
|
||||
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Object getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Class getDomainType() {
|
||||
return domainType;
|
||||
}
|
||||
|
||||
public Object get(String key) {
|
||||
return additonalValues.get(key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.mybatis;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.data.jdbc.core.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
|
||||
/**
|
||||
* {@link DataAccessStrategy} implementation based on MyBatis.
|
||||
*
|
||||
* Each method gets mapped to a statement. The name of the statement gets constructed as follows:
|
||||
*
|
||||
* The namespace is based on the class of the entity plus the suffix "Mapper". This is then followed by the method name separated by a dot.
|
||||
*
|
||||
* For methods taking a {@link PropertyPath} as argument, the relevant entity is that of the root of the path, and the path itself gets as dot separated String appended to the statement name.
|
||||
*
|
||||
* Each statement gets an instance of {@link MyBatisContext}, which at least has the entityType set.
|
||||
*
|
||||
* For methods taking a {@link PropertyPath} the entityTyoe if the context is set to the class of the leaf type.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class MyBatisDataAccessStrategy implements DataAccessStrategy {
|
||||
|
||||
private static final String MAPPER_SUFFIX = "Mapper";
|
||||
|
||||
private final SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
public MyBatisDataAccessStrategy(SqlSessionFactory sqlSessionFactory) {
|
||||
|
||||
this.sqlSessionFactory = sqlSessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
|
||||
sqlSession().insert(mapper(domainType) + ".insert",
|
||||
new MyBatisContext(null, instance, domainType, additionalParameters));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> void update(S instance, Class<S> domainType) {
|
||||
|
||||
sqlSession().update(mapper(domainType) + ".update",
|
||||
new MyBatisContext(null, instance, domainType, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object id, Class<?> domainType) {
|
||||
|
||||
sqlSession().delete(mapper(domainType) + ".delete",
|
||||
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object rootId, PropertyPath propertyPath) {
|
||||
|
||||
sqlSession().delete(mapper(propertyPath.getOwningType().getType()) + ".delete." + propertyPath.toDotPath(),
|
||||
new MyBatisContext(rootId, null, propertyPath.getLeafProperty().getTypeInformation().getType(),
|
||||
Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(Class<T> domainType) {
|
||||
|
||||
sqlSession().delete( //
|
||||
mapper(domainType) + ".deleteAll", //
|
||||
new MyBatisContext(null, null, domainType, Collections.emptyMap()) //
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void deleteAll(PropertyPath propertyPath) {
|
||||
|
||||
Class baseType = propertyPath.getOwningType().getType();
|
||||
Class leaveType = propertyPath.getLeafProperty().getTypeInformation().getType();
|
||||
|
||||
sqlSession().delete( //
|
||||
mapper(baseType) + ".deleteAll." + propertyPath.toDotPath(), //
|
||||
new MyBatisContext(null, null, leaveType, Collections.emptyMap()) //
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
return sqlSession().selectOne(mapper(domainType) + ".findById",
|
||||
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
return sqlSession().selectList(mapper(domainType) + ".findAll",
|
||||
new MyBatisContext(null, null, domainType, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
return sqlSession().selectList(mapper(domainType) + ".findAllById",
|
||||
new MyBatisContext(ids, null, domainType, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> findAllByProperty(Object rootId, JdbcPersistentProperty property) {
|
||||
return sqlSession().selectList(mapper(property.getOwner().getType()) + ".findAllByProperty." + property.getName(),
|
||||
new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
return sqlSession().selectOne(mapper(domainType) + ".existsById",
|
||||
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Class<?> domainType) {
|
||||
return sqlSession().selectOne(mapper(domainType) + ".count");
|
||||
}
|
||||
|
||||
private String mapper(Class<?> domainType) {
|
||||
return domainType.getName() + MAPPER_SUFFIX;
|
||||
}
|
||||
|
||||
private SqlSession sqlSession() {
|
||||
return sqlSessionFactory.openSession();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.data.jdbc.core.JdbcEntityOperations;
|
||||
import org.springframework.data.jdbc.core.JdbcEntityTemplate;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityInformation;
|
||||
@@ -75,12 +74,7 @@ public class SimpleJdbcRepository<T, ID> implements CrudRepository<T, ID> {
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> findById(ID id) {
|
||||
|
||||
try {
|
||||
return Optional.of(entityOperations.findById(id, entityInformation.getJavaType()));
|
||||
} catch (EmptyResultDataAccessException ex) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.ofNullable(entityOperations.findById(id, entityInformation.getJavaType()));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -16,18 +16,17 @@
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.jdbc.core.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.JdbcEntityTemplate;
|
||||
import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
|
||||
import org.springframework.data.jdbc.repository.SimpleJdbcRepository;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
@@ -37,15 +36,15 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
public class JdbcRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private final JdbcMappingContext context;
|
||||
private final NamedParameterJdbcOperations jdbcOperations;
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
|
||||
public JdbcRepositoryFactory(NamedParameterJdbcOperations namedParameterJdbcOperations,
|
||||
ApplicationEventPublisher publisher, NamingStrategy namingStrategy) {
|
||||
public JdbcRepositoryFactory(ApplicationEventPublisher publisher, JdbcMappingContext context,
|
||||
DataAccessStrategy dataAccessStrategy) {
|
||||
|
||||
this.jdbcOperations = namedParameterJdbcOperations;
|
||||
this.publisher = publisher;
|
||||
this.context = new JdbcMappingContext(namingStrategy);
|
||||
this.context = context;
|
||||
this.accessStrategy = dataAccessStrategy;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -62,7 +61,7 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
JdbcPersistentEntityInformation persistentEntityInformation = context
|
||||
.getRequiredPersistentEntityInformation(repositoryInformation.getDomainType());
|
||||
JdbcEntityTemplate template = new JdbcEntityTemplate(publisher, jdbcOperations, context);
|
||||
JdbcEntityTemplate template = new JdbcEntityTemplate(publisher, context, accessStrategy);
|
||||
|
||||
return new SimpleJdbcRepository<>(template, persistentEntityInformation);
|
||||
}
|
||||
@@ -71,4 +70,5 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
|
||||
protected Class<?> getRepositoryBaseClass(RepositoryMetadata repositoryMetadata) {
|
||||
return SimpleJdbcRepository.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,15 +16,26 @@
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.jdbc.core.CascadingDataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.DataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.DefaultDataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.DelegatingDataAccessStrategy;
|
||||
import org.springframework.data.jdbc.core.SqlGeneratorSource;
|
||||
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
|
||||
import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
|
||||
@@ -32,6 +43,7 @@ import org.springframework.data.util.Optionals;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Special adapter for Springs {@link org.springframework.beans.factory.FactoryBean} interface to allow easy setup of
|
||||
@@ -52,6 +64,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
|
||||
private static final String JDBC_OPERATIONS_BEAN_NAME = "jdbcTemplate";
|
||||
private static final String DATA_SOURCE_BEAN_NAME = "dataSource";
|
||||
private static final String NAMING_STRATEGY_BEAN_NAME = "namingStrategy";
|
||||
private static final String SQL_SESSION_FACTORY_BEAN_NAME = "sqlSessionFactory";
|
||||
|
||||
private final ApplicationEventPublisher applicationEventPublisher;
|
||||
private final ApplicationContext context;
|
||||
@@ -66,8 +79,40 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
|
||||
|
||||
@Override
|
||||
protected RepositoryFactorySupport doCreateRepositoryFactory() {
|
||||
return new JdbcRepositoryFactory(findOrCreateJdbcOperations(), applicationEventPublisher,
|
||||
findOrCreateNamingStrategy());
|
||||
|
||||
final JdbcMappingContext context = new JdbcMappingContext(findOrCreateNamingStrategy());
|
||||
|
||||
DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy();
|
||||
|
||||
List<DataAccessStrategy> accessStrategies = Stream.of( //
|
||||
createMyBatisDataAccessStrategy(), //
|
||||
createDefaultAccessStrategy(context, delegatingDataAccessStrategy) //
|
||||
) //
|
||||
.filter(Optional::isPresent) //
|
||||
.map(Optional::get) //
|
||||
.collect(Collectors.toList());
|
||||
|
||||
CascadingDataAccessStrategy strategy = new CascadingDataAccessStrategy(accessStrategies);
|
||||
delegatingDataAccessStrategy.setDelegate(strategy);
|
||||
|
||||
return new JdbcRepositoryFactory(applicationEventPublisher, context, strategy);
|
||||
}
|
||||
|
||||
private Optional<DataAccessStrategy> createMyBatisDataAccessStrategy() {
|
||||
|
||||
if (!ClassUtils.isPresent("org.apache.ibatis.session.SqlSessionFactory", this.getClass().getClassLoader())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return getBean(SqlSessionFactory.class, SQL_SESSION_FACTORY_BEAN_NAME)
|
||||
.map(ssf -> new MyBatisDataAccessStrategy(ssf));
|
||||
}
|
||||
|
||||
private Optional<DataAccessStrategy> createDefaultAccessStrategy(JdbcMappingContext context,
|
||||
DelegatingDataAccessStrategy delegatingDataAccessStrategy) {
|
||||
|
||||
return Optional.of(new DefaultDataAccessStrategy(new SqlGeneratorSource(context), findOrCreateJdbcOperations(),
|
||||
context, delegatingDataAccessStrategy));
|
||||
}
|
||||
|
||||
private NamedParameterJdbcOperations findOrCreateJdbcOperations() {
|
||||
|
||||
Reference in New Issue
Block a user