domainType);
/**
- * Updates the data of a single entity in the database and enforce optimistic record locking using the {@code previousVersion}
- * property. Referenced entities don't get handled.
+ * Updates the data of a single entity in the database and enforce optimistic record locking using the
+ * {@code previousVersion} property. Referenced entities don't get handled.
*
* The statement will be of the form : {@code UPDATE … SET … WHERE ID = :id and VERSION_COLUMN = :previousVersion }
* and throw an optimistic record locking exception if no rows have been updated.
@@ -87,7 +92,8 @@ default Object insert(T instance, Class domainType, Identifier identifier
* @param previousVersion The previous version assigned to the instance being saved.
* @param the type of the instance to save.
* @return whether the update actually updated a row.
- * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the optimistic locking version check failed.
+ * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the
+ * optimistic locking version check failed.
* @since 2.0
*/
boolean updateWithVersion(T instance, Class domainType, Number previousVersion);
@@ -113,7 +119,8 @@ default Object insert(T instance, Class domainType, Identifier identifier
* @param domainType the type of entity to be deleted. Implicitly determines the table to operate on. Must not be
* {@code null}.
* @param previousVersion The previous version assigned to the instance being saved.
- * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the optimistic locking version check failed.
+ * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the
+ * optimistic locking version check failed.
* @since 2.0
*/
void deleteWithVersion(Object id, Class domainType, Number previousVersion);
@@ -211,4 +218,24 @@ default Iterable findAllByPath(Identifier identifier,
* @return {@code true} if a matching row exists, otherwise {@code false}.
*/
boolean existsById(Object id, Class domainType);
+
+ /**
+ * Loads all entities of the given type, sorted.
+ *
+ * @param domainType the type of entities to load. Must not be {@code null}.
+ * @param the type of entities to load.
+ * @param sort the sorting information. Must not be {@code null}.
+ * @return Guaranteed to be not {@code null}.
+ */
+ Iterable findAll(Class domainType, Sort sort);
+
+ /**
+ * Loads all entities of the given type, paged and sorted.
+ *
+ * @param domainType the type of entities to load. Must not be {@code null}.
+ * @param the type of entities to load.
+ * @param pageable the pagination information. Must not be {@code null}.
+ * @return Guaranteed to be not {@code null}.
+ */
+ Iterable findAll(Class domainType, Pageable pageable);
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
index 04ae24ead8..6ce173d754 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
@@ -15,13 +15,11 @@
*/
package org.springframework.data.jdbc.core.convert;
-import static org.springframework.data.jdbc.core.convert.SqlGenerator.VERSION_SQL_PARAMETER_NAME;
+import static org.springframework.data.jdbc.core.convert.SqlGenerator.*;
import java.sql.JDBCType;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -31,20 +29,23 @@
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.OptimisticLockingFailureException;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyHandler;
-import org.springframework.data.relational.core.conversion.RelationalEntityVersionUtils;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
+import org.springframework.data.relational.domain.IdentifierProcessing;
+import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.jdbc.core.RowMapper;
-import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
+import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.KeyHolder;
@@ -61,6 +62,7 @@
* @author Christoph Strobl
* @author Tom Hombergs
* @author Tyler Van Gorder
+ * @author Milan Milanov
* @since 1.1
*/
public class DefaultDataAccessStrategy implements DataAccessStrategy {
@@ -98,7 +100,8 @@ public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, Relation
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
- public Object insert(T instance, Class domainType, Map additionalParameters) {
+ @Nullable
+ public Object insert(T instance, Class domainType, Map additionalParameters) {
return insert(instance, domainType, Identifier.from(additionalParameters));
}
@@ -109,11 +112,11 @@ public Object insert(T instance, Class domainType, Map ad
@Override
public Object insert(T instance, Class domainType, Identifier identifier) {
- KeyHolder holder = new GeneratedKeyHolder();
+ SqlGenerator sqlGenerator = sql(domainType);
RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType);
- MapSqlParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
- PersistentProperty::isIdProperty);
+ SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
+ PersistentProperty::isIdProperty, getIdentifierProcessing());
identifier.forEach((name, value, type) -> addConvertedPropertyValue(parameterSource, name, value, type));
@@ -124,8 +127,10 @@ public Object insert(T instance, Class domainType, Identifier identifier)
addConvertedPropertyValue(parameterSource, idProperty, idValue, idProperty.getColumnName());
}
+ KeyHolder holder = new GeneratedKeyHolder();
+
operations.update( //
- sql(domainType).getInsert(new HashSet<>(Arrays.asList(parameterSource.getParameterNames()))), //
+ sqlGenerator.getInsert(new HashSet<>(parameterSource.getIdentifiers())), //
parameterSource, //
holder //
);
@@ -142,7 +147,7 @@ public boolean update(S instance, Class domainType) {
RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType);
return operations.update(sql(domainType).getUpdate(),
- getParameterSource(instance, persistentEntity, "", Predicates.includeAll())) != 0;
+ getParameterSource(instance, persistentEntity, "", Predicates.includeAll(), getIdentifierProcessing())) != 0;
}
/*
@@ -155,13 +160,14 @@ public boolean updateWithVersion(S instance, Class domainType, Number pre
RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType);
// Adjust update statement to set the new version and use the old version in where clause.
- MapSqlParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
- Predicates.includeAll());
- parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, previousVersion);
+ SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
+ Predicates.includeAll(), getIdentifierProcessing());
+ parameterSource.addValue(VERSION_SQL_PARAMETER, previousVersion);
int affectedRows = operations.update(sql(domainType).getUpdateWithVersion(), parameterSource);
if (affectedRows == 0) {
+
throw new OptimisticLockingFailureException(
String.format("Optimistic lock exception on saving entity of type %s.", persistentEntity.getName()));
}
@@ -177,7 +183,7 @@ public boolean updateWithVersion(S instance, Class domainType, Number pre
public void delete(Object id, Class> domainType) {
String deleteByIdSql = sql(domainType).getDeleteById();
- MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
+ SqlParameterSource parameter = createIdParameterSource(id, domainType);
operations.update(deleteByIdSql, parameter);
}
@@ -193,8 +199,8 @@ public void deleteWithVersion(Object id, Class domainType, Number previou
RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType);
- MapSqlParameterSource parameterSource = createIdParameterSource(id, domainType);
- parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, previousVersion);
+ SqlIdentifierParameterSource parameterSource = createIdParameterSource(id, domainType);
+ parameterSource.addValue(VERSION_SQL_PARAMETER, previousVersion);
int affectedRows = operations.update(sql(domainType).getDeleteByIdAndVersion(), parameterSource);
if (affectedRows == 0) {
@@ -216,11 +222,11 @@ public void delete(Object rootId, PersistentPropertyPath parameters = new HashMap<>();
- parameters.put("rootId", rootId);
- operations.update(format, parameters);
+ SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(getIdentifierProcessing());
+ parameters.addValue(ROOT_ID_PARAMETER, rootId);
+ operations.update(delete, parameters);
}
/*
@@ -265,7 +271,7 @@ public long count(Class> domainType) {
public T findById(Object id, Class domainType) {
String findOneSql = sql(domainType).getFindOne();
- MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
+ SqlIdentifierParameterSource parameter = createIdParameterSource(id, domainType);
try {
return operations.queryForObject(findOneSql, parameter, (RowMapper) getEntityRowMapper(domainType));
@@ -297,9 +303,9 @@ public Iterable findAllById(Iterable> ids, Class domainType) {
}
RelationalPersistentProperty idProperty = getRequiredPersistentEntity(domainType).getRequiredIdProperty();
- MapSqlParameterSource parameterSource = new MapSqlParameterSource();
+ SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(getIdentifierProcessing());
- addConvertedPropertyValuesAsList(parameterSource, idProperty, ids, "ids");
+ addConvertedPropertyValuesAsList(parameterSource, idProperty, ids, IDS_SQL_PARAMETER);
String findAllInListSql = sql(domainType).getFindAllInList();
@@ -324,12 +330,20 @@ public Iterable findAllByPath(Identifier identifier,
String findAllByProperty = sql(actualType) //
.getFindAllByProperty(identifier, path.getQualifierColumn(), path.isOrdered());
- MapSqlParameterSource parameters = new MapSqlParameterSource(identifier.toMap());
-
RowMapper> rowMapper = path.isMap() ? this.getMapEntityRowMapper(path, identifier)
: this.getEntityRowMapper(path, identifier);
- return operations.query(findAllByProperty, parameters, (RowMapper) rowMapper);
+ return operations.query(findAllByProperty, createParameterSource(identifier, getIdentifierProcessing()),
+ (RowMapper) rowMapper);
+ }
+
+ private SqlParameterSource createParameterSource(Identifier identifier, IdentifierProcessing identifierProcessing) {
+
+ SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(identifierProcessing);
+
+ identifier.toMap().forEach(parameterSource::addValue);
+
+ return parameterSource;
}
/*
@@ -355,7 +369,7 @@ public Iterable findAllByProperty(Object rootId, RelationalPersistentPro
public boolean existsById(Object id, Class domainType) {
String existsSql = sql(domainType).getExists();
- MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
+ SqlParameterSource parameter = createIdParameterSource(id, domainType);
Boolean result = operations.queryForObject(existsSql, parameter, Boolean.class);
Assert.state(result != null, "The result of an exists query must not be null");
@@ -363,10 +377,31 @@ public boolean existsById(Object id, Class domainType) {
return result;
}
- private MapSqlParameterSource getParameterSource(@Nullable S instance, RelationalPersistentEntity persistentEntity,
- String prefix, Predicate skipProperty) {
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class, org.springframework.data.domain.Sort)
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Iterable findAll(Class domainType, Sort sort) {
+ return operations.query(sql(domainType).getFindAll(sort), (RowMapper) getEntityRowMapper(domainType));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class, org.springframework.data.domain.Pageable)
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Iterable findAll(Class domainType, Pageable pageable) {
+ return operations.query(sql(domainType).getFindAll(pageable), (RowMapper) getEntityRowMapper(domainType));
+ }
+
+ private SqlIdentifierParameterSource getParameterSource(@Nullable S instance,
+ RelationalPersistentEntity persistentEntity, String prefix,
+ Predicate skipProperty, IdentifierProcessing identifierProcessing) {
- MapSqlParameterSource parameters = new MapSqlParameterSource();
+ SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing);
PersistentPropertyAccessor propertyAccessor = instance != null ? persistentEntity.getPropertyAccessor(instance)
: NoValuePropertyAccessor.instance();
@@ -384,13 +419,14 @@ private MapSqlParameterSource getParameterSource(@Nullable S instance, Re
Object value = propertyAccessor.getProperty(property);
RelationalPersistentEntity> embeddedEntity = context.getPersistentEntity(property.getType());
- MapSqlParameterSource additionalParameters = getParameterSource((T) value,
- (RelationalPersistentEntity) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty);
- parameters.addValues(additionalParameters.getValues());
+ SqlIdentifierParameterSource additionalParameters = getParameterSource((T) value,
+ (RelationalPersistentEntity) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty,
+ identifierProcessing);
+ parameters.addAll(additionalParameters);
} else {
Object value = propertyAccessor.getProperty(property);
- String paramName = prefix + property.getColumnName();
+ SqlIdentifier paramName = property.getColumnName().prefix(prefix);
addConvertedPropertyValue(parameters, property, value, paramName);
}
@@ -434,7 +470,7 @@ private Object getIdFromHolder(KeyHolder holder, RelationalPersistentEntity<
return null;
}
- return keys.get(persistentEntity.getIdColumn());
+ return keys.get(persistentEntity.getIdColumn().toColumnName(getIdentifierProcessing()));
}
}
@@ -448,55 +484,58 @@ private EntityRowMapper> getEntityRowMapper(PersistentPropertyPathExtension pa
private RowMapper> getMapEntityRowMapper(PersistentPropertyPathExtension path, Identifier identifier) {
- String keyColumn = path.getQualifierColumn();
+ SqlIdentifier keyColumn = path.getQualifierColumn();
Assert.notNull(keyColumn, () -> "KeyColumn must not be null for " + path);
- return new MapEntityRowMapper<>(path, converter, identifier, keyColumn);
+ return new MapEntityRowMapper<>(path, converter, identifier, keyColumn, getIdentifierProcessing());
}
- private MapSqlParameterSource createIdParameterSource(Object id, Class domainType) {
+ private SqlIdentifierParameterSource createIdParameterSource(Object id, Class domainType) {
- MapSqlParameterSource parameterSource = new MapSqlParameterSource();
+ SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(getIdentifierProcessing());
addConvertedPropertyValue( //
parameterSource, //
getRequiredPersistentEntity(domainType).getRequiredIdProperty(), //
id, //
- "id" //
+ ID_SQL_PARAMETER //
);
return parameterSource;
}
- private void addConvertedPropertyValue(MapSqlParameterSource parameterSource, RelationalPersistentProperty property,
- @Nullable Object value, String paramName) {
+ private IdentifierProcessing getIdentifierProcessing() {
+ return sqlGeneratorSource.getDialect().getIdentifierProcessing();
+ }
- JdbcValue jdbcValue = converter.writeJdbcValue( //
- value, //
- property.getColumnType(), //
- property.getSqlType() //
- );
+ private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource,
+ RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) {
- parameterSource.addValue(paramName, jdbcValue.getValue(), JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType()));
+ addConvertedValue(parameterSource, value, name, property.getColumnType(), property.getSqlType());
}
- private void addConvertedPropertyValue(MapSqlParameterSource parameterSource, String name, Object value,
- Class> type) {
+ private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value,
+ Class> javaType) {
+
+ addConvertedValue(parameterSource, value, name, javaType, JdbcUtil.sqlTypeFor(javaType));
+ }
+
+ private void addConvertedValue(SqlIdentifierParameterSource parameterSource, @Nullable Object value,
+ SqlIdentifier paramName, Class> javaType, int sqlType) {
JdbcValue jdbcValue = converter.writeJdbcValue( //
value, //
- type, //
- JdbcUtil.sqlTypeFor(type) //
+ javaType, //
+ sqlType //
);
parameterSource.addValue( //
- name, //
+ paramName, //
jdbcValue.getValue(), //
- JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType()) //
- );
+ JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType()));
}
- private void addConvertedPropertyValuesAsList(MapSqlParameterSource parameterSource,
- RelationalPersistentProperty property, Iterable> values, String paramName) {
+ private void addConvertedPropertyValuesAsList(SqlIdentifierParameterSource parameterSource,
+ RelationalPersistentProperty property, Iterable> values, SqlIdentifier paramName) {
List convertedIds = new ArrayList<>();
JdbcValue jdbcValue = null;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
index 7996ac4b30..9a797a80ec 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
@@ -17,9 +17,12 @@
import java.util.Map;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
+import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.util.Assert;
/**
@@ -28,6 +31,7 @@
*
* @author Jens Schauder
* @author Tyler Van Gorder
+ * @author Milan Milanov
* @since 1.1
*/
public class DelegatingDataAccessStrategy implements DataAccessStrategy {
@@ -39,7 +43,7 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
- public Object insert(T instance, Class domainType, Map additionalParameters) {
+ public Object insert(T instance, Class domainType, Map additionalParameters) {
return delegate.insert(instance, domainType, additionalParameters);
}
@@ -70,6 +74,7 @@ public boolean updateWithVersion(S instance, Class domainType, Number nex
return delegate.updateWithVersion(instance, domainType, nextVersion);
}
+
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, org.springframework.data.mapping.PersistentPropertyPath)
@@ -185,6 +190,24 @@ public boolean existsById(Object id, Class domainType) {
return delegate.existsById(id, domainType);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class, org.springframework.data.domain.Sort)
+ */
+ @Override
+ public Iterable findAll(Class domainType, Sort sort) {
+ return delegate.findAll(domainType, sort);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class, org.springframework.data.domain.Pageable)
+ */
+ @Override
+ public Iterable findAll(Class domainType, Pageable pageable) {
+ return delegate.findAll(domainType, pageable);
+ }
+
/**
* Must be called exactly once before calling any of the other methods.
*
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java
index 1cae866dc8..51f038e1b1 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java
@@ -24,6 +24,8 @@
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.domain.Identifier;
+import org.springframework.data.relational.domain.IdentifierProcessing;
+import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.lang.NonNull;
@@ -40,13 +42,14 @@ class MapEntityRowMapper implements RowMapper> {
private final PersistentPropertyPathExtension path;
private final JdbcConverter converter;
private final Identifier identifier;
-
- private final String keyColumn;
+ private final SqlIdentifier keyColumn;
+ private final IdentifierProcessing identifierProcessing;
@NonNull
@Override
public Map.Entry mapRow(ResultSet rs, int rowNum) throws SQLException {
- Object key = rs.getObject(keyColumn);
+
+ Object key = rs.getObject(keyColumn.toColumnName(identifierProcessing));
return new HashMap.SimpleEntry<>(key, mapEntity(rs, key));
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java
index 005da10963..654ff40051 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java
@@ -20,6 +20,8 @@
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.SQL;
import org.springframework.data.relational.core.sql.Table;
+import org.springframework.data.relational.domain.IdentifierProcessing;
+import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Utility to get from path to SQL DSL elements.
@@ -33,36 +35,41 @@ class SqlContext {
private final RelationalPersistentEntity> entity;
private final Table table;
+ private final IdentifierProcessing identifierProcessing;
- SqlContext(RelationalPersistentEntity> entity) {
+ SqlContext(RelationalPersistentEntity> entity, IdentifierProcessing identifierProcessing) {
+
+ this.identifierProcessing = identifierProcessing;
this.entity = entity;
- this.table = SQL.table(entity.getTableName());
+ this.table = SQL.table(entity.getTableName().toSql(this.identifierProcessing));
}
Column getIdColumn() {
- return table.column(entity.getIdColumn());
+ return table.column(entity.getIdColumn().toSql(identifierProcessing));
}
Column getVersionColumn() {
- return table.column(entity.getRequiredVersionProperty().getColumnName());
+ return table.column(entity.getRequiredVersionProperty().getColumnName().toSql(identifierProcessing));
}
-
+
Table getTable() {
return table;
}
Table getTable(PersistentPropertyPathExtension path) {
- String tableAlias = path.getTableAlias();
- Table table = SQL.table(path.getTableName());
- return tableAlias == null ? table : table.as(tableAlias);
+ SqlIdentifier tableAlias = path.getTableAlias();
+ Table table = SQL.table(path.getTableName().toSql(identifierProcessing));
+ return tableAlias == null ? table : table.as(tableAlias.toSql(identifierProcessing));
}
Column getColumn(PersistentPropertyPathExtension path) {
- return getTable(path).column(path.getColumnName()).as(path.getColumnAlias());
+ return getTable(path).column(path.getColumnName().toSql(identifierProcessing))
+ .as(path.getColumnAlias().toSql(identifierProcessing));
}
Column getReverseColumn(PersistentPropertyPathExtension path) {
- return getTable(path).column(path.getReverseColumnName()).as(path.getReverseColumnNameAlias());
+ return getTable(path).column(path.getReverseColumnName().toSql(identifierProcessing))
+ .as(path.getReverseColumnNameAlias().toSql(identifierProcessing));
}
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java
index 3dc1bcd5e9..69f092b47b 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java
@@ -17,22 +17,19 @@
import lombok.Value;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
+import org.springframework.data.relational.core.dialect.Dialect;
+import org.springframework.data.relational.core.dialect.RenderContextFactory;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
@@ -40,6 +37,8 @@
import org.springframework.data.relational.core.sql.*;
import org.springframework.data.relational.core.sql.render.SqlRenderer;
import org.springframework.data.relational.domain.Identifier;
+import org.springframework.data.relational.domain.IdentifierProcessing;
+import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -54,16 +53,22 @@
* @author Mark Paluch
* @author Tom Hombergs
* @author Tyler Van Gorder
+ * @author Milan Milanov
*/
class SqlGenerator {
- static final String VERSION_SQL_PARAMETER_NAME = "___oldOptimisticLockingVersion";
+ static final SqlIdentifier VERSION_SQL_PARAMETER = SqlIdentifier.unquoted("___oldOptimisticLockingVersion");
+ static final SqlIdentifier ID_SQL_PARAMETER = SqlIdentifier.unquoted("id");
+ static final SqlIdentifier IDS_SQL_PARAMETER = SqlIdentifier.unquoted("ids");
+ static final SqlIdentifier ROOT_ID_PARAMETER = SqlIdentifier.unquoted("rootId");
private static final Pattern parameterPattern = Pattern.compile("\\W");
private final RelationalPersistentEntity> entity;
private final MappingContext, RelationalPersistentProperty> mappingContext;
+ private final IdentifierProcessing identifierProcessing;
private final SqlContext sqlContext;
+ private final SqlRenderer sqlRenderer;
private final Columns columns;
private final Lazy findOneSql = Lazy.of(this::createFindOneSql);
@@ -82,15 +87,18 @@ class SqlGenerator {
/**
* Create a new {@link SqlGenerator} given {@link RelationalMappingContext} and {@link RelationalPersistentEntity}.
- *
+ *
* @param mappingContext must not be {@literal null}.
* @param entity must not be {@literal null}.
+ * @param dialect must not be {@literal null}.
*/
- SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity> entity) {
+ SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity> entity, Dialect dialect) {
this.mappingContext = mappingContext;
this.entity = entity;
- this.sqlContext = new SqlContext(entity);
+ this.identifierProcessing = dialect.getIdentifierProcessing();
+ this.sqlRenderer = SqlRenderer.create(new RenderContextFactory(dialect).createRenderContext());
+ this.sqlContext = new SqlContext(entity, identifierProcessing);
this.columns = new Columns(entity, mappingContext);
}
@@ -103,7 +111,7 @@ class SqlGenerator {
* @param filterColumn the column to apply the IN-condition to.
* @return the IN condition
*/
- private static Condition getSubselectCondition(PersistentPropertyPathExtension path,
+ private Condition getSubselectCondition(PersistentPropertyPathExtension path,
Function rootCondition, Column filterColumn) {
PersistentPropertyPathExtension parentPath = path.getParentPath();
@@ -115,9 +123,10 @@ private static Condition getSubselectCondition(PersistentPropertyPathExtension p
return rootCondition.apply(filterColumn);
}
- Table subSelectTable = SQL.table(parentPath.getTableName());
- Column idColumn = subSelectTable.column(parentPath.getIdColumnName());
- Column selectFilterColumn = subSelectTable.column(parentPath.getEffectiveIdColumnName());
+ Table subSelectTable = SQL.table(parentPath.getTableName().toSql(identifierProcessing));
+ Column idColumn = subSelectTable.column(parentPath.getIdColumnName().toSql(identifierProcessing));
+ Column selectFilterColumn = subSelectTable
+ .column(parentPath.getEffectiveIdColumnName().toSql(identifierProcessing));
Condition innerCondition;
@@ -139,8 +148,8 @@ private static Condition getSubselectCondition(PersistentPropertyPathExtension p
return filterColumn.in(select);
}
- private static BindMarker getBindMarker(String columnName) {
- return SQL.bindMarker(":" + parameterPattern.matcher(columnName).replaceAll(""));
+ private BindMarker getBindMarker(SqlIdentifier columnName) {
+ return SQL.bindMarker(":" + parameterPattern.matcher(columnName.toColumnName(identifierProcessing)).replaceAll(""));
}
/**
@@ -162,6 +171,26 @@ String getFindAll() {
return findAllSql.get();
}
+ /**
+ * Returns a query for selecting all simple properties of an entity, including those for one-to-one relationships,
+ * sorted by the given parameter.
+ *
+ * @return a SQL statement. Guaranteed to be not {@code null}.
+ */
+ String getFindAll(Sort sort) {
+ return render(selectBuilder(Collections.emptyList(), sort, Pageable.unpaged()).build());
+ }
+
+ /**
+ * Returns a query for selecting all simple properties of an entity, including those for one-to-one relationships,
+ * paged and sorted by the given parameter.
+ *
+ * @return a SQL statement. Guaranteed to be not {@code null}.
+ */
+ String getFindAll(Pageable pageable) {
+ return render(selectBuilder(Collections.emptyList(), pageable.getSort(), pageable).build());
+ }
+
/**
* Returns a query for selecting all simple properties of an entity, including those for one-to-one relationships.
* Results are limited to those rows referencing some other entity using the column specified by
@@ -174,7 +203,7 @@ String getFindAll() {
* keyColumn must not be {@code null}.
* @return a SQL String.
*/
- String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyColumn, boolean ordered) {
+ String getFindAllByProperty(Identifier parentIdentifier, @Nullable SqlIdentifier keyColumn, boolean ordered) {
Assert.isTrue(keyColumn != null || !ordered,
"If the SQL statement should be ordered a keyColumn to order by must be provided.");
@@ -182,7 +211,7 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyCol
SelectBuilder.SelectWhere builder = selectBuilder( //
keyColumn == null //
? Collections.emptyList() //
- : Collections.singleton(keyColumn) //
+ : Collections.singleton(keyColumn.toSql(identifierProcessing)) //
);
Table table = getTable();
@@ -191,7 +220,9 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyCol
SelectBuilder.SelectWhereAndOr withWhereClause = builder.where(condition);
Select select = ordered //
- ? withWhereClause.orderBy(table.column(keyColumn).as(keyColumn)).build() //
+ ? withWhereClause
+ .orderBy(table.column(keyColumn.toSql(identifierProcessing)).as(keyColumn.toSql(identifierProcessing)))
+ .build() //
: withWhereClause.build();
return render(select);
@@ -200,9 +231,10 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyCol
private Condition buildConditionForBackReference(Identifier parentIdentifier, Table table) {
Condition condition = null;
- for (String backReferenceColumn : parentIdentifier.toMap().keySet()) {
+ for (SqlIdentifier backReferenceColumn : parentIdentifier.toMap().keySet()) {
- Condition newCondition = table.column(backReferenceColumn).isEqualTo(getBindMarker(backReferenceColumn));
+ Condition newCondition = table.column(backReferenceColumn.toSql(identifierProcessing))
+ .isEqualTo(getBindMarker(backReferenceColumn));
condition = condition == null ? newCondition : condition.and(newCondition);
}
@@ -214,7 +246,7 @@ private Condition buildConditionForBackReference(Identifier parentIdentifier, Ta
/**
* Create a {@code SELECT COUNT(id) FROM … WHERE :id = …} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getExists() {
return existsSql.get();
@@ -223,7 +255,7 @@ String getExists() {
/**
* Create a {@code SELECT … FROM … WHERE :id = …} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getFindOne() {
return findOneSql.get();
@@ -232,16 +264,16 @@ String getFindOne() {
/**
* Create a {@code INSERT INTO … (…) VALUES(…)} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
- String getInsert(Set additionalColumns) {
+ String getInsert(Set additionalColumns) {
return createInsertSql(additionalColumns);
}
/**
* Create a {@code UPDATE … SET …} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getUpdate() {
return updateSql.get();
@@ -250,7 +282,7 @@ String getUpdate() {
/**
* Create a {@code UPDATE … SET … WHERE ID = :id and VERSION_COLUMN = :___oldOptimisticLockingVersion } statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getUpdateWithVersion() {
return updateWithVersionSql.get();
@@ -259,7 +291,7 @@ String getUpdateWithVersion() {
/**
* Create a {@code SELECT COUNT(*) FROM …} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getCount() {
return countSql.get();
@@ -268,7 +300,7 @@ String getCount() {
/**
* Create a {@code DELETE FROM … WHERE :id = …} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getDeleteById() {
return deleteByIdSql.get();
@@ -277,7 +309,7 @@ String getDeleteById() {
/**
* Create a {@code DELETE FROM … WHERE :id = … and :___oldOptimisticLockingVersion = ...} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getDeleteByIdAndVersion() {
return deleteByIdAndVersionSql.get();
@@ -286,7 +318,7 @@ String getDeleteByIdAndVersion() {
/**
* Create a {@code DELETE FROM … WHERE :ids in (…)} statement.
*
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getDeleteByList() {
return deleteByListSql.get();
@@ -296,7 +328,7 @@ String getDeleteByList() {
* Create a {@code DELETE} query and optionally filter by {@link PersistentPropertyPath}.
*
* @param path can be {@literal null}.
- * @return
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String createDeleteAllSql(@Nullable PersistentPropertyPath path) {
@@ -315,16 +347,16 @@ String createDeleteAllSql(@Nullable PersistentPropertyPath path) {
return createDeleteByPathAndCriteria(new PersistentPropertyPathExtension(mappingContext, path),
- filterColumn -> filterColumn.isEqualTo(getBindMarker("rootId")));
+ filterColumn -> filterColumn.isEqualTo(getBindMarker(ROOT_ID_PARAMETER)));
}
private String createFindOneSql() {
- Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker("id"))) //
+ Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
.build();
return render(select);
@@ -376,11 +408,32 @@ private SelectBuilder.SelectWhere selectBuilder(Collection keyColumns) {
return (SelectBuilder.SelectWhere) baseSelect;
}
+ private SelectBuilder.SelectOrdered selectBuilder(Collection keyColumns, Sort sort, Pageable pageable) {
+ SelectBuilder.SelectWhere baseSelect = this.selectBuilder(keyColumns);
+
+ if (baseSelect instanceof SelectBuilder.SelectFromAndJoin) {
+ if (pageable.isPaged()) {
+ return ((SelectBuilder.SelectFromAndJoin) baseSelect).limitOffset(pageable.getPageSize(), pageable.getOffset())
+ .orderBy(extractOrderByFields(sort));
+ }
+ return ((SelectBuilder.SelectFromAndJoin) baseSelect).orderBy(extractOrderByFields(sort));
+
+ } else if (baseSelect instanceof SelectBuilder.SelectFromAndJoinCondition) {
+ if (pageable.isPaged()) {
+ return ((SelectBuilder.SelectFromAndJoinCondition) baseSelect)
+ .limitOffset(pageable.getPageSize(), pageable.getOffset()).orderBy(extractOrderByFields(sort));
+ }
+ return baseSelect.orderBy(extractOrderByFields(sort));
+ } else {
+ throw new RuntimeException();
+ }
+ }
+
/**
* Create a {@link Column} for {@link PersistentPropertyPathExtension}.
*
- * @param path
- * @return
+ * @param path the path to the column in question.
+ * @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
@Nullable
Column getColumn(PersistentPropertyPathExtension path) {
@@ -425,14 +478,14 @@ Join getJoin(PersistentPropertyPathExtension path) {
return new Join( //
currentTable, //
- currentTable.column(path.getReverseColumnName()), //
- parentTable.column(idDefiningParentPath.getIdColumnName()) //
+ currentTable.column(path.getReverseColumnName().toSql(identifierProcessing)), //
+ parentTable.column(idDefiningParentPath.getIdColumnName().toSql(identifierProcessing)) //
);
}
private String createFindAllInListSql() {
- Select select = selectBuilder().where(getIdColumn().in(getBindMarker("ids"))).build();
+ Select select = selectBuilder().where(getIdColumn().in(getBindMarker(IDS_SQL_PARAMETER))).build();
return render(select);
}
@@ -444,7 +497,7 @@ private String createExistsSql() {
Select select = StatementBuilder //
.select(Functions.count(getIdColumn())) //
.from(table) //
- .where(getIdColumn().isEqualTo(getBindMarker("id"))) //
+ .where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
.build();
return render(select);
@@ -462,21 +515,22 @@ private String createCountSql() {
return render(select);
}
- private String createInsertSql(Set additionalColumns) {
+ private String createInsertSql(Set additionalColumns) {
Table table = getTable();
- Set columnNamesForInsert = new LinkedHashSet<>(columns.getInsertableColumns());
+ Set columnNamesForInsert = new TreeSet<>(Comparator.comparing(id -> id.toSql(identifierProcessing)));
+ columnNamesForInsert.addAll(columns.getInsertableColumns());
columnNamesForInsert.addAll(additionalColumns);
InsertBuilder.InsertIntoColumnsAndValuesWithBuild insert = Insert.builder().into(table);
- for (String cn : columnNamesForInsert) {
- insert = insert.column(table.column(cn));
+ for (SqlIdentifier cn : columnNamesForInsert) {
+ insert = insert.column(table.column(cn.toSql(identifierProcessing)));
}
InsertBuilder.InsertValuesWithBuild insertWithValues = null;
- for (String cn : columnNamesForInsert) {
+ for (SqlIdentifier cn : columnNamesForInsert) {
insertWithValues = (insertWithValues == null ? insert : insertWithValues).values(getBindMarker(cn));
}
@@ -490,7 +544,8 @@ private String createUpdateSql() {
private String createUpdateWithVersionSql() {
Update update = createBaseUpdate() //
- .and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) //
+ .and(getVersionColumn()
+ .isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.toColumnName(identifierProcessing)))) //
.build();
return render(update);
@@ -503,7 +558,7 @@ private UpdateBuilder.UpdateWhereAndOr createBaseUpdate() {
List assignments = columns.getUpdateableColumns() //
.stream() //
.map(columnName -> Assignments.value( //
- table.column(columnName), //
+ table.column(columnName.toSql(identifierProcessing)), //
getBindMarker(columnName))) //
.collect(Collectors.toList());
@@ -520,7 +575,8 @@ private String createDeleteSql() {
private String createDeleteByIdAndVersionSql() {
Delete delete = createBaseDeleteById(getTable()) //
- .and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) //
+ .and(getVersionColumn()
+ .isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.toColumnName(identifierProcessing)))) //
.build();
return render(delete);
@@ -533,13 +589,13 @@ private DeleteBuilder.DeleteWhereAndOr createBaseDeleteById(Table table) {
private String createDeleteByPathAndCriteria(PersistentPropertyPathExtension path,
Function rootCondition) {
- Table table = SQL.table(path.getTableName());
+ Table table = SQL.table(path.getTableName().toSql(identifierProcessing));
DeleteBuilder.DeleteWhere builder = Delete.builder() //
.from(table);
Delete delete;
- Column filterColumn = table.column(path.getReverseColumnName());
+ Column filterColumn = table.column(path.getReverseColumnName().toSql(identifierProcessing));
if (path.getLength() == 1) {
@@ -561,26 +617,26 @@ private String createDeleteByListSql() {
Delete delete = Delete.builder() //
.from(table) //
- .where(getIdColumn().in(getBindMarker("ids"))) //
+ .where(getIdColumn().in(getBindMarker(IDS_SQL_PARAMETER))) //
.build();
return render(delete);
}
private String render(Select select) {
- return SqlRenderer.create().render(select);
+ return this.sqlRenderer.render(select);
}
private String render(Insert insert) {
- return SqlRenderer.create().render(insert);
+ return this.sqlRenderer.render(insert);
}
private String render(Update update) {
- return SqlRenderer.create().render(update);
+ return this.sqlRenderer.render(update);
}
private String render(Delete delete) {
- return SqlRenderer.create().render(delete);
+ return this.sqlRenderer.render(delete);
}
private Table getTable() {
@@ -595,6 +651,12 @@ private Column getVersionColumn() {
return sqlContext.getVersionColumn();
}
+ private List extractOrderByFields(Sort sort) {
+ return sort.stream()
+ .map(order -> OrderByField.from(Column.create(order.getProperty(), this.getTable()), order.getDirection()))
+ .collect(Collectors.toList());
+ }
+
/**
* Value object representing a {@code JOIN} association.
*/
@@ -609,17 +671,18 @@ static class Join {
* Value object encapsulating column name caches.
*
* @author Mark Paluch
+ * @author Jens Schauder
*/
static class Columns {
private final MappingContext, RelationalPersistentProperty> mappingContext;
- private final List columnNames = new ArrayList<>();
- private final List idColumnNames = new ArrayList<>();
- private final List nonIdColumnNames = new ArrayList<>();
- private final Set readOnlyColumnNames = new HashSet<>();
- private final Set insertableColumns;
- private final Set updateableColumns;
+ private final List columnNames = new ArrayList<>();
+ private final List idColumnNames = new ArrayList<>();
+ private final List nonIdColumnNames = new ArrayList<>();
+ private final Set readOnlyColumnNames = new HashSet<>();
+ private final Set insertableColumns;
+ private final Set updateableColumns;
Columns(RelationalPersistentEntity> entity,
MappingContext, RelationalPersistentProperty> mappingContext) {
@@ -628,12 +691,12 @@ static class Columns {
populateColumnNameCache(entity, "");
- Set insertable = new LinkedHashSet<>(nonIdColumnNames);
+ Set insertable = new LinkedHashSet<>(nonIdColumnNames);
insertable.removeAll(readOnlyColumnNames);
this.insertableColumns = Collections.unmodifiableSet(insertable);
- Set updateable = new LinkedHashSet<>(columnNames);
+ Set updateable = new LinkedHashSet<>(columnNames);
updateable.removeAll(idColumnNames);
updateable.removeAll(readOnlyColumnNames);
@@ -656,7 +719,7 @@ private void populateColumnNameCache(RelationalPersistentEntity> entity, Strin
private void initSimpleColumnName(RelationalPersistentProperty property, String prefix) {
- String columnName = prefix + property.getColumnName();
+ SqlIdentifier columnName = property.getColumnName().prefix(prefix);
columnNames.add(columnName);
@@ -684,14 +747,14 @@ private void initEmbeddedColumnNames(RelationalPersistentProperty property, Stri
/**
* @return Column names that can be used for {@code INSERT}.
*/
- Set getInsertableColumns() {
+ Set getInsertableColumns() {
return insertableColumns;
}
/**
* @return Column names that can be used for {@code UPDATE}.
*/
- Set getUpdateableColumns() {
+ Set getUpdateableColumns() {
return updateableColumns;
}
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java
index 4ea4752917..12fa253275 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java
@@ -19,6 +19,7 @@
import java.util.Map;
+import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.util.ConcurrentReferenceHashMap;
@@ -27,14 +28,25 @@
* domain type, the same generator will get returned.
*
* @author Jens Schauder
+ * @author Milan Milanov
*/
@RequiredArgsConstructor
public class SqlGeneratorSource {
private final Map, SqlGenerator> CACHE = new ConcurrentReferenceHashMap<>();
private final RelationalMappingContext context;
+ private final Dialect dialect;
+
+ /**
+ * @return the {@link Dialect} used by the created {@link SqlGenerator} instances. Guaranteed to be not
+ * {@literal null}.
+ */
+ public Dialect getDialect() {
+ return dialect;
+ }
SqlGenerator getSqlGenerator(Class> domainType) {
- return CACHE.computeIfAbsent(domainType, t -> new SqlGenerator(context, context.getRequiredPersistentEntity(t)));
+ return CACHE.computeIfAbsent(domainType,
+ t -> new SqlGenerator(context, context.getRequiredPersistentEntity(t), dialect));
}
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java
new file mode 100644
index 0000000000..30dbf931b9
--- /dev/null
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSource.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 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
+ *
+ * https://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.convert;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.data.relational.domain.IdentifierProcessing;
+import org.springframework.data.relational.domain.SqlIdentifier;
+import org.springframework.jdbc.core.namedparam.AbstractSqlParameterSource;
+
+/**
+ * Implementation of the {@link org.springframework.jdbc.core.namedparam.SqlParameterSource} interface based on
+ * {@link SqlIdentifier} instead of {@link String} for names.
+ *
+ * @author Jens Schauder
+ * @since 2.0
+ */
+class SqlIdentifierParameterSource extends AbstractSqlParameterSource {
+
+ private final IdentifierProcessing identifierProcessing;
+ private final Set identifiers = new HashSet<>();
+ private final Map namesToValues = new HashMap<>();
+
+ SqlIdentifierParameterSource(IdentifierProcessing identifierProcessing) {
+ this.identifierProcessing = identifierProcessing;
+ }
+
+ @Override
+ public boolean hasValue(String paramName) {
+ return namesToValues.containsKey(paramName);
+ }
+
+ @Override
+ public Object getValue(String paramName) throws IllegalArgumentException {
+ return namesToValues.get(paramName);
+ }
+
+ @Override
+ public String[] getParameterNames() {
+ return namesToValues.keySet().toArray(new String[0]);
+ }
+
+ Set getIdentifiers() {
+ return Collections.unmodifiableSet(identifiers);
+ }
+
+ void addValue(SqlIdentifier name, Object value) {
+ addValue(name, value, Integer.MIN_VALUE);
+ }
+
+ void addValue(SqlIdentifier identifier, Object value, int sqlType) {
+
+ identifiers.add(identifier);
+ String name = identifier.toColumnName(identifierProcessing);
+ namesToValues.put(name, value);
+ registerSqlType(name, sqlType);
+ }
+
+ void addAll(SqlIdentifierParameterSource others) {
+
+ for (SqlIdentifier identifier : others.getIdentifiers()) {
+
+ String name = identifier.toColumnName(identifierProcessing);
+ addValue(identifier, others.getValue(name), others.getSqlType(name));
+ }
+ }
+}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
index c798a6e88c..53f82dc964 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategy.java
@@ -15,17 +15,20 @@
*/
package org.springframework.data.jdbc.mybatis;
-import static java.util.Arrays.asList;
+import static java.util.Arrays.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.stream.Collectors;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.core.convert.CascadingDataAccessStrategy;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy;
@@ -34,9 +37,12 @@
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyPath;
+import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
+import org.springframework.data.relational.domain.IdentifierProcessing;
+import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.Assert;
@@ -54,6 +60,7 @@
* @author Oliver Gierke
* @author Mark Paluch
* @author Tyler Van Gorder
+ * @author Milan Milanov
*/
public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@@ -61,6 +68,7 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
private static final String VERSION_SQL_PARAMETER_NAME_OLD = "___oldOptimisticLockingVersion";
private final SqlSession sqlSession;
+ private final IdentifierProcessing identifierProcessing;
private NamespaceStrategy namespaceStrategy = NamespaceStrategy.DEFAULT_INSTANCE;
/**
@@ -68,8 +76,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
* uses a {@link DefaultDataAccessStrategy}
*/
public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingContext context,
- JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession) {
- return createCombinedAccessStrategy(context, converter, operations, sqlSession, NamespaceStrategy.DEFAULT_INSTANCE);
+ JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession, Dialect dialect) {
+ return createCombinedAccessStrategy(context, converter, operations, sqlSession, NamespaceStrategy.DEFAULT_INSTANCE,
+ dialect);
}
/**
@@ -78,19 +87,20 @@ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC
*/
public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingContext context,
JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession,
- NamespaceStrategy namespaceStrategy) {
+ NamespaceStrategy namespaceStrategy, Dialect dialect) {
// the DefaultDataAccessStrategy needs a reference to the returned DataAccessStrategy. This creates a dependency
// cycle. In order to create it, we need something that allows to defer closing the cycle until all the elements are
// created. That is the purpose of the DelegatingAccessStrategy.
DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy();
- MyBatisDataAccessStrategy myBatisDataAccessStrategy = new MyBatisDataAccessStrategy(sqlSession, context, converter);
+ MyBatisDataAccessStrategy myBatisDataAccessStrategy = new MyBatisDataAccessStrategy(sqlSession,
+ dialect.getIdentifierProcessing());
myBatisDataAccessStrategy.setNamespaceStrategy(namespaceStrategy);
CascadingDataAccessStrategy cascadingDataAccessStrategy = new CascadingDataAccessStrategy(
asList(myBatisDataAccessStrategy, delegatingDataAccessStrategy));
- SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context);
+ SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, dialect);
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( //
sqlGeneratorSource, //
context, //
@@ -109,13 +119,17 @@ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC
* Use a {@link SqlSessionTemplate} for {@link SqlSession} or a similar implementation tying the session to the proper
* transaction. Note that the resulting {@link DataAccessStrategy} only handles MyBatis. It does not include the
* functionality of the {@link DefaultDataAccessStrategy} which one normally still wants. Use
- * {@link #createCombinedAccessStrategy(RelationalMappingContext, JdbcConverter, NamedParameterJdbcOperations, SqlSession, NamespaceStrategy)}
+ * {@link #createCombinedAccessStrategy(RelationalMappingContext, JdbcConverter, NamedParameterJdbcOperations, SqlSession, NamespaceStrategy, Dialect)}
* to create such a {@link DataAccessStrategy}.
*
* @param sqlSession Must be non {@literal null}.
+ * @param identifierProcessing the {@link IdentifierProcessing} applied to {@link SqlIdentifier} instances in order to
+ * turn them into {@link String}
*/
- public MyBatisDataAccessStrategy(SqlSession sqlSession, RelationalMappingContext context, JdbcConverter converter) {
+ public MyBatisDataAccessStrategy(SqlSession sqlSession, IdentifierProcessing identifierProcessing) {
+
this.sqlSession = sqlSession;
+ this.identifierProcessing = identifierProcessing;
}
/**
@@ -135,9 +149,10 @@ public void setNamespaceStrategy(NamespaceStrategy namespaceStrategy) {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
- public Object insert(T instance, Class domainType, Map additionalParameters) {
+ public Object insert(T instance, Class domainType, Map additionalParameters) {
- MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, additionalParameters);
+ MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType,
+ convertToParameterMap(additionalParameters));
sqlSession().insert(namespace(domainType) + ".insert", myBatisContext);
return myBatisContext.getId();
@@ -150,7 +165,8 @@ public Object insert(T instance, Class domainType, Map ad
@Override
public Object insert(T instance, Class domainType, Identifier identifier) {
- MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, identifier.toMap());
+ MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType,
+ convertToParameterMap(identifier.toMap()));
sqlSession().insert(namespace(domainType) + ".insert", myBatisContext);
return myBatisContext.getId();
@@ -174,9 +190,10 @@ public boolean update(S instance, Class domainType) {
@Override
public boolean updateWithVersion(S instance, Class domainType, Number previousVersion) {
-
- return sqlSession().update(namespace(domainType) + ".updateWithVersion",
- new MyBatisContext(null, instance, domainType, Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion))) != 0;
+ String statement = namespace(domainType) + ".updateWithVersion";
+ MyBatisContext parameter = new MyBatisContext(null, instance, domainType,
+ Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion));
+ return sqlSession().update(statement, parameter) != 0;
}
/*
@@ -186,8 +203,9 @@ public boolean updateWithVersion(S instance, Class domainType, Number pre
@Override
public void delete(Object id, Class> domainType) {
- sqlSession().delete(namespace(domainType) + ".delete",
- new MyBatisContext(id, null, domainType, Collections.emptyMap()));
+ String statement = namespace(domainType) + ".delete";
+ MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
+ sqlSession().delete(statement, parameter);
}
/*
@@ -197,8 +215,10 @@ public void delete(Object id, Class> domainType) {
@Override
public void deleteWithVersion(Object id, Class domainType, Number previousVersion) {
- sqlSession().delete(namespace(domainType) + ".deleteWithVersion",
- new MyBatisContext(id, null, domainType, Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion)));
+ String statement = namespace(domainType) + ".deleteWithVersion";
+ MyBatisContext parameter = new MyBatisContext(id, null, domainType,
+ Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion));
+ sqlSession().delete(statement, parameter);
}
/*
@@ -208,10 +228,12 @@ public void deleteWithVersion(Object id, Class domainType, Number previou
@Override
public void delete(Object rootId, PersistentPropertyPath propertyPath) {
- sqlSession().delete(
- namespace(propertyPath.getBaseProperty().getOwner().getType()) + ".delete-" + toDashPath(propertyPath),
- new MyBatisContext(rootId, null, propertyPath.getRequiredLeafProperty().getTypeInformation().getType(),
- Collections.emptyMap()));
+ Class> ownerType = propertyPath.getBaseProperty().getOwner().getType();
+ String statement = namespace(ownerType) + ".delete-" + toDashPath(propertyPath);
+ Class> leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType();
+ MyBatisContext parameter = new MyBatisContext(rootId, null, leafType, Collections.emptyMap());
+
+ sqlSession().delete(statement, parameter);
}
/*
@@ -221,10 +243,9 @@ public void delete(Object rootId, PersistentPropertyPath void deleteAll(Class domainType) {
- sqlSession().delete( //
- namespace(domainType) + ".deleteAll", //
- new MyBatisContext(null, null, domainType, Collections.emptyMap()) //
- );
+ String statement = namespace(domainType) + ".deleteAll";
+ MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap());
+ sqlSession().delete(statement, parameter);
}
/*
@@ -237,10 +258,9 @@ public void deleteAll(PersistentPropertyPath prope
Class> baseType = propertyPath.getBaseProperty().getOwner().getType();
Class> leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType();
- sqlSession().delete( //
- namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath), //
- new MyBatisContext(null, null, leafType, Collections.emptyMap()) //
- );
+ String statement = namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath);
+ MyBatisContext parameter = new MyBatisContext(null, null, leafType, Collections.emptyMap());
+ sqlSession().delete(statement, parameter);
}
/*
@@ -249,8 +269,10 @@ public void deleteAll(PersistentPropertyPath prope
*/
@Override
public T findById(Object id, Class domainType) {
- return sqlSession().selectOne(namespace(domainType) + ".findById",
- new MyBatisContext(id, null, domainType, Collections.emptyMap()));
+
+ String statement = namespace(domainType) + ".findById";
+ MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
+ return sqlSession().selectOne(statement, parameter);
}
/*
@@ -259,8 +281,10 @@ public T findById(Object id, Class domainType) {
*/
@Override
public Iterable findAll(Class domainType) {
- return sqlSession().selectList(namespace(domainType) + ".findAll",
- new MyBatisContext(null, null, domainType, Collections.emptyMap()));
+
+ String statement = namespace(domainType) + ".findAll";
+ MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap());
+ return sqlSession().selectList(statement, parameter);
}
/*
@@ -299,9 +323,9 @@ public Iterable findAllByPath(Identifier identifier,
@Override
public Iterable findAllByProperty(Object rootId, RelationalPersistentProperty property) {
- return sqlSession().selectList(
- namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(),
- new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap()));
+ String statement = namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName();
+ MyBatisContext parameter = new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap());
+ return sqlSession().selectList(statement, parameter);
}
/*
@@ -311,8 +335,33 @@ public Iterable findAllByProperty(Object rootId, RelationalPersistentProp
@Override
public boolean existsById(Object id, Class domainType) {
- return sqlSession().selectOne(namespace(domainType) + ".existsById",
- new MyBatisContext(id, null, domainType, Collections.emptyMap()));
+ String statement = namespace(domainType) + ".existsById";
+ MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
+ return sqlSession().selectOne(statement, parameter);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class, org.springframework.data.domain.Sort)
+ */
+ @Override
+ public Iterable findAll(Class domainType, Sort sort) {
+ Map additionalContext = new HashMap<>();
+ additionalContext.put("sort", sort);
+ return sqlSession().selectList(namespace(domainType) + ".findAllSorted",
+ new MyBatisContext(null, null, domainType, additionalContext));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class, org.springframework.data.domain.Pageable)
+ */
+ @Override
+ public Iterable findAll(Class domainType, Pageable pageable) {
+ Map additionalContext = new HashMap<>();
+ additionalContext.put("pageable", pageable);
+ return sqlSession().selectList(namespace(domainType) + ".findAllPaged",
+ new MyBatisContext(null, null, domainType, additionalContext));
}
/*
@@ -321,8 +370,16 @@ public boolean existsById(Object id, Class domainType) {
*/
@Override
public long count(Class> domainType) {
- return sqlSession().selectOne(namespace(domainType) + ".count",
- new MyBatisContext(null, null, domainType, Collections.emptyMap()));
+
+ String statement = namespace(domainType) + ".count";
+ MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap());
+ return sqlSession().selectOne(statement, parameter);
+ }
+
+ private Map convertToParameterMap(Map additionalParameters) {
+
+ return additionalParameters.entrySet().stream() //
+ .collect(Collectors.toMap(e -> e.getKey().toSql(identifierProcessing), Map.Entry::getValue));
}
private String namespace(Class> domainType) {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java
index dfa189be38..262f3d8955 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java
@@ -34,6 +34,8 @@
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.conversion.RelationalConverter;
+import org.springframework.data.relational.core.dialect.Dialect;
+import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -71,8 +73,8 @@ public JdbcMappingContext jdbcMappingContext(Optional namingStra
}
/**
- * Creates a {@link RelationalConverter} using the configured {@link #jdbcMappingContext(Optional)}. Will get
- * {@link #jdbcCustomConversions()} applied.
+ * Creates a {@link RelationalConverter} using the configured
+ * {@link #jdbcMappingContext(Optional, JdbcCustomConversions)}. Will get {@link #jdbcCustomConversions()} applied.
*
* @see #jdbcMappingContext(Optional, JdbcCustomConversions)
* @see #jdbcCustomConversions()
@@ -125,7 +127,12 @@ public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationContext applicatio
*/
@Bean
public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter,
- RelationalMappingContext context) {
- return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, jdbcConverter, operations);
+ RelationalMappingContext context, Dialect dialect) {
+ return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, jdbcConverter, operations);
+ }
+
+ @Bean
+ Dialect dialect() {
+ return HsqlDbDialect.INSTANCE;
}
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java
index cb23cc696e..f4672688bf 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java
@@ -34,6 +34,8 @@
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.conversion.RelationalConverter;
+import org.springframework.data.relational.core.dialect.Dialect;
+import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -102,12 +104,15 @@ public JdbcCustomConversions jdbcCustomConversions() {
* @param context
* @param converter
* @param operations
+ * @param dialect
* @return
*/
@Bean
public JdbcAggregateOperations jdbcAggregateOperations(ApplicationEventPublisher publisher,
- RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations) {
- return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy(context, converter, operations));
+ RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations,
+ Dialect dialect) {
+ return new JdbcAggregateTemplate(publisher, context, converter,
+ dataAccessStrategy(context, converter, operations, dialect));
}
/**
@@ -117,11 +122,17 @@ public JdbcAggregateOperations jdbcAggregateOperations(ApplicationEventPublisher
* @param context
* @param converter
* @param operations
+ * @param dialect
* @return
*/
@Bean
public DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, JdbcConverter converter,
- NamedParameterJdbcOperations operations) {
- return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, converter, operations);
+ NamedParameterJdbcOperations operations, Dialect dialect) {
+ return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, converter, operations);
+ }
+
+ @Bean
+ public Dialect dialect() {
+ return HsqlDbDialect.INSTANCE;
}
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java
index 8406176251..c1e3a441ec 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java
@@ -22,6 +22,7 @@
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy;
+import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -43,8 +44,8 @@ public class MyBatisJdbcConfiguration extends AbstractJdbcConfiguration {
@Bean
@Override
public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter,
- RelationalMappingContext context) {
+ RelationalMappingContext context, Dialect dialect) {
- return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session);
+ return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session, dialect);
}
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
index 82493ce8be..10eb6cda22 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
@@ -28,6 +28,7 @@
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
import org.springframework.data.jdbc.repository.RowMapperMap;
import org.springframework.data.mapping.callback.EntityCallbacks;
+import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
@@ -56,6 +57,7 @@ public class JdbcRepositoryFactoryBean, S, ID extend
private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
private NamedParameterJdbcOperations operations;
private EntityCallbacks entityCallbacks;
+ private Dialect dialect;
/**
* Creates a new {@link JdbcRepositoryFactoryBean} for the given repository interface.
@@ -99,6 +101,11 @@ protected void setMappingContext(RelationalMappingContext mappingContext) {
this.mappingContext = mappingContext;
}
+ @Autowired
+ protected void setDialect(Dialect dialect) {
+ this.dialect = dialect;
+ }
+
/**
* @param dataAccessStrategy can be {@literal null}.
*/
@@ -167,7 +174,9 @@ public void afterPropertiesSet() {
this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class) //
.getIfAvailable(() -> {
- SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext);
+ Assert.state(this.dialect != null, "Dialect is required and must not be null!");
+
+ SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext, this.dialect);
return new DefaultDataAccessStrategy(sqlGeneratorSource, this.mappingContext, this.converter,
this.operations);
});
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java
index fe23c84851..66093c5d1c 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java
@@ -21,9 +21,13 @@
import java.util.Optional;
import java.util.stream.Collectors;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.util.Streamable;
import org.springframework.transaction.annotation.Transactional;
@@ -32,10 +36,11 @@
*
* @author Jens Schauder
* @author Oliver Gierke
+ * @author Milan Milanov
*/
@RequiredArgsConstructor
@Transactional(readOnly = true)
-public class SimpleJdbcRepository implements CrudRepository {
+public class SimpleJdbcRepository implements CrudRepository, PagingAndSortingRepository