diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java index 439724e3..0bc2a6e9 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java @@ -266,7 +266,7 @@ class DefaultJdbcInterpreter implements Interpreter { .getRequiredPersistentEntity(idOwningAction.getEntityType()); Object identifier = persistentEntity.getIdentifierAccessor(idOwningAction.getEntity()).getIdentifier(); - Assert.state(identifier != null, "Couldn't get obtain a required id value"); + Assert.state(identifier != null, "Couldn't obtain a required id value"); return identifier; } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java index 703ec5c5..69c11faf 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java @@ -37,12 +37,14 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.relational.core.conversion.BasicRelationalConverter; import org.springframework.data.relational.core.conversion.RelationalConverter; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension; 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.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; @@ -69,6 +71,7 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc private static final Converter, Map> ITERABLE_OF_ENTRY_TO_MAP_CONVERTER = new IterableOfEntryToMapConverter(); private final JdbcTypeFactory typeFactory; + private final IdentifierProcessing identifierProcessing = HsqlDbDialect.INSTANCE.getIdentifierProcessing(); private RelationResolver relationResolver; @@ -374,7 +377,8 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc return readEntityFrom(property, path); } - Object value = getObjectFromResultSet(path.extendBy(property).getColumnAlias()); + Object value = getObjectFromResultSet( + path.extendBy(property).getColumnAlias().toColumnName(identifierProcessing)); return readValue(value, property.getTypeInformation()); } @@ -428,7 +432,8 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc if (idProperty != null) { idValue = newContext.readFrom(idProperty); } else { - idValue = newContext.getObjectFromResultSet(path.extendBy(property).getReverseColumnNameAlias()); + idValue = newContext.getObjectFromResultSet( + path.extendBy(property).getReverseColumnNameAlias().toColumnName(identifierProcessing)); } if (idValue == null) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java index dee26310..0deff63f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/CascadingDataAccessStrategy.java @@ -24,6 +24,7 @@ import java.util.function.Function; 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; /** * Delegates each methods to the {@link DataAccessStrategy}s passed to the constructor in turn until the first that does @@ -47,7 +48,7 @@ public class CascadingDataAccessStrategy 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 collect(das -> das.insert(instance, domainType, additionalParameters)); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java index fde16ad4..dc612fc7 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DataAccessStrategy.java @@ -22,6 +22,7 @@ import org.springframework.data.jdbc.core.JdbcAggregateOperations; 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.lang.Nullable; /** @@ -46,7 +47,8 @@ public interface DataAccessStrategy extends RelationResolver { * @deprecated since 1.1, use {@link #insert(Object, Class, Identifier)} instead. */ @Deprecated - Object insert(T instance, Class domainType, Map additionalParameters); + @Nullable + Object insert(T instance, Class domainType, Map additionalParameters); /** * Inserts a the data of a single entity. Referenced entities don't get handled. @@ -76,8 +78,8 @@ public interface DataAccessStrategy extends RelationResolver { boolean update(T instance, Class 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 +89,8 @@ public interface DataAccessStrategy extends RelationResolver { * @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 +116,8 @@ public interface DataAccessStrategy extends RelationResolver { * @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); 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 298fd6ac..1860c8c9 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; @@ -36,15 +34,16 @@ 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; @@ -98,7 +97,8 @@ public class DefaultDataAccessStrategy 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) { + @Nullable + public Object insert(T instance, Class domainType, Map additionalParameters) { return insert(instance, domainType, Identifier.from(additionalParameters)); } @@ -109,11 +109,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { @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 +124,10 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +144,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +157,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +180,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +196,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +219,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { RelationalPersistentProperty referencingProperty = propertyPath.getLeafProperty(); Assert.notNull(referencingProperty, "No property found matching the PropertyPath " + propertyPath); - String format = sql(rootEntity.getType()).createDeleteByPath(propertyPath); + String delete = sql(rootEntity.getType()).createDeleteByPath(propertyPath); - HashMap 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 +268,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +300,9 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { } 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 +327,20 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +366,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +374,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { return result; } - private MapSqlParameterSource getParameterSource(@Nullable S instance, RelationalPersistentEntity persistentEntity, - String prefix, Predicate skipProperty) { + 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 +396,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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 +447,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { return null; } - return keys.get(persistentEntity.getIdColumn()); + return keys.get(persistentEntity.getIdColumn().toColumnName(getIdentifierProcessing())); } } @@ -448,55 +461,58 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy { 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) { - - JdbcValue jdbcValue = converter.writeJdbcValue( // - value, // - property.getColumnType(), // - property.getSqlType() // - ); - - parameterSource.addValue(paramName, jdbcValue.getValue(), JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType())); + private IdentifierProcessing getIdentifierProcessing() { + return sqlGeneratorSource.getDialect().getIdentifierProcessing(); } - private void addConvertedPropertyValue(MapSqlParameterSource parameterSource, String name, Object value, - Class type) { + private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, + RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) { + + addConvertedValue(parameterSource, value, name, property.getColumnType(), property.getSqlType()); + } + + 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 d98536aa..2b3cd575 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 @@ -20,6 +20,7 @@ import java.util.Map; 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; /** @@ -39,7 +40,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 +71,7 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy { return delegate.updateWithVersion(instance, domainType, nextVersion); } + /* * (non-Javadoc) * @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, org.springframework.data.mapping.PersistentPropertyPath) 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 2125a1b2..62cc7174 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 java.util.Map; 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 2a65c4b2..35673fb5 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.mapping.RelationalPersistentEnti 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 069fd457..a7ae2378 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,14 +17,7 @@ package org.springframework.data.jdbc.core.convert; 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; @@ -40,6 +33,8 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp 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; @@ -57,11 +52,15 @@ import org.springframework.util.Assert; */ 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 Columns columns; @@ -82,15 +81,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 identifierProcessing must not be {@literal null}. */ - SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity entity) { + SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity entity, + IdentifierProcessing identifierProcessing) { this.mappingContext = mappingContext; this.entity = entity; - this.sqlContext = new SqlContext(entity); + this.identifierProcessing = identifierProcessing; + this.sqlContext = new SqlContext(entity, identifierProcessing); this.columns = new Columns(entity, mappingContext); } @@ -103,7 +105,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 +117,10 @@ class SqlGenerator { 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 +142,8 @@ class SqlGenerator { 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("")); } /** @@ -174,7 +177,7 @@ class SqlGenerator { * 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 +185,7 @@ class SqlGenerator { SelectBuilder.SelectWhere builder = selectBuilder( // keyColumn == null // ? Collections.emptyList() // - : Collections.singleton(keyColumn) // + : Collections.singleton(keyColumn.toSql(identifierProcessing)) // ); Table table = getTable(); @@ -191,7 +194,9 @@ class SqlGenerator { 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 +205,10 @@ class SqlGenerator { 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 +220,7 @@ class SqlGenerator { /** * 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 +229,7 @@ class SqlGenerator { /** * 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 +238,16 @@ class SqlGenerator { /** * 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 +256,7 @@ class SqlGenerator { /** * 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 +265,7 @@ class SqlGenerator { /** * 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 +274,7 @@ class SqlGenerator { /** * 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 +283,7 @@ class SqlGenerator { /** * 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 +292,7 @@ class SqlGenerator { /** * 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 +302,7 @@ class SqlGenerator { * 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 +321,16 @@ class SqlGenerator { * Create a {@code DELETE} query and filter by {@link PersistentPropertyPath}. * * @param path must not be {@literal null}. - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String createDeleteByPath(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); @@ -379,8 +385,8 @@ class SqlGenerator { /** * 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 +431,14 @@ class SqlGenerator { 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 +450,7 @@ class SqlGenerator { 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 +468,22 @@ class SqlGenerator { 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 +497,8 @@ class SqlGenerator { 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 +511,7 @@ class SqlGenerator { List assignments = columns.getUpdateableColumns() // .stream() // .map(columnName -> Assignments.value( // - table.column(columnName), // + table.column(columnName.toSql(identifierProcessing)), // getBindMarker(columnName))) // .collect(Collectors.toList()); @@ -520,7 +528,8 @@ class SqlGenerator { 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 +542,13 @@ class SqlGenerator { 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,7 +570,7 @@ class SqlGenerator { Delete delete = Delete.builder() // .from(table) // - .where(getIdColumn().in(getBindMarker("ids"))) // + .where(getIdColumn().in(getBindMarker(IDS_SQL_PARAMETER))) // .build(); return render(delete); @@ -609,17 +618,18 @@ class SqlGenerator { * 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 +638,12 @@ class SqlGenerator { 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 +666,7 @@ class SqlGenerator { private void initSimpleColumnName(RelationalPersistentProperty property, String prefix) { - String columnName = prefix + property.getColumnName(); + SqlIdentifier columnName = property.getColumnName().prefix(prefix); columnNames.add(columnName); @@ -684,14 +694,14 @@ class SqlGenerator { /** * @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 de4aa6b4..3dae16b3 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,7 +19,9 @@ import lombok.RequiredArgsConstructor; import java.util.Map; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; +import org.springframework.data.relational.domain.IdentifierProcessing; import org.springframework.util.ConcurrentReferenceHashMap; /** @@ -33,8 +35,17 @@ 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.getIdentifierProcessing())); } } 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 00000000..30dbf931 --- /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 ea2b8104..69d134ec 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,11 +15,11 @@ */ 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; @@ -34,9 +34,12 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter; 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; @@ -61,6 +64,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 +72,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 +83,20 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { */ 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 +115,17 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { * 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 +145,10 @@ public class MyBatisDataAccessStrategy 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) { - 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 +161,8 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @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 +186,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @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 +199,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @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 +211,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @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 +224,12 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @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 +239,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @Override public 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 +254,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { 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 +265,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { */ @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 +277,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { */ @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 +319,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @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 +331,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { @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); } /* @@ -321,8 +342,16 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy { */ @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 08148fb7..983ac4c1 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.RelationResolver; 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 class AbstractJdbcConfiguration { } /** - * 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 class AbstractJdbcConfiguration { */ @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 6bd60056..7d8125b7 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.RelationResolver; 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 class JdbcConfiguration { * @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 class JdbcConfiguration { * @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 b840977f..c5cc23d5 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.context.annotation.Configuration; 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 bbaf86ea..efe66fb6 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.core.convert.SqlGeneratorSource; 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 @@ public class JdbcRepositoryFactoryBean, S, ID extend this.mappingContext = mappingContext; } + @Autowired + protected void setDialect(Dialect dialect) { + this.dialect = dialect; + } + /** * @param dataAccessStrategy can be {@literal null}. */ @@ -167,7 +174,9 @@ public class JdbcRepositoryFactoryBean, S, ID extend 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/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java index 9bfdf98e..9c531585 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java @@ -15,14 +15,11 @@ */ package org.springframework.data.jdbc.core; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.tuple; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.springframework.data.jdbc.core.PropertyPathTestingUtils.toPath; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.data.jdbc.core.PropertyPathTestingUtils.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import java.util.List; @@ -52,9 +49,8 @@ import org.springframework.data.relational.domain.Identifier; */ public class DefaultJdbcInterpreterUnitTests { + public static final SimpleSqlIdentifier BACK_REFERENCE = quoted("container"); static final long CONTAINER_ID = 23L; - static final String BACK_REFERENCE = "container"; - RelationalMappingContext context = new JdbcMappingContext(); JdbcConverter converter = new BasicJdbcConverter(context, (Identifier, path) -> null); DataAccessStrategy dataAccessStrategy = mock(DataAccessStrategy.class); @@ -156,9 +152,10 @@ public class DefaultJdbcInterpreterUnitTests { assertThat(argumentCaptor.getValue().getParts()) // .extracting("name", "value", "targetType") // - .containsOnly(tuple("root_with_list", CONTAINER_ID, Long.class), // the top level id - tuple("root_with_list_key", 3, Integer.class), // midlevel key - tuple("with_list_key", 6, Integer.class) // lowlevel key + .containsOnly(tuple(quoted("root_with_list"), CONTAINER_ID, Long.class), // the top + // level id + tuple(quoted("root_with_list_key"), 3, Integer.class), // midlevel key + tuple(quoted("with_list_key"), 6, Integer.class) // lowlevel key ); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java index b7e1e49f..f35ff0a1 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java @@ -89,6 +89,109 @@ public class JdbcAggregateTemplateIntegrationTests { @Autowired NamedParameterJdbcOperations jdbcTemplate; LegoSet legoSet = createLegoSet(); + /** + * creates an instance of {@link NoIdListChain4} with the following properties: + *
    + *
  • Each element has two children with indices 0 and 1.
  • + *
  • the xxxValue of each element is a {@literal v} followed by the indices used to navigate to the given instance. + *
  • + *
+ */ + private static NoIdListChain4 createNoIdTree() { + + NoIdListChain4 chain4 = new NoIdListChain4(); + chain4.fourValue = "v"; + + IntStream.of(0, 1).forEach(i -> { + + NoIdListChain3 c3 = new NoIdListChain3(); + c3.threeValue = chain4.fourValue + i; + chain4.chain3.add(c3); + + IntStream.of(0, 1).forEach(j -> { + + NoIdListChain2 c2 = new NoIdListChain2(); + c2.twoValue = c3.threeValue + j; + c3.chain2.add(c2); + + IntStream.of(0, 1).forEach(k -> { + + NoIdListChain1 c1 = new NoIdListChain1(); + c1.oneValue = c2.twoValue + k; + c2.chain1.add(c1); + + IntStream.of(0, 1).forEach(m -> { + + NoIdListChain0 c0 = new NoIdListChain0(); + c0.zeroValue = c1.oneValue + m; + c1.chain0.add(c0); + }); + }); + }); + }); + + return chain4; + } + + private static NoIdMapChain4 createNoIdMapTree() { + + NoIdMapChain4 chain4 = new NoIdMapChain4(); + chain4.fourValue = "v"; + + IntStream.of(0, 1).forEach(i -> { + + NoIdMapChain3 c3 = new NoIdMapChain3(); + c3.threeValue = chain4.fourValue + i; + chain4.chain3.put(asString(i), c3); + + IntStream.of(0, 1).forEach(j -> { + + NoIdMapChain2 c2 = new NoIdMapChain2(); + c2.twoValue = c3.threeValue + j; + c3.chain2.put(asString(j), c2); + + IntStream.of(0, 1).forEach(k -> { + + NoIdMapChain1 c1 = new NoIdMapChain1(); + c1.oneValue = c2.twoValue + k; + c2.chain1.put(asString(k), c1); + + IntStream.of(0, 1).forEach(it -> { + + NoIdMapChain0 c0 = new NoIdMapChain0(); + c0.zeroValue = c1.oneValue + it; + c1.chain0.put(asString(it), c0); + }); + }); + }); + }); + + return chain4; + } + + private static String asString(int i) { + return "_" + i; + } + + private static void assumeNot(String dbProfileName) { + + Assume.assumeTrue("true" + .equalsIgnoreCase(ProfileValueUtils.retrieveProfileValueSource(JdbcAggregateTemplateIntegrationTests.class) + .get("current.database.is.not." + dbProfileName))); + } + + private static LegoSet createLegoSet() { + + LegoSet entity = new LegoSet(); + entity.setName("Star Destroyer"); + + Manual manual = new Manual(); + manual.setContent("Accelerates to 99% of light speed. Destroys almost everything. See https://what-if.xkcd.com/1/"); + entity.setManual(manual); + + return entity; + } + @Test // DATAJDBC-112 public void saveAndLoadAnEntityWithReferencedEntityById() { @@ -550,50 +653,6 @@ public class JdbcAggregateTemplateIntegrationTests { }); } - /** - * creates an instance of {@link NoIdListChain4} with the following properties: - *
    - *
  • Each element has two children with indices 0 and 1.
  • - *
  • the xxxValue of each element is a {@literal v} followed by the indices used to navigate to the given instance. - *
  • - *
- */ - private static NoIdListChain4 createNoIdTree() { - - NoIdListChain4 chain4 = new NoIdListChain4(); - chain4.fourValue = "v"; - - IntStream.of(0, 1).forEach(i -> { - - NoIdListChain3 c3 = new NoIdListChain3(); - c3.threeValue = chain4.fourValue + i; - chain4.chain3.add(c3); - - IntStream.of(0, 1).forEach(j -> { - - NoIdListChain2 c2 = new NoIdListChain2(); - c2.twoValue = c3.threeValue + j; - c3.chain2.add(c2); - - IntStream.of(0, 1).forEach(k -> { - - NoIdListChain1 c1 = new NoIdListChain1(); - c1.oneValue = c2.twoValue + k; - c2.chain1.add(c1); - - IntStream.of(0, 1).forEach(m -> { - - NoIdListChain0 c0 = new NoIdListChain0(); - c0.zeroValue = c1.oneValue + m; - c1.chain0.add(c0); - }); - }); - }); - }); - - return chain4; - } - @Test // DATAJDBC-223 public void saveAndLoadLongChainOfMapsWithoutIds() { @@ -758,57 +817,10 @@ public class JdbcAggregateTemplateIntegrationTests { .withFailMessage("saving an aggregate with a future version should raise an exception"); } - private static NoIdMapChain4 createNoIdMapTree() { - - NoIdMapChain4 chain4 = new NoIdMapChain4(); - chain4.fourValue = "v"; - - IntStream.of(0, 1).forEach(i -> { - - NoIdMapChain3 c3 = new NoIdMapChain3(); - c3.threeValue = chain4.fourValue + i; - chain4.chain3.put(asString(i), c3); - - IntStream.of(0, 1).forEach(j -> { - - NoIdMapChain2 c2 = new NoIdMapChain2(); - c2.twoValue = c3.threeValue + j; - c3.chain2.put(asString(j), c2); - - IntStream.of(0, 1).forEach(k -> { - - NoIdMapChain1 c1 = new NoIdMapChain1(); - c1.oneValue = c2.twoValue + k; - c2.chain1.put(asString(k), c1); - - IntStream.of(0, 1).forEach(it -> { - - NoIdMapChain0 c0 = new NoIdMapChain0(); - c0.zeroValue = c1.oneValue + it; - c1.chain0.put(asString(it), c0); - }); - }); - }); - }); - - return chain4; - } - - private static String asString(int i) { - return "_" + i; - } - private Long count(String tableName) { return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM " + tableName, emptyMap(), Long.class); } - private static void assumeNot(String dbProfileName) { - - Assume.assumeTrue("true" - .equalsIgnoreCase(ProfileValueUtils.retrieveProfileValueSource(JdbcAggregateTemplateIntegrationTests.class) - .get("current.database.is.not." + dbProfileName))); - } - private static class ArrayOwner { @Id Long id; @@ -836,18 +848,6 @@ public class JdbcAggregateTemplateIntegrationTests { Set digits = new HashSet<>(); } - private static LegoSet createLegoSet() { - - LegoSet entity = new LegoSet(); - entity.setName("Star Destroyer"); - - Manual manual = new Manual(); - manual.setContent("Accelerates to 99% of light speed. Destroys almost everything. See https://what-if.xkcd.com/1/"); - entity.setManual(manual); - - return entity; - } - @Data static class LegoSet { @@ -1060,13 +1060,13 @@ public class JdbcAggregateTemplateIntegrationTests { @Version private long version; @Override - void setVersion(Number newVersion) { - this.version = (long) newVersion; + Number getVersion() { + return this.version; } @Override - Number getVersion() { - return this.version; + void setVersion(Number newVersion) { + this.version = (long) newVersion; } } @@ -1088,13 +1088,13 @@ public class JdbcAggregateTemplateIntegrationTests { @Version private int version; @Override - void setVersion(Number newVersion) { - this.version = (int) newVersion; + Number getVersion() { + return this.version; } @Override - Number getVersion() { - return this.version; + void setVersion(Number newVersion) { + this.version = (int) newVersion; } } @@ -1116,13 +1116,13 @@ public class JdbcAggregateTemplateIntegrationTests { @Version private short version; @Override - void setVersion(Number newVersion) { - this.version = (short) newVersion; + Number getVersion() { + return this.version; } @Override - Number getVersion() { - return this.version; + void setVersion(Number newVersion) { + this.version = (short) newVersion; } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/PersistentPropertyPathExtensionUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/PersistentPropertyPathExtensionUnitTests.java index a8f616b3..f7e6b5ae 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/PersistentPropertyPathExtensionUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/PersistentPropertyPathExtensionUnitTests.java @@ -15,9 +15,11 @@ */ package org.springframework.data.jdbc.core; +import static org.assertj.core.api.SoftAssertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; + import java.util.List; -import org.jetbrains.annotations.NotNull; import org.junit.Test; import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; @@ -28,8 +30,6 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathEx import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import static org.assertj.core.api.SoftAssertions.*; - /** * @author Jens Schauder */ @@ -99,13 +99,13 @@ public class PersistentPropertyPathExtensionUnitTests { assertSoftly(softly -> { - softly.assertThat(extPath(entity).getTableName()).isEqualTo("dummy_entity"); - softly.assertThat(extPath("second").getTableName()).isEqualTo("second"); - softly.assertThat(extPath("second.third2").getTableName()).isEqualTo("second"); - softly.assertThat(extPath("second.third2.value").getTableName()).isEqualTo("second"); - softly.assertThat(extPath("secondList.third2").getTableName()).isEqualTo("second"); - softly.assertThat(extPath("secondList.third2.value").getTableName()).isEqualTo("second"); - softly.assertThat(extPath("secondList").getTableName()).isEqualTo("second"); + softly.assertThat(extPath(entity).getTableName()).isEqualTo(quoted("dummy_entity")); + softly.assertThat(extPath("second").getTableName()).isEqualTo(quoted("second")); + softly.assertThat(extPath("second.third2").getTableName()).isEqualTo(quoted("second")); + softly.assertThat(extPath("second.third2.value").getTableName()).isEqualTo(quoted("second")); + softly.assertThat(extPath("secondList.third2").getTableName()).isEqualTo(quoted("second")); + softly.assertThat(extPath("secondList.third2.value").getTableName()).isEqualTo(quoted("second")); + softly.assertThat(extPath("secondList").getTableName()).isEqualTo(quoted("second")); }); } @@ -115,17 +115,17 @@ public class PersistentPropertyPathExtensionUnitTests { assertSoftly(softly -> { softly.assertThat(extPath(entity).getTableAlias()).isEqualTo(null); - softly.assertThat(extPath("second").getTableAlias()).isEqualTo("second"); - softly.assertThat(extPath("second.third2").getTableAlias()).isEqualTo("second"); - softly.assertThat(extPath("second.third2.value").getTableAlias()).isEqualTo("second"); - softly.assertThat(extPath("second.third").getTableAlias()).isEqualTo("second_third"); - softly.assertThat(extPath("second.third.value").getTableAlias()).isEqualTo("second_third"); - softly.assertThat(extPath("secondList.third2").getTableAlias()).isEqualTo("secondList"); - softly.assertThat(extPath("secondList.third2.value").getTableAlias()).isEqualTo("secondList"); - softly.assertThat(extPath("secondList.third").getTableAlias()).isEqualTo("secondList_third"); - softly.assertThat(extPath("secondList.third.value").getTableAlias()).isEqualTo("secondList_third"); - softly.assertThat(extPath("secondList").getTableAlias()).isEqualTo("secondList"); - softly.assertThat(extPath("second2.third").getTableAlias()).isEqualTo("secthird"); + softly.assertThat(extPath("second").getTableAlias()).isEqualTo(quoted("second")); + softly.assertThat(extPath("second.third2").getTableAlias()).isEqualTo(quoted("second")); + softly.assertThat(extPath("second.third2.value").getTableAlias()).isEqualTo(quoted("second")); + softly.assertThat(extPath("second.third").getTableAlias()).isEqualTo(quoted("second_third")); + softly.assertThat(extPath("second.third.value").getTableAlias()).isEqualTo(quoted("second_third")); + softly.assertThat(extPath("secondList.third2").getTableAlias()).isEqualTo(quoted("secondList")); + softly.assertThat(extPath("secondList.third2.value").getTableAlias()).isEqualTo(quoted("secondList")); + softly.assertThat(extPath("secondList.third").getTableAlias()).isEqualTo(quoted("secondList_third")); + softly.assertThat(extPath("secondList.third.value").getTableAlias()).isEqualTo(quoted("secondList_third")); + softly.assertThat(extPath("secondList").getTableAlias()).isEqualTo(quoted("secondList")); + softly.assertThat(extPath("second2.third").getTableAlias()).isEqualTo(quoted("secthird")); }); } @@ -134,12 +134,12 @@ public class PersistentPropertyPathExtensionUnitTests { assertSoftly(softly -> { - softly.assertThat(extPath("second.third2.value").getColumnName()).isEqualTo("thrdvalue"); - softly.assertThat(extPath("second.third.value").getColumnName()).isEqualTo("value"); - softly.assertThat(extPath("secondList.third2.value").getColumnName()).isEqualTo("thrdvalue"); - softly.assertThat(extPath("secondList.third.value").getColumnName()).isEqualTo("value"); - softly.assertThat(extPath("second2.third2.value").getColumnName()).isEqualTo("secthrdvalue"); - softly.assertThat(extPath("second2.third.value").getColumnName()).isEqualTo("value"); + softly.assertThat(extPath("second.third2.value").getColumnName()).isEqualTo(quoted("thrdvalue")); + softly.assertThat(extPath("second.third.value").getColumnName()).isEqualTo(quoted("value")); + softly.assertThat(extPath("secondList.third2.value").getColumnName()).isEqualTo(quoted("thrdvalue")); + softly.assertThat(extPath("secondList.third.value").getColumnName()).isEqualTo(quoted("value")); + softly.assertThat(extPath("second2.third2.value").getColumnName()).isEqualTo(quoted("secthrdvalue")); + softly.assertThat(extPath("second2.third.value").getColumnName()).isEqualTo(quoted("value")); }); } @@ -164,15 +164,15 @@ public class PersistentPropertyPathExtensionUnitTests { assertSoftly(softly -> { - softly.assertThat(extPath("second.third2").getReverseColumnName()).isEqualTo("dummy_entity"); - softly.assertThat(extPath("second.third").getReverseColumnName()).isEqualTo("dummy_entity"); - softly.assertThat(extPath("secondList.third2").getReverseColumnName()).isEqualTo("dummy_entity"); - softly.assertThat(extPath("secondList.third").getReverseColumnName()).isEqualTo("dummy_entity"); - softly.assertThat(extPath("second2.third2").getReverseColumnName()).isEqualTo("dummy_entity"); - softly.assertThat(extPath("second2.third").getReverseColumnName()).isEqualTo("dummy_entity"); - softly.assertThat(extPath("withId.second.third2.value").getReverseColumnName()).isEqualTo("with_id"); - softly.assertThat(extPath("withId.second.third").getReverseColumnName()).isEqualTo("with_id"); - softly.assertThat(extPath("withId.second2.third").getReverseColumnName()).isEqualTo("with_id"); + softly.assertThat(extPath("second.third2").getReverseColumnName()).isEqualTo(quoted("dummy_entity")); + softly.assertThat(extPath("second.third").getReverseColumnName()).isEqualTo(quoted("dummy_entity")); + softly.assertThat(extPath("secondList.third2").getReverseColumnName()).isEqualTo(quoted("dummy_entity")); + softly.assertThat(extPath("secondList.third").getReverseColumnName()).isEqualTo(quoted("dummy_entity")); + softly.assertThat(extPath("second2.third2").getReverseColumnName()).isEqualTo(quoted("dummy_entity")); + softly.assertThat(extPath("second2.third").getReverseColumnName()).isEqualTo(quoted("dummy_entity")); + softly.assertThat(extPath("withId.second.third2.value").getReverseColumnName()).isEqualTo(quoted("with_id")); + softly.assertThat(extPath("withId.second.third").getReverseColumnName()).isEqualTo(quoted("with_id")); + softly.assertThat(extPath("withId.second2.third").getReverseColumnName()).isEqualTo(quoted("with_id")); }); } @@ -200,12 +200,10 @@ public class PersistentPropertyPathExtensionUnitTests { }); } - @NotNull private PersistentPropertyPathExtension extPath(RelationalPersistentEntity entity) { return new PersistentPropertyPathExtension(context, entity); } - @NotNull private PersistentPropertyPathExtension extPath(String path) { return new PersistentPropertyPathExtension(context, createSimplePath(path)); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java index fa42af0c..932aaee2 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java @@ -18,6 +18,7 @@ package org.springframework.data.jdbc.core.convert; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; @@ -33,7 +34,10 @@ import org.springframework.data.annotation.Id; import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.SqlParameterSource; @@ -54,7 +58,7 @@ public class DefaultDataAccessStrategyUnitTests { JdbcOperations jdbcOperations = mock(JdbcOperations.class); RelationalMappingContext context = new JdbcMappingContext(); - HashMap additionalParameters = new HashMap<>(); + HashMap additionalParameters = new HashMap<>(); ArgumentCaptor paramSourceCaptor = ArgumentCaptor.forClass(SqlParameterSource.class); JdbcConverter converter; @@ -62,12 +66,14 @@ public class DefaultDataAccessStrategyUnitTests { @Before public void before() { + DelegatingDataAccessStrategy relationResolver = new DelegatingDataAccessStrategy(); + Dialect dialect = HsqlDbDialect.INSTANCE; converter = new BasicJdbcConverter(context, relationResolver, new JdbcCustomConversions(), new DefaultJdbcTypeFactory(jdbcOperations)); accessStrategy = new DefaultDataAccessStrategy( // - new SqlGeneratorSource(context), // + new SqlGeneratorSource(context, dialect), // context, // converter, // namedJdbcOperations); @@ -78,12 +84,12 @@ public class DefaultDataAccessStrategyUnitTests { @Test // DATAJDBC-146 public void additionalParameterForIdDoesNotLeadToDuplicateParameters() { - additionalParameters.put("id", ID_FROM_ADDITIONAL_VALUES); + additionalParameters.put(SqlIdentifier.quoted("ID"), ID_FROM_ADDITIONAL_VALUES); accessStrategy.insert(new DummyEntity(ORIGINAL_ID), DummyEntity.class, additionalParameters); - verify(namedJdbcOperations).update(eq("INSERT INTO dummy_entity (id) VALUES (:id)"), paramSourceCaptor.capture(), - any(KeyHolder.class)); + verify(namedJdbcOperations).update(eq("INSERT INTO \"DUMMY_ENTITY\" (\"ID\") VALUES (:ID)"), + paramSourceCaptor.capture(), any(KeyHolder.class)); } @Test // DATAJDBC-146 @@ -91,16 +97,16 @@ public class DefaultDataAccessStrategyUnitTests { ArgumentCaptor sqlCaptor = ArgumentCaptor.forClass(String.class); - additionalParameters.put("reference", ID_FROM_ADDITIONAL_VALUES); + additionalParameters.put(unquoted("reference"), ID_FROM_ADDITIONAL_VALUES); accessStrategy.insert(new DummyEntity(ORIGINAL_ID), DummyEntity.class, additionalParameters); verify(namedJdbcOperations).update(sqlCaptor.capture(), paramSourceCaptor.capture(), any(KeyHolder.class)); assertThat(sqlCaptor.getValue()) // - .containsSequence("INSERT INTO dummy_entity (", "id", ") VALUES (", ":id", ")") // - .containsSequence("INSERT INTO dummy_entity (", "reference", ") VALUES (", ":reference", ")"); - assertThat(paramSourceCaptor.getValue().getValue("id")).isEqualTo(ORIGINAL_ID); + .containsSequence("INSERT INTO \"DUMMY_ENTITY\" (", "\"ID\"", ") VALUES (", ":ID", ")") // + .containsSequence("INSERT INTO \"DUMMY_ENTITY\" (", "reference", ") VALUES (", ":reference", ")"); + assertThat(paramSourceCaptor.getValue().getValue("ID")).isEqualTo(ORIGINAL_ID); } @Test // DATAJDBC-235 @@ -113,7 +119,7 @@ public class DefaultDataAccessStrategyUnitTests { new DefaultJdbcTypeFactory(jdbcOperations)); DefaultDataAccessStrategy accessStrategy = new DefaultDataAccessStrategy( // - new SqlGeneratorSource(context), // + new SqlGeneratorSource(context, HsqlDbDialect.INSTANCE), // context, // converter, // namedJdbcOperations); @@ -128,8 +134,8 @@ public class DefaultDataAccessStrategyUnitTests { verify(namedJdbcOperations).update(sqlCaptor.capture(), paramSourceCaptor.capture(), any(KeyHolder.class)); - assertThat(paramSourceCaptor.getValue().getValue("id")).isEqualTo(ORIGINAL_ID); - assertThat(paramSourceCaptor.getValue().getValue("flag")).isEqualTo("T"); + assertThat(paramSourceCaptor.getValue().getValue("ID")).isEqualTo(ORIGINAL_ID); + assertThat(paramSourceCaptor.getValue().getValue("FLAG")).isEqualTo("T"); } @RequiredArgsConstructor diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java index bc47a266..83ea1154 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java @@ -60,6 +60,7 @@ 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.SqlIdentifier.SimpleSqlIdentifier; import org.springframework.data.repository.query.Param; import org.springframework.util.Assert; @@ -80,15 +81,15 @@ public class EntityRowMapperUnitTests { public static final long ID_FOR_ENTITY_NOT_REFERENCING_MAP = 23L; public static final NamingStrategy X_APPENDING_NAMINGSTRATEGY = new NamingStrategy() { @Override - public String getColumnName(RelationalPersistentProperty property) { - return NamingStrategy.super.getColumnName(property) + "x"; + public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) { + return NamingStrategy.super.getColumnName(property).suffix("x"); } }; @Test // DATAJDBC-113 public void simpleEntitiesGetProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha"); rs.next(); @@ -103,7 +104,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-181 public void namingStrategyGetsHonored() throws SQLException { - ResultSet rs = mockResultSet(asList("idx", "namex"), // + ResultSet rs = mockResultSet(asList("IDX", "NAMEX"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha"); rs.next(); @@ -118,7 +119,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-181 public void namingStrategyGetsHonoredForConstructor() throws SQLException { - ResultSet rs = mockResultSet(asList("idx", "namex"), // + ResultSet rs = mockResultSet(asList("IDX", "NAMEX"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha"); rs.next(); @@ -133,7 +134,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-427 public void simpleWithReferenceGetProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name", "trivial_id"), // + ResultSet rs = mockResultSet(asList("ID", "NAME", "TRIVIAL_ID"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 100L); rs.next(); @@ -148,7 +149,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-113 public void simpleOneToOneGetsProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name", "child_id", "child_name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME", "CHILD_ID", "CHILD_NAME"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L, "beta"); rs.next(); @@ -163,7 +164,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-286 public void immutableOneToOneGetsProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name", "child_id", "child_name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME", "CHILD_ID", "CHILD_NAME"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L, "beta"); rs.next(); @@ -178,7 +179,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-427 public void immutableWithReferenceGetsProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name", "trivial_id"), // + ResultSet rs = mockResultSet(asList("ID", "NAME", "TRIVIAL_ID"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 100L); rs.next(); @@ -194,7 +195,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-111 public void simpleEmbeddedGetsProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name", "prefix_id", "prefix_name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME", "PREFIX_ID", "PREFIX_NAME"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L, "beta"); rs.next(); @@ -209,7 +210,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-113 public void collectionReferenceGetsLoadedWithAdditionalSelect() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha"); rs.next(); @@ -224,7 +225,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-131 public void mapReferenceGetsLoadedWithAdditionalSelect() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME"), // ID_FOR_ENTITY_REFERENCING_MAP, "alpha"); rs.next(); @@ -239,7 +240,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-130 public void listReferenceGetsLoadedWithAdditionalSelect() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME"), // ID_FOR_ENTITY_REFERENCING_LIST, "alpha"); rs.next(); @@ -254,7 +255,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-252 public void doesNotTryToSetPropertiesThatAreSetViaConstructor() throws SQLException { - ResultSet rs = mockResultSet(singletonList("value"), // + ResultSet rs = mockResultSet(singletonList("VALUE"), // "value-from-resultSet"); rs.next(); @@ -267,7 +268,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-252 public void handlesMixedProperties() throws SQLException { - ResultSet rs = mockResultSet(asList("one", "two", "three"), // + ResultSet rs = mockResultSet(asList("ONE", "TWO", "THREE"), // "111", "222", "333"); rs.next(); @@ -281,7 +282,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-273 public void handlesNonSimplePropertyInConstructor() throws SQLException { - ResultSet rs = mockResultSet(singletonList("id"), // + ResultSet rs = mockResultSet(singletonList("ID"), // ID_FOR_ENTITY_REFERENCING_LIST); rs.next(); @@ -297,22 +298,22 @@ public class EntityRowMapperUnitTests { Fixture fixture = this. buildFixture() // // Id of the aggregate root and backreference to it from // the various aggregate members. - .value(4L).inColumns("four", // - "chain3_no_id_chain4", // - "chain3_chain2_no_id_chain4", // - "chain3_chain2_chain1_no_id_chain4", // - "chain3_chain2_chain1_chain0_no_id_chain4") // + .value(4L).inColumns("FOUR", // + "CHAIN3_NO_ID_CHAIN4", // + "CHAIN3_CHAIN2_NO_ID_CHAIN4", // + "CHAIN3_CHAIN2_CHAIN1_NO_ID_CHAIN4", // + "CHAIN3_CHAIN2_CHAIN1_CHAIN0_NO_ID_CHAIN4") // .endUpIn(e -> e.four) // values for the different entities - .value("four_value").inColumns("four_value").endUpIn(e -> e.fourValue) // + .value("four_value").inColumns("FOUR_VALUE").endUpIn(e -> e.fourValue) // - .value("three_value").inColumns("chain3_three_value").endUpIn(e -> e.chain3.threeValue) // + .value("three_value").inColumns("CHAIN3_THREE_VALUE").endUpIn(e -> e.chain3.threeValue) // - .value("two_value").inColumns("chain3_chain2_two_value").endUpIn(e -> e.chain3.chain2.twoValue) // + .value("two_value").inColumns("CHAIN3_CHAIN2_TWO_VALUE").endUpIn(e -> e.chain3.chain2.twoValue) // - .value("one_value").inColumns("chain3_chain2_chain1_one_value").endUpIn(e -> e.chain3.chain2.chain1.oneValue) // + .value("one_value").inColumns("CHAIN3_CHAIN2_CHAIN1_ONE_VALUE").endUpIn(e -> e.chain3.chain2.chain1.oneValue) // - .value("zero_value").inColumns("chain3_chain2_chain1_chain0_zero_value") + .value("zero_value").inColumns("CHAIN3_CHAIN2_CHAIN1_CHAIN0_ZERO_VALUE") .endUpIn(e -> e.chain3.chain2.chain1.chain0.zeroValue) // .build(); // @formatter:on @@ -329,7 +330,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-370 public void simpleNullableImmutableEmbeddedGetsProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "value"), // + ResultSet rs = mockResultSet(asList("ID", "VALUE"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "ru'Ha'"); rs.next(); @@ -345,7 +346,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-374 public void simpleEmptyImmutableEmbeddedGetsProperlyExtracted() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "value"), // + ResultSet rs = mockResultSet(asList("ID", "VALUE"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, null); rs.next(); @@ -361,7 +362,7 @@ public class EntityRowMapperUnitTests { @SneakyThrows public void simplePrimitiveImmutableEmbeddedGetsProperlyExtracted() { - ResultSet rs = mockResultSet(asList("id", "value"), // + ResultSet rs = mockResultSet(asList("ID", "VALUE"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, 24); rs.next(); @@ -377,7 +378,7 @@ public class EntityRowMapperUnitTests { @Test // DATAJDBC-370 public void simpleImmutableEmbeddedShouldBeNullIfAllOfTheEmbeddableAreNull() throws SQLException { - ResultSet rs = mockResultSet(asList("id", "value"), // + ResultSet rs = mockResultSet(asList("ID", "VALUE"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, null); rs.next(); @@ -394,7 +395,7 @@ public class EntityRowMapperUnitTests { @SneakyThrows public void embeddedShouldBeNullWhenFieldsAreNull() { - ResultSet rs = mockResultSet(asList("id", "name", "prefix_id", "prefix_name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME", "PREFIX_ID", "PREFIX_NAME"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", null, null); rs.next(); @@ -410,7 +411,7 @@ public class EntityRowMapperUnitTests { @SneakyThrows public void embeddedShouldNotBeNullWhenAtLeastOneFieldIsNotNull() { - ResultSet rs = mockResultSet(asList("id", "name", "prefix_id", "prefix_name"), // + ResultSet rs = mockResultSet(asList("ID", "NAME", "PREFIX_ID", "PREFIX_NAME"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24, null); rs.next(); @@ -426,7 +427,7 @@ public class EntityRowMapperUnitTests { @SneakyThrows public void primitiveEmbeddedShouldBeNullWhenNoValuePresent() { - ResultSet rs = mockResultSet(asList("id", "value"), // + ResultSet rs = mockResultSet(asList("ID", "VALUE"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, null); rs.next(); @@ -443,7 +444,7 @@ public class EntityRowMapperUnitTests { @SneakyThrows public void deepNestedEmbeddable() { - ResultSet rs = mockResultSet(asList("id", "level0", "level1_value", "level1_level2_value"), // + ResultSet rs = mockResultSet(asList("ID", "LEVEL0", "LEVEL1_VALUE", "LEVEL1_LEVEL2_VALUE"), // ID_FOR_ENTITY_NOT_REFERENCING_MAP, "0", "1", "2"); rs.next(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilderUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilderUnitTests.java index 0bf2879d..68a4f589 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilderUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/JdbcIdentifierBuilderUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core.convert; import static org.assertj.core.api.Assertions.*; import static org.springframework.data.jdbc.core.PropertyPathTestingUtils.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import java.util.List; import java.util.Map; @@ -46,7 +47,7 @@ public class JdbcIdentifierBuilderUnitTests { assertThat(identifier.getParts()) // .extracting("name", "value", "targetType") // .containsExactly( // - tuple("dummy_entity", "eins", UUID.class) // + tuple(quoted("dummy_entity"), "eins", UUID.class) // ); } @@ -63,8 +64,8 @@ public class JdbcIdentifierBuilderUnitTests { assertThat(identifier.getParts()) // .extracting("name", "value", "targetType") // .containsExactlyInAnyOrder( // - tuple("dummy_entity", "parent-eins", UUID.class), // - tuple("dummy_entity_key", "map-key-eins", String.class) // + tuple(quoted("dummy_entity"), "parent-eins", UUID.class), // + tuple(quoted("dummy_entity_key"), "map-key-eins", String.class) // ); } @@ -81,8 +82,8 @@ public class JdbcIdentifierBuilderUnitTests { assertThat(identifier.getParts()) // .extracting("name", "value", "targetType") // .containsExactlyInAnyOrder( // - tuple("dummy_entity", "parent-eins", UUID.class), // - tuple("dummy_entity_key", "list-index-eins", Integer.class) // + tuple(quoted("dummy_entity"), "parent-eins", UUID.class), // + tuple(quoted("dummy_entity_key"), "list-index-eins", Integer.class) // ); } @@ -96,7 +97,7 @@ public class JdbcIdentifierBuilderUnitTests { assertThat(identifier.getParts()) // .extracting("name", "value", "targetType") // .containsExactly( // - tuple("dummy_entity", "parent-eins", UUID.class) // + tuple(quoted("dummy_entity"), "parent-eins", UUID.class) // ); } @@ -110,7 +111,7 @@ public class JdbcIdentifierBuilderUnitTests { assertThat(identifier.getParts()) // .extracting("name", "value", "targetType") // .containsExactly( // - tuple("dummy_entity", "parent-eins", UUID.class) // + tuple(quoted("dummy_entity"), "parent-eins", UUID.class) // ); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java index ce65d661..3e9e9f46 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorContextBasedNamingStrategyUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.core.convert; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -25,7 +26,6 @@ import java.util.function.Consumer; import org.assertj.core.api.SoftAssertions; import org.junit.Test; import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.core.convert.SqlGenerator; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils; import org.springframework.data.mapping.PersistentPropertyPath; @@ -33,6 +33,10 @@ import org.springframework.data.relational.core.mapping.NamingStrategy; 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.IdentifierProcessing.DefaultIdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; +import org.springframework.data.relational.domain.SqlIdentifier; /** * Unit tests to verify a contextual {@link NamingStrategy} implementation that customizes using a user-centric @@ -52,8 +56,8 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests { private final NamingStrategy contextualNamingStrategy = new NamingStrategy() { @Override - public String getSchema() { - return userHandler.get(); + public SqlIdentifier getSchema() { + return unquoted(userHandler.get()); } }; @@ -87,8 +91,11 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests { String sql = sqlGenerator.createDeleteByPath(getPath("ref")); - assertThat(sql).isEqualTo( - "DELETE FROM " + user + ".referenced_entity WHERE " + user + ".referenced_entity.dummy_entity = :rootId"); + assertThat(sql).isEqualTo( // + "DELETE FROM " // + + user + ".referenced_entity WHERE " // + + user + ".referenced_entity.dummy_entity = :rootId" // + ); }); } @@ -213,7 +220,8 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests { RelationalMappingContext context = new JdbcMappingContext(namingStrategy); RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); - return new SqlGenerator(context, persistentEntity); + return new SqlGenerator(context, persistentEntity, + new DefaultIdentifierProcessing(new Quoting(""), LetterCasing.AS_IS)); } @SuppressWarnings("unused") diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java index 85bbc3ff..2309942d 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java @@ -32,6 +32,9 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathEx import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.sql.Aliased; +import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; /** * Unit tests for the {@link SqlGenerator} in a context of the {@link Embedded} annotation. @@ -50,81 +53,121 @@ public class SqlGeneratorEmbeddedUnitTests { SqlGenerator createSqlGenerator(Class type) { RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(type); - return new SqlGenerator(context, persistentEntity); + return new SqlGenerator(context, persistentEntity, + new DefaultIdentifierProcessing(new Quoting(""), LetterCasing.AS_IS)); } @Test // DATAJDBC-111 public void findOne() { final String sql = sqlGenerator.getFindOne(); - SoftAssertions softAssertions = new SoftAssertions(); - softAssertions.assertThat(sql).startsWith("SELECT").contains("dummy_entity.id1 AS id1") - .contains("dummy_entity.test AS test").contains("dummy_entity.attr1 AS attr1") - .contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") - .contains("dummy_entity.prefix2_attr2 AS prefix2_attr2").contains("dummy_entity.prefix_test AS prefix_test") - .contains("dummy_entity.prefix_attr1 AS prefix_attr1").contains("dummy_entity.prefix_attr2 AS prefix_attr2") - .contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") - .contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2").contains("WHERE dummy_entity.id1 = :id") - .doesNotContain("JOIN").doesNotContain("embeddable"); - softAssertions.assertAll(); + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(sql).startsWith("SELECT") // + .contains("dummy_entity.id1 AS id1") // + .contains("dummy_entity.test AS test") // + .contains("dummy_entity.attr1 AS attr1") // + .contains("dummy_entity.attr2 AS attr2") // + .contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") // + .contains("dummy_entity.prefix2_attr2 AS prefix2_attr2") // + .contains("dummy_entity.prefix_test AS prefix_test") // + .contains("dummy_entity.prefix_attr1 AS prefix_attr1") // + .contains("dummy_entity.prefix_attr2 AS prefix_attr2") // + .contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") // + .contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2") // + .contains("WHERE dummy_entity.id1 = :id") // + .doesNotContain("JOIN").doesNotContain("embeddable"); // + }); } @Test // DATAJDBC-111 public void findAll() { final String sql = sqlGenerator.getFindAll(); - SoftAssertions softAssertions = new SoftAssertions(); - softAssertions.assertThat(sql).startsWith("SELECT").contains("dummy_entity.id1 AS id1") - .contains("dummy_entity.test AS test").contains("dummy_entity.attr1 AS attr1") - .contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") - .contains("dummy_entity.prefix2_attr2 AS prefix2_attr2").contains("dummy_entity.prefix_test AS prefix_test") - .contains("dummy_entity.prefix_attr1 AS prefix_attr1").contains("dummy_entity.prefix_attr2 AS prefix_attr2") - .contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") - .contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2").doesNotContain("JOIN") - .doesNotContain("embeddable"); - softAssertions.assertAll(); + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(sql).startsWith("SELECT") // + .contains("dummy_entity.id1 AS id1") // + .contains("dummy_entity.test AS test") // + .contains("dummy_entity.attr1 AS attr1") // + .contains("dummy_entity.attr2 AS attr2") // + .contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") // + .contains("dummy_entity.prefix2_attr2 AS prefix2_attr2") // + .contains("dummy_entity.prefix_test AS prefix_test") // + .contains("dummy_entity.prefix_attr1 AS prefix_attr1") // + .contains("dummy_entity.prefix_attr2 AS prefix_attr2") // + .contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") // + .contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2") // + .doesNotContain("JOIN") // + .doesNotContain("embeddable"); + }); } @Test // DATAJDBC-111 public void findAllInList() { final String sql = sqlGenerator.getFindAllInList(); - SoftAssertions softAssertions = new SoftAssertions(); - softAssertions.assertThat(sql).startsWith("SELECT").contains("dummy_entity.id1 AS id1") - .contains("dummy_entity.test AS test").contains("dummy_entity.attr1 AS attr1") - .contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") - .contains("dummy_entity.prefix2_attr2 AS prefix2_attr2").contains("dummy_entity.prefix_test AS prefix_test") - .contains("dummy_entity.prefix_attr1 AS prefix_attr1").contains("dummy_entity.prefix_attr2 AS prefix_attr2") - .contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") - .contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2") - .contains("WHERE dummy_entity.id1 IN (:ids)").doesNotContain("JOIN").doesNotContain("embeddable"); - softAssertions.assertAll(); + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(sql).startsWith("SELECT") // + .contains("dummy_entity.id1 AS id1") // + .contains("dummy_entity.test AS test") // + .contains("dummy_entity.attr1 AS attr1") // + .contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") // + .contains("dummy_entity.prefix2_attr2 AS prefix2_attr2") // + .contains("dummy_entity.prefix_test AS prefix_test") // + .contains("dummy_entity.prefix_attr1 AS prefix_attr1") // + .contains("dummy_entity.prefix_attr2 AS prefix_attr2") // + .contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") // + .contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2") // + .contains("WHERE dummy_entity.id1 IN (:ids)") // + .doesNotContain("JOIN") // + .doesNotContain("embeddable"); + }); } @Test // DATAJDBC-111 public void insert() { final String sql = sqlGenerator.getInsert(emptySet()); - SoftAssertions softAssertions = new SoftAssertions(); - softAssertions.assertThat(sql).startsWith("INSERT INTO").contains("dummy_entity").contains(":test") - .contains(":attr1").contains(":attr2").contains(":prefix2_attr1").contains(":prefix2_attr2") - .contains(":prefix_test").contains(":prefix_attr1").contains(":prefix_attr2").contains(":prefix_prefix2_attr1") - .contains(":prefix_prefix2_attr2"); - softAssertions.assertAll(); + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(sql) // + .startsWith("INSERT INTO") // + .contains("dummy_entity") // + .contains(":test") // + .contains(":attr1") // + .contains(":attr2") // + .contains(":prefix2_attr1") // + .contains(":prefix2_attr2") // + .contains(":prefix_test") // + .contains(":prefix_attr1") // + .contains(":prefix_attr2") // + .contains(":prefix_prefix2_attr1") // + .contains(":prefix_prefix2_attr2"); + }); } @Test // DATAJDBC-111 public void update() { final String sql = sqlGenerator.getUpdate(); - SoftAssertions softAssertions = new SoftAssertions(); - softAssertions.assertThat(sql).startsWith("UPDATE").contains("dummy_entity").contains("test = :test") - .contains("attr1 = :attr1").contains("attr2 = :attr2").contains("prefix2_attr1 = :prefix2_attr1") - .contains("prefix2_attr2 = :prefix2_attr2").contains("prefix_test = :prefix_test") - .contains("prefix_attr1 = :prefix_attr1").contains("prefix_attr2 = :prefix_attr2") - .contains("prefix_prefix2_attr1 = :prefix_prefix2_attr1") - .contains("prefix_prefix2_attr2 = :prefix_prefix2_attr2"); - softAssertions.assertAll(); + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(sql) // + .startsWith("UPDATE") // + .contains("dummy_entity") // + .contains("test = :test") // + .contains("attr1 = :attr1") // + .contains("attr2 = :attr2") // + .contains("prefix2_attr1 = :prefix2_attr1") // + .contains("prefix2_attr2 = :prefix2_attr2") // + .contains("prefix_test = :prefix_test") // + .contains("prefix_attr1 = :prefix_attr1") // + .contains("prefix_attr2 = :prefix_attr2") // + .contains("prefix_prefix2_attr1 = :prefix_prefix2_attr1") // + .contains("prefix_prefix2_attr2 = :prefix_prefix2_attr2"); + }); } @Test // DATAJDBC-340 diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java index f361312c..eb7071e1 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorFixedNamingStrategyUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.core.convert; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import org.assertj.core.api.SoftAssertions; import org.junit.Test; @@ -23,10 +24,13 @@ import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils; import org.springframework.data.mapping.PersistentPropertyPath; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.relational.core.mapping.NamingStrategy; 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.SqlIdentifier.SimpleSqlIdentifier; /** * Unit tests the {@link SqlGenerator} with a fixed {@link NamingStrategy} implementation containing a hard wired @@ -39,31 +43,31 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { final NamingStrategy fixedCustomTablePrefixStrategy = new NamingStrategy() { @Override - public String getSchema() { - return "FixedCustomSchema"; + public SqlIdentifier getSchema() { + return unquoted("FixedCustomSchema"); } @Override - public String getTableName(Class type) { - return "FixedCustomTablePrefix_" + type.getSimpleName(); + public SqlIdentifier getTableName(Class type) { + return unquoted("FixedCustomTablePrefix_" + type.getSimpleName()); } @Override - public String getColumnName(RelationalPersistentProperty property) { - return "FixedCustomPropertyPrefix_" + property.getName(); + public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) { + return unquoted("FixedCustomPropertyPrefix_" + property.getName()); } }; final NamingStrategy upperCaseLowerCaseStrategy = new NamingStrategy() { @Override - public String getTableName(Class type) { - return type.getSimpleName().toUpperCase(); + public SqlIdentifier getTableName(Class type) { + return unquoted(type.getSimpleName().toUpperCase()); } @Override - public String getColumnName(RelationalPersistentProperty property) { - return property.getName().toLowerCase(); + public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) { + return unquoted(property.getName().toLowerCase()); } }; @@ -83,8 +87,8 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { "FixedCustomSchema.FixedCustomTablePrefix_DummyEntity.FixedCustomPropertyPrefix_id AS FixedCustomPropertyPrefix_id,") // .contains( "FixedCustomSchema.FixedCustomTablePrefix_DummyEntity.FixedCustomPropertyPrefix_name AS FixedCustomPropertyPrefix_name,") // - .contains("ref.FixedCustomPropertyPrefix_l1id AS ref_FixedCustomPropertyPrefix_l1id") // - .contains("ref.FixedCustomPropertyPrefix_content AS ref_FixedCustomPropertyPrefix_content") // + .contains("\"REF\".FixedCustomPropertyPrefix_l1id AS ref_FixedCustomPropertyPrefix_l1id") // + .contains("\"REF\".FixedCustomPropertyPrefix_content AS ref_FixedCustomPropertyPrefix_content") // .contains("FROM FixedCustomSchema.FixedCustomTablePrefix_DummyEntity"); softAssertions.assertAll(); } @@ -101,8 +105,8 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { .startsWith("SELECT") // .contains("DUMMYENTITY.id AS id,") // .contains("DUMMYENTITY.name AS name,") // - .contains("ref.l1id AS ref_l1id") // - .contains("ref.content AS ref_content") // + .contains("\"REF\".l1id AS ref_l1id") // + .contains("\"REF\".content AS ref_content") // .contains("FROM DUMMYENTITY"); softAssertions.assertAll(); } @@ -115,7 +119,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { String sql = sqlGenerator.createDeleteByPath(getPath("ref")); assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity = :rootId"); + + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" = :rootId"); } @Test // DATAJDBC-107 @@ -126,10 +130,10 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { String sql = sqlGenerator.createDeleteByPath(getPath("ref.further")); assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.referenced_entity IN " + + "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.\"REFERENCED_ENTITY\" IN " + "(SELECT FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.FixedCustomPropertyPrefix_l1id " + "FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity = :rootId)"); + + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" = :rootId)"); } @Test // DATAJDBC-107 @@ -150,7 +154,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { String sql = sqlGenerator.createDeleteAllSql(getPath("ref")); assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity IS NOT NULL"); + + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" IS NOT NULL"); } @Test // DATAJDBC-107 @@ -161,10 +165,10 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { String sql = sqlGenerator.createDeleteAllSql(getPath("ref.further")); assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.referenced_entity IN " + + "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.\"REFERENCED_ENTITY\" IN " + "(SELECT FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.FixedCustomPropertyPrefix_l1id " + "FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity IS NOT NULL)"); + + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" IS NOT NULL)"); } @Test // DATAJDBC-113 @@ -189,7 +193,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests { RelationalMappingContext context = new JdbcMappingContext(namingStrategy); RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); - return new SqlGenerator(context, persistentEntity); + return new SqlGenerator(context, persistentEntity, IdentifierProcessing.ANSI); } @SuppressWarnings("unused") diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java index 5fa155f8..c5abe3a0 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core.convert; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import java.util.Map; import java.util.Set; @@ -41,6 +42,11 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp import org.springframework.data.relational.core.sql.Aliased; import org.springframework.data.relational.core.sql.Table; import org.springframework.data.relational.domain.Identifier; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; +import org.springframework.data.relational.domain.SqlIdentifier.*; /** * Unit tests for the {@link SqlGenerator}. @@ -54,7 +60,7 @@ import org.springframework.data.relational.domain.Identifier; */ public class SqlGeneratorUnitTests { - static final Identifier BACKREF = Identifier.of("backref", "some-value", String.class); + static final Identifier BACKREF = Identifier.of(unquoted("backref"), "some-value", String.class); SqlGenerator sqlGenerator; NamingStrategy namingStrategy = new PrefixingNamingStrategy(); @@ -67,9 +73,14 @@ public class SqlGeneratorUnitTests { SqlGenerator createSqlGenerator(Class type) { + return createSqlGenerator(type, new DefaultIdentifierProcessing(new Quoting(""), LetterCasing.AS_IS)); + } + + SqlGenerator createSqlGenerator(Class type, IdentifierProcessing identifierProcessing) { + RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(type); - return new SqlGenerator(context, persistentEntity); + return new SqlGenerator(context, persistentEntity, identifierProcessing); } @Test // DATAJDBC-112 @@ -174,8 +185,8 @@ public class SqlGeneratorUnitTests { public void findAllByPropertyWithMultipartIdentifier() { // this would get called when ListParent is the element type of a Set - Identifier parentIdentifier = Identifier.of("backref", "some-value", String.class) // - .withPart("backref_key", "key-value", Object.class); + Identifier parentIdentifier = Identifier.of(unquoted("backref"), "some-value", String.class) // + .withPart(unquoted("backref_key"), "key-value", Object.class); String sql = sqlGenerator.getFindAllByProperty(parentIdentifier, null, false); assertThat(sql).contains("SELECT", // @@ -197,7 +208,7 @@ public class SqlGeneratorUnitTests { public void findAllByPropertyWithKey() { // this would get called when ListParent is th element type of a Map - String sql = sqlGenerator.getFindAllByProperty(BACKREF, "key-column", false); + String sql = sqlGenerator.getFindAllByProperty(BACKREF, unquoted("key-column"), false); assertThat(sql).isEqualTo("SELECT dummy_entity.id1 AS id1, dummy_entity.x_name AS x_name, " // + "dummy_entity.x_other AS x_other, " // @@ -219,7 +230,7 @@ public class SqlGeneratorUnitTests { public void findAllByPropertyWithKeyOrdered() { // this would get called when ListParent is th element type of a Map - String sql = sqlGenerator.getFindAllByProperty(BACKREF, "key-column", true); + String sql = sqlGenerator.getFindAllByProperty(BACKREF, unquoted("key-column"), true); assertThat(sql).isEqualTo("SELECT dummy_entity.id1 AS id1, dummy_entity.x_name AS x_name, " // + "dummy_entity.x_other AS x_other, " // @@ -235,16 +246,16 @@ public class SqlGeneratorUnitTests { @Test // DATAJDBC-219 public void updateWithVersion() { - SqlGenerator sqlGenerator = createSqlGenerator(VersionedEntity.class); + SqlGenerator sqlGenerator = createSqlGenerator(VersionedEntity.class, IdentifierProcessing.ANSI); assertThat(sqlGenerator.getUpdateWithVersion()).containsSequence( // "UPDATE", // - "versioned_entity", // + "\"VERSIONED_ENTITY\"", // "SET", // "WHERE", // - "id1 = :id", // + "\"ID1\" = :ID1", // "AND", // - "version = :___oldOptimisticLockingVersion"); + "\"X_VERSION\" = :___oldOptimisticLockingVersion"); } @Test // DATAJDBC-264 @@ -260,66 +271,69 @@ public class SqlGeneratorUnitTests { @Test // DATAJDBC-334 public void getInsertForQuotedColumnName() { - SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class); + SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class, IdentifierProcessing.ANSI); String insert = sqlGenerator.getInsert(emptySet()); - assertThat(insert) - .isEqualTo("INSERT INTO entity_with_quoted_column_name " + "(\"test_@123\") " + "VALUES (:test_123)"); + assertThat(insert).isEqualTo("INSERT INTO \"ENTITY_WITH_QUOTED_COLUMN_NAME\" " // + + "(\"TEST\"\"_@123\") " + "VALUES (:TEST_123)"); } @Test // DATAJDBC-266 public void joinForOneToOneWithoutIdIncludesTheBackReferenceOfTheOuterJoin() { - SqlGenerator sqlGenerator = createSqlGenerator(ParentOfNoIdChild.class); + SqlGenerator sqlGenerator = createSqlGenerator(ParentOfNoIdChild.class, IdentifierProcessing.ANSI); String findAll = sqlGenerator.getFindAll(); - assertThat(findAll).containsSequence("SELECT", "child.parent_of_no_id_child AS child_parent_of_no_id_child", - "FROM"); + assertThat(findAll).containsSequence("SELECT", + "\"CHILD\".\"PARENT_OF_NO_ID_CHILD\" AS \"CHILD_PARENT_OF_NO_ID_CHILD\"", "FROM"); } @Test // DATAJDBC-262 public void update() { + SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, IdentifierProcessing.ANSI); + assertThat(sqlGenerator.getUpdate()).containsSequence( // "UPDATE", // - "dummy_entity", // + "\"DUMMY_ENTITY\"", // "SET", // "WHERE", // - "id1 = :id"); + "\"ID1\" = :ID"); } @Test // DATAJDBC-324 public void readOnlyPropertyExcludedFromQuery_when_generateUpdateSql() { - final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class, IdentifierProcessing.ANSI); assertThat(sqlGenerator.getUpdate()).isEqualToIgnoringCase( // - "UPDATE entity_with_read_only_property " // - + "SET x_name = :x_name " // - + "WHERE entity_with_read_only_property.x_id = :x_id" // + "UPDATE \"ENTITY_WITH_READ_ONLY_PROPERTY\" " // + + "SET \"X_NAME\" = :X_NAME " // + + "WHERE \"ENTITY_WITH_READ_ONLY_PROPERTY\".\"X_ID\" = :X_ID" // ); } @Test // DATAJDBC-334 public void getUpdateForQuotedColumnName() { - SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class); + SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class, IdentifierProcessing.ANSI); String update = sqlGenerator.getUpdate(); - assertThat(update).isEqualTo("UPDATE entity_with_quoted_column_name " + "SET \"test_@123\" = :test_123 " - + "WHERE entity_with_quoted_column_name.\"test_@id\" = :test_id"); + assertThat(update).isEqualTo("UPDATE \"ENTITY_WITH_QUOTED_COLUMN_NAME\" " // + + "SET \"TEST\"\"_@123\" = :TEST_123 " // + + "WHERE \"ENTITY_WITH_QUOTED_COLUMN_NAME\".\"TEST\"\"_@ID\" = :TEST_ID"); } @Test // DATAJDBC-324 public void readOnlyPropertyExcludedFromQuery_when_generateInsertSql() { - final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); + final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class, IdentifierProcessing.ANSI); assertThat(sqlGenerator.getInsert(emptySet())).isEqualToIgnoringCase( // - "INSERT INTO entity_with_read_only_property (x_name) " // + "INSERT INTO \"ENTITY_WITH_READ_ONLY_PROPERTY\" (\"X_NAME\") " // + "VALUES (:x_name)" // ); } @@ -340,7 +354,7 @@ public class SqlGeneratorUnitTests { final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class); - assertThat(sqlGenerator.getFindAllByProperty(BACKREF, "key-column", true)).isEqualToIgnoringCase( // + assertThat(sqlGenerator.getFindAllByProperty(BACKREF, unquoted("key-column"), true)).isEqualToIgnoringCase( // "SELECT " // + "entity_with_read_only_property.x_id AS x_id, " // + "entity_with_read_only_property.x_name AS x_name, " // @@ -434,14 +448,15 @@ public class SqlGeneratorUnitTests { @Test // DATAJDBC-340 public void joinForSimpleReference() { + SqlGenerator.Join join = generateJoin("ref", DummyEntity.class); + SoftAssertions.assertSoftly(softly -> { - SqlGenerator.Join join = generateJoin("ref", DummyEntity.class); - softly.assertThat(join.getJoinTable().getName()).isEqualTo("referenced_entity"); + softly.assertThat(join.getJoinTable().getName()).isEqualTo("\"REFERENCED_ENTITY\""); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable()); - softly.assertThat(join.getJoinColumn().getName()).isEqualTo("dummy_entity"); - softly.assertThat(join.getParentId().getName()).isEqualTo("id1"); - softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("dummy_entity"); + softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"DUMMY_ENTITY\""); + softly.assertThat(join.getParentId().getName()).isEqualTo("\"ID1\""); + softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"DUMMY_ENTITY\""); }); } @@ -465,37 +480,39 @@ public class SqlGeneratorUnitTests { @Test // DATAJDBC-340 public void joinForSecondLevelReference() { + SqlGenerator.Join join = generateJoin("ref.further", DummyEntity.class); + SoftAssertions.assertSoftly(softly -> { - SqlGenerator.Join join = generateJoin("ref.further", DummyEntity.class); - softly.assertThat(join.getJoinTable().getName()).isEqualTo("second_level_referenced_entity"); + softly.assertThat(join.getJoinTable().getName()).isEqualTo("\"SECOND_LEVEL_REFERENCED_ENTITY\""); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable()); - softly.assertThat(join.getJoinColumn().getName()).isEqualTo("referenced_entity"); - softly.assertThat(join.getParentId().getName()).isEqualTo("x_l1id"); - softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("referenced_entity"); + softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"REFERENCED_ENTITY\""); + softly.assertThat(join.getParentId().getName()).isEqualTo("\"X_L1ID\""); + softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"REFERENCED_ENTITY\""); }); } @Test // DATAJDBC-340 public void joinForOneToOneWithoutId() { + SqlGenerator.Join join = generateJoin("child", ParentOfNoIdChild.class); + Table joinTable = join.getJoinTable(); + SoftAssertions.assertSoftly(softly -> { - SqlGenerator.Join join = generateJoin("child", ParentOfNoIdChild.class); - Table joinTable = join.getJoinTable(); - softly.assertThat(joinTable.getName()).isEqualTo("no_id_child"); + softly.assertThat(joinTable.getName()).isEqualTo("\"NO_ID_CHILD\""); softly.assertThat(joinTable).isInstanceOf(Aliased.class); - softly.assertThat(((Aliased) joinTable).getAlias()).isEqualTo("child"); + softly.assertThat(((Aliased) joinTable).getAlias()).isEqualTo("\"CHILD\""); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(joinTable); - softly.assertThat(join.getJoinColumn().getName()).isEqualTo("parent_of_no_id_child"); - softly.assertThat(join.getParentId().getName()).isEqualTo("x_id"); - softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("parent_of_no_id_child"); + softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"PARENT_OF_NO_ID_CHILD\""); + softly.assertThat(join.getParentId().getName()).isEqualTo("\"X_ID\""); + softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"PARENT_OF_NO_ID_CHILD\""); }); } private SqlGenerator.Join generateJoin(String path, Class type) { - return createSqlGenerator(type) + return createSqlGenerator(type, IdentifierProcessing.ANSI) .getJoin(new PersistentPropertyPathExtension(context, PropertyPathTestingUtils.toPath(path, type, context))); } @@ -504,7 +521,7 @@ public class SqlGeneratorUnitTests { assertThat(generatedColumn("id", DummyEntity.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) - .containsExactly("id1", "dummy_entity", null, "id1"); + .containsExactly("\"ID1\"", "\"DUMMY_ENTITY\"", null, "\"ID1\""); } @Test // DATAJDBC-340 @@ -512,7 +529,7 @@ public class SqlGeneratorUnitTests { assertThat(generatedColumn("ref.l1id", DummyEntity.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) // - .containsExactly("x_l1id", "referenced_entity", "ref", "ref_x_l1id"); + .containsExactly("\"X_L1ID\"", "\"REFERENCED_ENTITY\"", "\"REF\"", "\"REF_X_L1ID\""); } @Test // DATAJDBC-340 @@ -526,7 +543,8 @@ public class SqlGeneratorUnitTests { assertThat(generatedColumn("child", ParentOfNoIdChild.class)) // .extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) // - .containsExactly("parent_of_no_id_child", "no_id_child", "child", "child_parent_of_no_id_child"); + .containsExactly("\"PARENT_OF_NO_ID_CHILD\"", "\"NO_ID_CHILD\"", "\"CHILD\"", + "\"CHILD_PARENT_OF_NO_ID_CHILD\""); } private String getAlias(Object maybeAliased) { @@ -539,7 +557,7 @@ public class SqlGeneratorUnitTests { private org.springframework.data.relational.core.sql.Column generatedColumn(String path, Class type) { - return createSqlGenerator(type) + return createSqlGenerator(type, IdentifierProcessing.ANSI) .getColumn(new PersistentPropertyPathExtension(context, PropertyPathTestingUtils.toPath(path, type, context))); } @@ -599,8 +617,8 @@ public class SqlGeneratorUnitTests { private static class PrefixingNamingStrategy implements NamingStrategy { @Override - public String getColumnName(RelationalPersistentProperty property) { - return "x_" + NamingStrategy.super.getColumnName(property); + public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) { + return NamingStrategy.super.getColumnName(property).prefix("x_"); } } @@ -621,8 +639,10 @@ public class SqlGeneratorUnitTests { static class EntityWithQuotedColumnName { - @Id @Column("\"test_@id\"") Long id; - @Column("\"test_@123\"") String name; + // these column names behave like single double quote in the name since the get quoted and then doubling the double + // quote escapes it. + @Id @Column("test\"\"_@id") Long id; + @Column("test\"\"_@123") String name; } @SuppressWarnings("unused") diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java new file mode 100644 index 00000000..f4aaed9b --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java @@ -0,0 +1,118 @@ +/* + * 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 org.assertj.core.api.SoftAssertions; +import org.junit.Test; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.SqlIdentifier; + +/** + * Tests for {@link SqlIdentifierParameterSource}. + * + * @author Jens Schauder + */ +public class SqlIdentifierParameterSourceUnitTests { + + private IdentifierProcessing identifierProcessing = IdentifierProcessing.ANSI; + + @Test // DATAJDBC-386 + public void empty() { + + SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing); + + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(parameters.getParameterNames()).isEmpty(); + softly.assertThat(parameters.getValue("blah")).isNull(); + softly.assertThat(parameters.hasValue("blah")).isFalse(); + softly.assertThat(parameters.getSqlType("blah")).isEqualTo(Integer.MIN_VALUE); + }); + } + + @Test // DATAJDBC-386 + public void addSingleValue() { + + SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing); + + parameters.addValue(SqlIdentifier.unquoted("key"), 23); + + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(parameters.getParameterNames()).isEqualTo(new String[] { "key" }); + softly.assertThat(parameters.getValue("key")).isEqualTo(23); + softly.assertThat(parameters.hasValue("key")).isTrue(); + + softly.assertThat(parameters.getValue("blah")).isNull(); + softly.assertThat(parameters.hasValue("blah")).isFalse(); + softly.assertThat(parameters.getSqlType("blah")).isEqualTo(Integer.MIN_VALUE); + }); + } + + @Test // DATAJDBC-386 + public void addSingleValueWithType() { + + SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing); + + parameters.addValue(SqlIdentifier.unquoted("key"), 23, 42); + + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(parameters.getParameterNames()).isEqualTo(new String[] { "key" }); + softly.assertThat(parameters.getValue("key")).isEqualTo(23); + softly.assertThat(parameters.hasValue("key")).isTrue(); + softly.assertThat(parameters.getSqlType("key")).isEqualTo(42); + + softly.assertThat(parameters.getValue("blah")).isNull(); + softly.assertThat(parameters.hasValue("blah")).isFalse(); + softly.assertThat(parameters.getSqlType("blah")).isEqualTo(Integer.MIN_VALUE); + }); + } + + @Test // DATAJDBC-386 + public void addOtherDatabaseObjectIdentifierParameterSource() { + + SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing); + parameters.addValue(SqlIdentifier.unquoted("key1"), 111, 11); + parameters.addValue(SqlIdentifier.unquoted("key2"), 111); + + SqlIdentifierParameterSource parameters2 = new SqlIdentifierParameterSource(identifierProcessing); + parameters2.addValue(SqlIdentifier.unquoted("key2"), 222, 22); + parameters2.addValue(SqlIdentifier.unquoted("key3"), 222); + + parameters.addAll(parameters2); + + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(parameters.getParameterNames()).isEqualTo(new String[] { "key1", "key2", "key3" }); + softly.assertThat(parameters.getValue("key1")).isEqualTo(111); + softly.assertThat(parameters.hasValue("key1")).isTrue(); + softly.assertThat(parameters.getSqlType("key1")).isEqualTo(11); + + softly.assertThat(parameters.getValue("key2")).isEqualTo(222); + softly.assertThat(parameters.hasValue("key2")).isTrue(); + softly.assertThat(parameters.getSqlType("key2")).isEqualTo(22); + + softly.assertThat(parameters.getValue("key3")).isEqualTo(222); + softly.assertThat(parameters.hasValue("key3")).isTrue(); + softly.assertThat(parameters.getSqlType("key3")).isEqualTo(Integer.MIN_VALUE); + + softly.assertThat(parameters.getValue("blah")).isNull(); + softly.assertThat(parameters.hasValue("blah")).isFalse(); + softly.assertThat(parameters.getSqlType("blah")).isEqualTo(Integer.MIN_VALUE); + }); + } +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java index d3eefe23..05816deb 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jdbc.core.mapping; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import lombok.Data; @@ -27,7 +28,6 @@ import java.util.UUID; import org.assertj.core.api.SoftAssertions; import org.junit.Test; - import org.springframework.data.annotation.Id; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.relational.core.mapping.BasicRelationalPersistentProperty; @@ -89,9 +89,9 @@ public class BasicJdbcPersistentPropertyUnitTests { RelationalPersistentEntity entity = context.getRequiredPersistentEntity(DummyEntity.class); - assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo("dummy_name"); + assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo(quoted("dummy_name")); assertThat(entity.getRequiredPersistentProperty("localDateTime").getColumnName()) - .isEqualTo("dummy_last_updated_at"); + .isEqualTo(quoted("dummy_last_updated_at")); } @Test // DATAJDBC-218 @@ -101,8 +101,8 @@ public class BasicJdbcPersistentPropertyUnitTests { .getRequiredPersistentEntity(DummyEntity.class) // .getRequiredPersistentProperty("someList"); - assertThat(listProperty.getReverseColumnName()).isEqualTo("dummy_column_name"); - assertThat(listProperty.getKeyColumn()).isEqualTo("dummy_key_column_name"); + assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("dummy_column_name")); + assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("dummy_key_column_name")); } @Test // DATAJDBC-221 @@ -125,8 +125,8 @@ public class BasicJdbcPersistentPropertyUnitTests { .getRequiredPersistentEntity(WithCollections.class) // .getRequiredPersistentProperty("someList"); - assertThat(listProperty.getKeyColumn()).isEqualTo("some_key"); - assertThat(listProperty.getReverseColumnName()).isEqualTo("some_value"); + assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("some_key")); + assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("some_value")); } @Test // DATAJDBC-331 @@ -136,8 +136,8 @@ public class BasicJdbcPersistentPropertyUnitTests { .getRequiredPersistentEntity(WithCollections.class) // .getRequiredPersistentProperty("overrideList"); - assertThat(listProperty.getKeyColumn()).isEqualTo("override_key"); - assertThat(listProperty.getReverseColumnName()).isEqualTo("override_id"); + assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("override_key")); + assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("override_id")); } private void checkTargetType(SoftAssertions softly, RelationalPersistentEntity persistentEntity, @@ -148,6 +148,11 @@ public class BasicJdbcPersistentPropertyUnitTests { softly.assertThat(property.getColumnType()).describedAs(propertyName).isEqualTo(expected); } + @SuppressWarnings("unused") + private enum SomeEnum { + ALPHA + } + @Data @SuppressWarnings("unused") private static class DummyEntity { @@ -187,9 +192,4 @@ public class BasicJdbcPersistentPropertyUnitTests { @Column(value = "some_value", keyColumn = "some_key") @MappedCollection(idColumn = "override_id", keyColumn = "override_key") List overrideList; } - - @SuppressWarnings("unused") - private enum SomeEnum { - ALPHA - } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java index bdb79273..cad4b3d2 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mapping/model/NamingStrategyUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.mapping.model; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import lombok.Data; @@ -27,6 +28,7 @@ import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; +import org.springframework.data.relational.domain.SqlIdentifier; /** * Unit tests for the default {@link NamingStrategy}. @@ -44,42 +46,42 @@ public class NamingStrategyUnitTests { @Test // DATAJDBC-184 public void getTableName() { - assertThat(target.getTableName(persistentEntity.getType())).isEqualTo("dummy_entity"); + assertThat(target.getTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity")); } @Test // DATAJDBC-184 public void getColumnName() { assertThat(target.getColumnName(persistentEntity.getPersistentProperty("id"))) // - .isEqualTo("id"); + .isEqualTo(quoted("id")); assertThat(target.getColumnName(persistentEntity.getPersistentProperty("createdAt"))) // - .isEqualTo("created_at"); + .isEqualTo(quoted("created_at")); assertThat(target.getColumnName(persistentEntity.getPersistentProperty("dummySubEntities"))) // - .isEqualTo("dummy_sub_entities"); + .isEqualTo(quoted("dummy_sub_entities")); } @Test // DATAJDBC-184 public void getReverseColumnName() { assertThat(target.getReverseColumnName(persistentEntity.getPersistentProperty("dummySubEntities"))) - .isEqualTo("dummy_entity"); + .isEqualTo(quoted("dummy_entity")); } @Test // DATAJDBC-184 public void getKeyColumn() { assertThat(target.getKeyColumn(persistentEntity.getPersistentProperty("dummySubEntities"))) // - .isEqualTo("dummy_entity_key"); + .isEqualTo(quoted("dummy_entity_key")); } @Test // DATAJDBC-184 public void getSchema() { - assertThat(target.getSchema()).isEmpty(); + assertThat(target.getSchema()).isEqualTo(SqlIdentifier.EMPTY); } @Test // DATAJDBC-184 public void getQualifiedTableName() { - assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo("dummy_entity"); + assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity")); } @Data diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java index 2de583ad..be8c18e4 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisCustomizingNamespaceHsqlIntegrationTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.jdbc.mybatis; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import junit.framework.AssertionFailedError; @@ -39,6 +39,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; @@ -79,6 +80,8 @@ public class MyBatisCustomizingNamespaceHsqlIntegrationTests { assertThat(reloaded.name).isEqualTo("name " + saved.id); } + interface DummyEntityRepository extends CrudRepository {} + @org.springframework.context.annotation.Configuration @Import(TestConfiguration.class) @EnableJdbcRepositories(considerNestedRepositories = true) @@ -116,8 +119,9 @@ public class MyBatisCustomizingNamespaceHsqlIntegrationTests { RelationalMappingContext context = new JdbcMappingContext(); JdbcConverter converter = new BasicJdbcConverter(context, (Identifier, path) -> null); - - MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession, context, converter); + + MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession, + HsqlDbDialect.INSTANCE.getIdentifierProcessing()); strategy.setNamespaceStrategy(new NamespaceStrategy() { @Override @@ -129,6 +133,4 @@ public class MyBatisCustomizingNamespaceHsqlIntegrationTests { return strategy; } } - - interface DummyEntityRepository extends CrudRepository {} } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategyUnitTests.java index 2c048f63..aa729cc8 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisDataAccessStrategyUnitTests.java @@ -15,18 +15,12 @@ */ package org.springframework.data.jdbc.mybatis; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static java.util.Arrays.*; +import static java.util.Collections.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import java.util.Collections; @@ -44,6 +38,7 @@ import org.springframework.data.mapping.PersistentPropertyPath; 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; /** * Unit tests for the {@link MyBatisDataAccessStrategy}, mainly ensuring that the correct statements get's looked up. @@ -60,7 +55,7 @@ public class MyBatisDataAccessStrategyUnitTests { SqlSession session = mock(SqlSession.class); ArgumentCaptor captor = ArgumentCaptor.forClass(MyBatisContext.class); - MyBatisDataAccessStrategy accessStrategy = new MyBatisDataAccessStrategy(session, context, converter); + MyBatisDataAccessStrategy accessStrategy = new MyBatisDataAccessStrategy(session, IdentifierProcessing.ANSI); PersistentPropertyPath path = PropertyPathTestingUtils.toPath("one.two", DummyEntity.class, context); @@ -74,7 +69,7 @@ public class MyBatisDataAccessStrategyUnitTests { @Test // DATAJDBC-123 public void insert() { - accessStrategy.insert("x", String.class, Collections.singletonMap("key", "value")); + accessStrategy.insert("x", String.class, Collections.singletonMap(unquoted("key"), "value")); verify(session).insert(eq("java.lang.StringMapper.insert"), captor.capture()); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java index 2014ac18..a6f7d884 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisHsqlIntegrationTests.java @@ -35,6 +35,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -57,6 +58,36 @@ import org.springframework.transaction.annotation.Transactional; @Transactional public class MyBatisHsqlIntegrationTests { + @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); + @Rule public SpringMethodRule methodRule = new SpringMethodRule(); + @Autowired SqlSessionFactory sqlSessionFactory; + @Autowired DummyEntityRepository repository; + + @Test // DATAJDBC-123 + public void mybatisSelfTest() { + + SqlSession session = sqlSessionFactory.openSession(); + + session.selectList("org.springframework.data.jdbc.mybatis.DummyEntityMapper.findById"); + } + + @Test // DATAJDBC-123 + public void myBatisGetsUsedForInsertAndSelect() { + + DummyEntity entity = new DummyEntity(null, "some name"); + DummyEntity saved = repository.save(entity); + + assertThat(saved.id).isNotNull(); + + DummyEntity reloaded = repository.findById(saved.id).orElseThrow(AssertionFailedError::new); + + assertThat(reloaded).isNotNull().extracting(e -> e.id, e -> e.name); + } + + interface DummyEntityRepository extends CrudRepository { + + } + @org.springframework.context.annotation.Configuration @Import(TestConfiguration.class) @EnableJdbcRepositories(considerNestedRepositories = true) @@ -94,38 +125,7 @@ public class MyBatisHsqlIntegrationTests { SqlSession sqlSession, EmbeddedDatabase db) { return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, converter, - new NamedParameterJdbcTemplate(db), sqlSession); + new NamedParameterJdbcTemplate(db), sqlSession, HsqlDbDialect.INSTANCE); } } - - @ClassRule public static final SpringClassRule classRule = new SpringClassRule(); - @Rule public SpringMethodRule methodRule = new SpringMethodRule(); - - @Autowired SqlSessionFactory sqlSessionFactory; - @Autowired DummyEntityRepository repository; - - @Test // DATAJDBC-123 - public void mybatisSelfTest() { - - SqlSession session = sqlSessionFactory.openSession(); - - session.selectList("org.springframework.data.jdbc.mybatis.DummyEntityMapper.findById"); - } - - @Test // DATAJDBC-123 - public void myBatisGetsUsedForInsertAndSelect() { - - DummyEntity entity = new DummyEntity(null, "some name"); - DummyEntity saved = repository.save(entity); - - assertThat(saved.id).isNotNull(); - - DummyEntity reloaded = repository.findById(saved.id).orElseThrow(AssertionFailedError::new); - - assertThat(reloaded).isNotNull().extracting(e -> e.id, e -> e.name); - } - - interface DummyEntityRepository extends CrudRepository { - - } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java index 12a77ef4..e50f6e2d 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.repository; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import lombok.Data; import lombok.Value; @@ -27,7 +28,6 @@ import java.util.concurrent.atomic.AtomicLong; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -39,6 +39,7 @@ import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.test.context.ContextConfiguration; @@ -166,8 +167,8 @@ public class JdbcRepositoryIdGenerationIntegrationTests { return new NamingStrategy() { @Override - public String getTableName(Class type) { - return type.getSimpleName().toUpperCase(); + public SqlIdentifier getTableName(Class type) { + return unquoted(type.getSimpleName().toUpperCase()); } }; } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java index dac68792..1329178c 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java @@ -45,6 +45,7 @@ import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.event.AfterDeleteEvent; import org.springframework.data.relational.core.mapping.event.AfterLoadEvent; @@ -82,7 +83,7 @@ public class SimpleJdbcRepositoryEventsUnitTests { DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy(); JdbcConverter converter = new BasicJdbcConverter(context, delegatingDataAccessStrategy, new JdbcCustomConversions(), new DefaultJdbcTypeFactory(operations.getJdbcOperations())); - SqlGeneratorSource generatorSource = new SqlGeneratorSource(context); + SqlGeneratorSource generatorSource = new SqlGeneratorSource(context, HsqlDbDialect.INSTANCE); this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations)); delegatingDataAccessStrategy.setDelegate(dataAccessStrategy); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditingHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditingHsqlIntegrationTests.java index 35283996..3f5fd83f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditingHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditingHsqlIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.repository.config; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import lombok.Data; @@ -44,6 +45,7 @@ import org.springframework.data.domain.AuditorAware; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback; import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.springframework.test.context.ActiveProfiles; @@ -271,8 +273,8 @@ public class EnableJdbcAuditingHsqlIntegrationTests { return new NamingStrategy() { - public String getTableName(@NotNull Class type) { - return "DummyEntity"; + public SqlIdentifier getTableName(@NotNull Class type) { + return unquoted("DummyEntity"); } }; } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java index 45aa3c42..13a0ab6d 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositoriesIntegrationTests.java @@ -40,9 +40,9 @@ import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositoriesIntegrationTests.TestConfiguration; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.CrudRepository; -import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -69,7 +69,6 @@ public class EnableJdbcRepositoriesIntegrationTests { "dataAccessStrategy"); public static final RowMapper DUMMY_ENTITY_ROW_MAPPER = mock(RowMapper.class); public static final RowMapper STRING_ROW_MAPPER = mock(RowMapper.class); - public static final ResultSetExtractor INTEGER_RESULT_SET_EXTRACTOR = mock(ResultSetExtractor.class); @Autowired JdbcRepositoryFactoryBean factoryBean; @Autowired DummyRepository repository; @@ -153,8 +152,8 @@ public class EnableJdbcRepositoriesIntegrationTests { @Bean("qualifierDataAccessStrategy") DataAccessStrategy defaultDataAccessStrategy( @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, - RelationalMappingContext context, JdbcConverter converter) { - return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, converter, template); + RelationalMappingContext context, JdbcConverter converter, Dialect dialect) { + return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, converter, template); } } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java index f479ac15..43403b5c 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java @@ -37,6 +37,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.QueryMappingConfiguration; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -60,6 +61,8 @@ public class JdbcRepositoryFactoryBeanUnitTests { @Mock DataAccessStrategy dataAccessStrategy; @Mock ApplicationEventPublisher publisher; @Mock(answer = Answers.RETURNS_DEEP_STUBS) ListableBeanFactory beanFactory; + @Mock + Dialect dialect; RelationalMappingContext mappingContext; @@ -86,6 +89,7 @@ public class JdbcRepositoryFactoryBeanUnitTests { factoryBean.setConverter(new BasicJdbcConverter(mappingContext, dataAccessStrategy)); factoryBean.setApplicationEventPublisher(publisher); factoryBean.setBeanFactory(beanFactory); + factoryBean.setDialect(dialect); factoryBean.afterPropertiesSet(); assertThat(factoryBean.getObject()).isNotNull(); @@ -113,6 +117,7 @@ public class JdbcRepositoryFactoryBeanUnitTests { factoryBean.setConverter(new BasicJdbcConverter(mappingContext, dataAccessStrategy)); factoryBean.setApplicationEventPublisher(publisher); factoryBean.setBeanFactory(beanFactory); + factoryBean.setDialect(dialect); factoryBean.afterPropertiesSet(); assertThat(factoryBean.getObject()).isNotNull(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java index 9d4b7137..3be5ef41 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceConfiguration.java @@ -21,6 +21,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @@ -47,4 +49,9 @@ class HsqlDataSourceConfiguration { .addScript(TestUtils.createScriptName(context, "hsql")) // .build(); } + + @Bean + Dialect dialect() { + return HsqlDbDialect.INSTANCE; + } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java index 8a0b8a70..385c0a56 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java @@ -22,8 +22,11 @@ import javax.script.ScriptException; import javax.sql.DataSource; import org.mariadb.jdbc.MariaDbDataSource; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.MariaDbDialect; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.jdbc.ext.ScriptUtils; @@ -48,7 +51,9 @@ class MariaDBDataSourceConfiguration extends DataSourceConfiguration { */ @Override protected DataSource createDataSource() { + try { + MariaDbDataSource dataSource = new MariaDbDataSource(); dataSource.setUrl(MARIADB_CONTAINER.getJdbcUrl()); dataSource.setUser(MARIADB_CONTAINER.getUsername()); @@ -59,6 +64,11 @@ class MariaDBDataSourceConfiguration extends DataSourceConfiguration { } } + @Bean + Dialect dialect() { + return MariaDbDialect.INSTANCE; + } + @PostConstruct public void initDatabase() throws SQLException, ScriptException { ScriptUtils.executeSqlScript(createDataSource().getConnection(), null, "DROP DATABASE test;CREATE DATABASE test;"); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java index 94ce8ea4..f62872ba 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java @@ -21,8 +21,11 @@ import javax.annotation.PostConstruct; import javax.script.ScriptException; import javax.sql.DataSource; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.MySqlDialect; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.jdbc.ext.ScriptUtils; @@ -62,6 +65,11 @@ class MySqlDataSourceConfiguration extends DataSourceConfiguration { return dataSource; } + @Bean + Dialect dialect() { + return MySqlDialect.INSTANCE; + } + @PostConstruct public void initDatabase() throws SQLException, ScriptException { ScriptUtils.executeSqlScript(createDataSource().getConnection(), null, "DROP DATABASE test;CREATE DATABASE test;"); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/PostgresDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/PostgresDataSourceConfiguration.java index f9b4cb71..46f238af 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/PostgresDataSourceConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/PostgresDataSourceConfiguration.java @@ -18,8 +18,11 @@ package org.springframework.data.jdbc.testing; import javax.sql.DataSource; import org.postgresql.ds.PGSimpleDataSource; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.PostgresDialect; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.testcontainers.containers.PostgreSQLContainer; @@ -55,6 +58,11 @@ public class PostgresDataSourceConfiguration extends DataSourceConfiguration { return dataSource; } + @Bean + Dialect dialect() { + return PostgresDialect.INSTANCE; + } + /* * (non-Javadoc) * @see org.springframework.data.jdbc.testing.DataSourceFactoryBean#customizePopulator(org.springframework.jdbc.datasource.init.ResourceDatabasePopulator) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java index 66803880..e134a864 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/TestConfiguration.java @@ -38,6 +38,7 @@ import org.springframework.data.jdbc.core.convert.RelationResolver; import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.core.NamedQueries; @@ -86,10 +87,10 @@ public class TestConfiguration { @Bean DataAccessStrategy defaultDataAccessStrategy( @Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context, - JdbcConverter converter) { + JdbcConverter converter, Dialect dialect) { - DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy(new SqlGeneratorSource(context), - context, converter, template); + DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( + new SqlGeneratorSource(context, dialect), context, converter, template); return defaultDataAccessStrategy; } diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mysql.sql index db71d7a1..1ea550fa 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mysql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mysql.sql @@ -1,292 +1,297 @@ CREATE TABLE LEGO_SET ( - id1 BIGINT AUTO_INCREMENT PRIMARY KEY, - NAME VARCHAR(30) + id1 BIGINT AUTO_INCREMENT PRIMARY KEY, + NAME VARCHAR(30) ); CREATE TABLE MANUAL ( - id2 BIGINT AUTO_INCREMENT PRIMARY KEY, - LEGO_SET BIGINT, - ALTERNATIVE BIGINT, - CONTENT VARCHAR(2000) + id2 BIGINT AUTO_INCREMENT PRIMARY KEY, + LEGO_SET BIGINT, + ALTERNATIVE BIGINT, + CONTENT VARCHAR(2000) ); ALTER TABLE MANUAL - ADD FOREIGN KEY (LEGO_SET) - REFERENCES LEGO_SET (id1); + ADD FOREIGN KEY (LEGO_SET) + REFERENCES LEGO_SET (id1); CREATE TABLE ONE_TO_ONE_PARENT ( - id3 BIGINT AUTO_INCREMENT PRIMARY KEY, - content VARCHAR(30) + id3 BIGINT AUTO_INCREMENT PRIMARY KEY, + content VARCHAR(30) ); CREATE TABLE Child_No_Id ( - ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, - content VARCHAR(30) + ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, + content VARCHAR(30) ); CREATE TABLE LIST_PARENT ( - id4 BIGINT AUTO_INCREMENT PRIMARY KEY, - NAME VARCHAR(100) + id4 BIGINT AUTO_INCREMENT PRIMARY KEY, + NAME VARCHAR(100) ); CREATE TABLE element_no_id ( - content VARCHAR(100), - LIST_PARENT_key BIGINT, - LIST_PARENT BIGINT + content VARCHAR(100), + LIST_PARENT_key BIGINT, + LIST_PARENT BIGINT ); CREATE TABLE BYTE_ARRAY_OWNER ( - ID BIGINT AUTO_INCREMENT PRIMARY KEY, - BINARY_DATA VARBINARY(20) NOT NULL + ID BIGINT AUTO_INCREMENT PRIMARY KEY, + BINARY_DATA VARBINARY(20) NOT NULL ); CREATE TABLE CHAIN4 ( - FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE CHAIN3 ( - THREE BIGINT AUTO_INCREMENT PRIMARY KEY, - THREE_VALUE VARCHAR(20), - CHAIN4 BIGINT, - FOREIGN KEY (CHAIN4) REFERENCES CHAIN4(FOUR) + THREE BIGINT AUTO_INCREMENT PRIMARY KEY, + THREE_VALUE VARCHAR(20), + CHAIN4 BIGINT, + FOREIGN KEY (CHAIN4) REFERENCES CHAIN4 (FOUR) ); CREATE TABLE CHAIN2 ( - TWO BIGINT AUTO_INCREMENT PRIMARY KEY, - TWO_VALUE VARCHAR(20), - CHAIN3 BIGINT, - FOREIGN KEY (CHAIN3) REFERENCES CHAIN3(THREE) + TWO BIGINT AUTO_INCREMENT PRIMARY KEY, + TWO_VALUE VARCHAR(20), + CHAIN3 BIGINT, + FOREIGN KEY (CHAIN3) REFERENCES CHAIN3 (THREE) ); CREATE TABLE CHAIN1 ( - ONE BIGINT AUTO_INCREMENT PRIMARY KEY, - ONE_VALUE VARCHAR(20), - CHAIN2 BIGINT, - FOREIGN KEY (CHAIN2) REFERENCES CHAIN2(TWO) + ONE BIGINT AUTO_INCREMENT PRIMARY KEY, + ONE_VALUE VARCHAR(20), + CHAIN2 BIGINT, + FOREIGN KEY (CHAIN2) REFERENCES CHAIN2 (TWO) ); CREATE TABLE CHAIN0 ( - ZERO BIGINT AUTO_INCREMENT PRIMARY KEY, - ZERO_VALUE VARCHAR(20), - CHAIN1 BIGINT, - FOREIGN KEY (CHAIN1) REFERENCES CHAIN1(ONE) + ZERO BIGINT AUTO_INCREMENT PRIMARY KEY, + ZERO_VALUE VARCHAR(20), + CHAIN1 BIGINT, + FOREIGN KEY (CHAIN1) REFERENCES CHAIN1 (ONE) ); CREATE TABLE NO_ID_CHAIN4 ( - FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE NO_ID_CHAIN3 ( - THREE_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + THREE_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_CHAIN2 ( - TWO_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + TWO_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_CHAIN1 ( - ONE_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + ONE_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_CHAIN0 ( - ZERO_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + ZERO_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_LIST_CHAIN4 ( - FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE NO_ID_LIST_CHAIN3 ( - THREE_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY), - FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR) + THREE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY), + FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_LIST_CHAIN2 ( - TWO_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - NO_ID_LIST_CHAIN3_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY), - FOREIGN KEY ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY - ) REFERENCES NO_ID_LIST_CHAIN3 ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY - ) + TWO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) REFERENCES NO_ID_LIST_CHAIN3 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) ); CREATE TABLE NO_ID_LIST_CHAIN1 ( - ONE_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - NO_ID_LIST_CHAIN3_KEY BIGINT, - NO_ID_LIST_CHAIN2_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY), - FOREIGN KEY ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY - ) REFERENCES NO_ID_LIST_CHAIN2 ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY - ) + ONE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + NO_ID_LIST_CHAIN2_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) REFERENCES NO_ID_LIST_CHAIN2 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) ); CREATE TABLE NO_ID_LIST_CHAIN0 ( - ZERO_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - NO_ID_LIST_CHAIN3_KEY BIGINT, - NO_ID_LIST_CHAIN2_KEY BIGINT, - NO_ID_LIST_CHAIN1_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY, - NO_ID_LIST_CHAIN1_KEY), - FOREIGN KEY ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY - ) REFERENCES NO_ID_LIST_CHAIN1 ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY - ) + ZERO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + NO_ID_LIST_CHAIN2_KEY BIGINT, + NO_ID_LIST_CHAIN1_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY, + NO_ID_LIST_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) REFERENCES NO_ID_LIST_CHAIN1 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) ); - CREATE TABLE NO_ID_MAP_CHAIN4 ( - FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR BIGINT AUTO_INCREMENT PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE NO_ID_MAP_CHAIN3 ( - THREE_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY), - FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR) + THREE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY), + FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_MAP_CHAIN2 ( - TWO_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - NO_ID_MAP_CHAIN3_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY), - FOREIGN KEY ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY - ) REFERENCES NO_ID_MAP_CHAIN3 ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY - ) + TWO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) REFERENCES NO_ID_MAP_CHAIN3 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) ); CREATE TABLE NO_ID_MAP_CHAIN1 ( - ONE_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - NO_ID_MAP_CHAIN3_KEY VARCHAR(20), - NO_ID_MAP_CHAIN2_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY), - FOREIGN KEY ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY - ) REFERENCES NO_ID_MAP_CHAIN2 ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY - ) + ONE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) REFERENCES NO_ID_MAP_CHAIN2 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) ); CREATE TABLE NO_ID_MAP_CHAIN0 ( - ZERO_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - NO_ID_MAP_CHAIN3_KEY VARCHAR(20), - NO_ID_MAP_CHAIN2_KEY VARCHAR(20), - NO_ID_MAP_CHAIN1_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY, - NO_ID_MAP_CHAIN1_KEY), - FOREIGN KEY ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY - ) REFERENCES NO_ID_MAP_CHAIN1 ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY - ) + ZERO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + NO_ID_MAP_CHAIN1_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY, + NO_ID_MAP_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) REFERENCES NO_ID_MAP_CHAIN1 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) ); CREATE TABLE VERSIONED_AGGREGATE ( - ID BIGINT AUTO_INCREMENT PRIMARY KEY, - VERSION BIGINT + ID BIGINT AUTO_INCREMENT PRIMARY KEY, + VERSION BIGINT ); +CREATE TABLE WITH_READ_ONLY +( + ID BIGINT AUTO_INCREMENT PRIMARY KEY, + NAME VARCHAR(200), + READ_ONLY VARCHAR(200) DEFAULT 'from-db' +); \ No newline at end of file diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-postgres.sql index 9ac303a0..3e9396f7 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-postgres.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-postgres.sql @@ -11,302 +11,309 @@ DROP TABLE CHAIN3; DROP TABLE CHAIN2; DROP TABLE CHAIN1; DROP TABLE CHAIN0; +DROP TABLE WITH_READ_ONLY; CREATE TABLE LEGO_SET ( - id1 SERIAL PRIMARY KEY, - NAME VARCHAR(30) + id1 SERIAL PRIMARY KEY, + NAME VARCHAR(30) ); CREATE TABLE MANUAL ( - id2 SERIAL PRIMARY KEY, - LEGO_SET BIGINT, - ALTERNATIVE BIGINT, - CONTENT VARCHAR(2000) + id2 SERIAL PRIMARY KEY, + LEGO_SET BIGINT, + ALTERNATIVE BIGINT, + CONTENT VARCHAR(2000) ); ALTER TABLE MANUAL - ADD FOREIGN KEY (LEGO_SET) - REFERENCES LEGO_SET (id1); + ADD FOREIGN KEY (LEGO_SET) + REFERENCES LEGO_SET (id1); CREATE TABLE ONE_TO_ONE_PARENT ( - id3 SERIAL PRIMARY KEY, - content VARCHAR(30) + id3 SERIAL PRIMARY KEY, + content VARCHAR(30) ); CREATE TABLE Child_No_Id ( - ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, - content VARCHAR(30) + ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, + content VARCHAR(30) ); CREATE TABLE LIST_PARENT ( - id4 SERIAL PRIMARY KEY, - NAME VARCHAR(100) + id4 SERIAL PRIMARY KEY, + NAME VARCHAR(100) ); CREATE TABLE element_no_id ( - content VARCHAR(100), - LIST_PARENT_key BIGINT, - LIST_PARENT INTEGER + content VARCHAR(100), + LIST_PARENT_key BIGINT, + LIST_PARENT INTEGER ); CREATE TABLE ARRAY_OWNER ( - ID SERIAL PRIMARY KEY, - DIGITS VARCHAR(20)[10], - MULTIDIMENSIONAL VARCHAR(20)[10][10] + ID SERIAL PRIMARY KEY, + DIGITS VARCHAR(20)[10], + MULTIDIMENSIONAL VARCHAR(20)[10][10] ); CREATE TABLE BYTE_ARRAY_OWNER ( - ID SERIAL PRIMARY KEY, - BINARY_DATA BYTEA NOT NULL + ID SERIAL PRIMARY KEY, + BINARY_DATA BYTEA NOT NULL ); CREATE TABLE CHAIN4 ( - FOUR SERIAL PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE CHAIN3 ( - THREE SERIAL PRIMARY KEY, - THREE_VALUE VARCHAR(20), - CHAIN4 BIGINT, - FOREIGN KEY (CHAIN4) REFERENCES CHAIN4 (FOUR) + THREE SERIAL PRIMARY KEY, + THREE_VALUE VARCHAR(20), + CHAIN4 BIGINT, + FOREIGN KEY (CHAIN4) REFERENCES CHAIN4 (FOUR) ); CREATE TABLE CHAIN2 ( - TWO SERIAL PRIMARY KEY, - TWO_VALUE VARCHAR(20), - CHAIN3 BIGINT, - FOREIGN KEY (CHAIN3) REFERENCES CHAIN3 (THREE) + TWO SERIAL PRIMARY KEY, + TWO_VALUE VARCHAR(20), + CHAIN3 BIGINT, + FOREIGN KEY (CHAIN3) REFERENCES CHAIN3 (THREE) ); CREATE TABLE CHAIN1 ( - ONE SERIAL PRIMARY KEY, - ONE_VALUE VARCHAR(20), - CHAIN2 BIGINT, - FOREIGN KEY (CHAIN2) REFERENCES CHAIN2 (TWO) + ONE SERIAL PRIMARY KEY, + ONE_VALUE VARCHAR(20), + CHAIN2 BIGINT, + FOREIGN KEY (CHAIN2) REFERENCES CHAIN2 (TWO) ); CREATE TABLE CHAIN0 ( - ZERO SERIAL PRIMARY KEY, - ZERO_VALUE VARCHAR(20), - CHAIN1 BIGINT, - FOREIGN KEY (CHAIN1) REFERENCES CHAIN1 (ONE) + ZERO SERIAL PRIMARY KEY, + ZERO_VALUE VARCHAR(20), + CHAIN1 BIGINT, + FOREIGN KEY (CHAIN1) REFERENCES CHAIN1 (ONE) ); CREATE TABLE NO_ID_CHAIN4 ( - FOUR SERIAL PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE NO_ID_CHAIN3 ( - THREE_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + THREE_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_CHAIN2 ( - TWO_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + TWO_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_CHAIN1 ( - ONE_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + ONE_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_CHAIN0 ( - ZERO_VALUE VARCHAR(20), - NO_ID_CHAIN4 BIGINT, - FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) + ZERO_VALUE VARCHAR(20), + NO_ID_CHAIN4 BIGINT, + FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_LIST_CHAIN4 ( - FOUR SERIAL PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE NO_ID_LIST_CHAIN3 ( - THREE_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY), - FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR) + THREE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY), + FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_LIST_CHAIN2 ( - TWO_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - NO_ID_LIST_CHAIN3_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY), - FOREIGN KEY ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY - ) REFERENCES NO_ID_LIST_CHAIN3 ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY - ) + TWO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) REFERENCES NO_ID_LIST_CHAIN3 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY + ) ); CREATE TABLE NO_ID_LIST_CHAIN1 ( - ONE_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - NO_ID_LIST_CHAIN3_KEY BIGINT, - NO_ID_LIST_CHAIN2_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY), - FOREIGN KEY ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY - ) REFERENCES NO_ID_LIST_CHAIN2 ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY - ) + ONE_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + NO_ID_LIST_CHAIN2_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) REFERENCES NO_ID_LIST_CHAIN2 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY + ) ); CREATE TABLE NO_ID_LIST_CHAIN0 ( - ZERO_VALUE VARCHAR(20), - NO_ID_LIST_CHAIN4 BIGINT, - NO_ID_LIST_CHAIN4_KEY BIGINT, - NO_ID_LIST_CHAIN3_KEY BIGINT, - NO_ID_LIST_CHAIN2_KEY BIGINT, - NO_ID_LIST_CHAIN1_KEY BIGINT, - PRIMARY KEY (NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY, - NO_ID_LIST_CHAIN1_KEY), - FOREIGN KEY ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY - ) REFERENCES NO_ID_LIST_CHAIN1 ( - NO_ID_LIST_CHAIN4, - NO_ID_LIST_CHAIN4_KEY, - NO_ID_LIST_CHAIN3_KEY, - NO_ID_LIST_CHAIN2_KEY - ) + ZERO_VALUE VARCHAR(20), + NO_ID_LIST_CHAIN4 BIGINT, + NO_ID_LIST_CHAIN4_KEY BIGINT, + NO_ID_LIST_CHAIN3_KEY BIGINT, + NO_ID_LIST_CHAIN2_KEY BIGINT, + NO_ID_LIST_CHAIN1_KEY BIGINT, + PRIMARY KEY (NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY, + NO_ID_LIST_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) REFERENCES NO_ID_LIST_CHAIN1 ( + NO_ID_LIST_CHAIN4, + NO_ID_LIST_CHAIN4_KEY, + NO_ID_LIST_CHAIN3_KEY, + NO_ID_LIST_CHAIN2_KEY + ) ); - CREATE TABLE NO_ID_MAP_CHAIN4 ( - FOUR SERIAL PRIMARY KEY, - FOUR_VALUE VARCHAR(20) + FOUR SERIAL PRIMARY KEY, + FOUR_VALUE VARCHAR(20) ); CREATE TABLE NO_ID_MAP_CHAIN3 ( - THREE_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY), - FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR) + THREE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY), + FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR) ); CREATE TABLE NO_ID_MAP_CHAIN2 ( - TWO_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - NO_ID_MAP_CHAIN3_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY), - FOREIGN KEY ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY - ) REFERENCES NO_ID_MAP_CHAIN3 ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY - ) + TWO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) REFERENCES NO_ID_MAP_CHAIN3 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY + ) ); CREATE TABLE NO_ID_MAP_CHAIN1 ( - ONE_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - NO_ID_MAP_CHAIN3_KEY VARCHAR(20), - NO_ID_MAP_CHAIN2_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY), - FOREIGN KEY ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY - ) REFERENCES NO_ID_MAP_CHAIN2 ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY - ) + ONE_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) REFERENCES NO_ID_MAP_CHAIN2 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY + ) ); CREATE TABLE NO_ID_MAP_CHAIN0 ( - ZERO_VALUE VARCHAR(20), - NO_ID_MAP_CHAIN4 BIGINT, - NO_ID_MAP_CHAIN4_KEY VARCHAR(20), - NO_ID_MAP_CHAIN3_KEY VARCHAR(20), - NO_ID_MAP_CHAIN2_KEY VARCHAR(20), - NO_ID_MAP_CHAIN1_KEY VARCHAR(20), - PRIMARY KEY (NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY, - NO_ID_MAP_CHAIN1_KEY), - FOREIGN KEY ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY - ) REFERENCES NO_ID_MAP_CHAIN1 ( - NO_ID_MAP_CHAIN4, - NO_ID_MAP_CHAIN4_KEY, - NO_ID_MAP_CHAIN3_KEY, - NO_ID_MAP_CHAIN2_KEY - ) + ZERO_VALUE VARCHAR(20), + NO_ID_MAP_CHAIN4 BIGINT, + NO_ID_MAP_CHAIN4_KEY VARCHAR(20), + NO_ID_MAP_CHAIN3_KEY VARCHAR(20), + NO_ID_MAP_CHAIN2_KEY VARCHAR(20), + NO_ID_MAP_CHAIN1_KEY VARCHAR(20), + PRIMARY KEY (NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY, + NO_ID_MAP_CHAIN1_KEY), + FOREIGN KEY ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) REFERENCES NO_ID_MAP_CHAIN1 ( + NO_ID_MAP_CHAIN4, + NO_ID_MAP_CHAIN4_KEY, + NO_ID_MAP_CHAIN3_KEY, + NO_ID_MAP_CHAIN2_KEY + ) ); CREATE TABLE VERSIONED_AGGREGATE ( - ID SERIAL PRIMARY KEY, - VERSION BIGINT + ID SERIAL PRIMARY KEY, + VERSION BIGINT +); + +CREATE TABLE WITH_READ_ONLY +( + ID SERIAL PRIMARY KEY, + NAME VARCHAR(200), + READ_ONLY VARCHAR(200) DEFAULT 'from-db' ); \ No newline at end of file diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java index 8f6f45d9..21c1634e 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java @@ -16,6 +16,8 @@ package org.springframework.data.relational.core.dialect; import org.springframework.data.relational.core.sql.render.SelectRenderContext; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.SqlIdentifier; /** * Represents a dialect that is implemented by a particular database. Please note that not all features are supported by @@ -50,4 +52,14 @@ public interface Dialect { * @return the {@link SelectRenderContext}. */ SelectRenderContext getSelectContext(); + + /** + * Returns the {@link IdentifierProcessing} used for processing {@link SqlIdentifier} when converting them to SQL snippets or parameter names. + * + * @return the {@link IdentifierProcessing}. Guaranteed to be not {@literal null}. + * @since 2.0 + */ + default IdentifierProcessing getIdentifierProcessing() { + return IdentifierProcessing.ANSI; + } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java new file mode 100644 index 00000000..94e2184b --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java @@ -0,0 +1,57 @@ +/* + * 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.relational.core.dialect; + +/** + * A {@link Dialect} for HsqlDb. + * + * @author Jens Schauder + */ +public class HsqlDbDialect extends AbstractDialect { + + public static final HsqlDbDialect INSTANCE = new HsqlDbDialect(); + + protected HsqlDbDialect() { } + + @Override + public LimitClause limit() { + return LIMIT_CLAUSE; + } + + private static final LimitClause LIMIT_CLAUSE = new LimitClause() { + + @Override + public String getLimit(long limit) { + return "LIMIT " + limit; + } + + @Override + public String getOffset(long offset) { + return "OFFSET " + offset; + } + + @Override + public String getLimitOffset(long limit, long offset) { + return getOffset(offset) + " " + getLimit(limit); + } + + @Override + public Position getClausePosition() { + return Position.AFTER_ORDER_BY; + } + }; + +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MariaDbDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MariaDbDialect.java new file mode 100644 index 00000000..dd42bb2e --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MariaDbDialect.java @@ -0,0 +1,29 @@ +/* + * 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.relational.core.dialect; + +/** + * The {@link Dialect} to be used with MariaDb. Since we haven't encountered any significant differences between MariaDb + * and Mysql its instance is actually {@link MySqlDialect#INSTANCE}, although that might change at any time. + * + * @author Jens Schauder + */ +public class MariaDbDialect extends MySqlDialect{ + + public static final Dialect INSTANCE = MySqlDialect.INSTANCE; + + protected MariaDbDialect() { } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java index 50f6ff9e..e19b9c7e 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java @@ -15,8 +15,13 @@ */ package org.springframework.data.relational.core.dialect; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; + /** - * An SQL dialect for MySQL. + * A SQL dialect for MySQL. * * @author Mark Paluch * @author Jens Schauder @@ -29,6 +34,8 @@ public class MySqlDialect extends AbstractDialect { */ public static final MySqlDialect INSTANCE = new MySqlDialect(); + protected MySqlDialect() { } + private static final LimitClause LIMIT_CLAUSE = new LimitClause() { /* @@ -80,4 +87,9 @@ public class MySqlDialect extends AbstractDialect { public LimitClause limit() { return LIMIT_CLAUSE; } + + @Override + public IdentifierProcessing getIdentifierProcessing() { + return new DefaultIdentifierProcessing(new Quoting("`"), LetterCasing.LOWER_CASE); + } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java index 25e398df..435fe2d7 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/PostgresDialect.java @@ -17,6 +17,10 @@ package org.springframework.data.relational.core.dialect; import lombok.RequiredArgsConstructor; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -33,6 +37,8 @@ public class PostgresDialect extends AbstractDialect { */ public static final PostgresDialect INSTANCE = new PostgresDialect(); + protected PostgresDialect() {} + private static final LimitClause LIMIT_CLAUSE = new LimitClause() { /* @@ -116,4 +122,9 @@ public class PostgresDialect extends AbstractDialect { return ClassUtils.resolvePrimitiveIfNecessary(userType); } } + + @Override + public IdentifierProcessing getIdentifierProcessing() { + return new DefaultIdentifierProcessing(Quoting.ANSI, LetterCasing.LOWER_CASE); + } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java index b7ea2946..9242d4a0 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/SqlServerDialect.java @@ -31,6 +31,8 @@ public class SqlServerDialect extends AbstractDialect { */ public static final SqlServerDialect INSTANCE = new SqlServerDialect(); + protected SqlServerDialect() { } + private static final LimitClause LIMIT_CLAUSE = new LimitClause() { /* diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java index c0aa4b6f..2ae5a096 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentProperty.java @@ -30,6 +30,8 @@ import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; +import org.springframework.data.relational.domain.SqlIdentifier; +import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier; import org.springframework.data.util.Lazy; import org.springframework.data.util.Optionals; import org.springframework.lang.Nullable; @@ -58,9 +60,9 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent } private final RelationalMappingContext context; - private final Lazy columnName; - private final Lazy> collectionIdColumnName; - private final Lazy collectionKeyColumnName; + private final Lazy columnName; + private final Lazy> collectionIdColumnName; + private final Lazy collectionKeyColumnName; private final Lazy isEmbedded; private final Lazy embeddedPrefix; private final Lazy> columnType = Lazy.of(this::doGetColumnType); @@ -91,17 +93,24 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent this.columnName = Lazy.of(() -> Optional.ofNullable(findAnnotation(Column.class)) // .map(Column::value) // .filter(StringUtils::hasText) // + .map(name -> SqlIdentifier.quoted(name).withAdjustableLetterCasing()) // .orElseGet(() -> context.getNamingStrategy().getColumnName(this))); this.collectionIdColumnName = Lazy.of(() -> Optionals - .toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)).map(MappedCollection::idColumn), - Optional.ofNullable(findAnnotation(Column.class)).map(Column::value)) // - .filter(StringUtils::hasText).findFirst()); + .toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)) // + .map(MappedCollection::idColumn), // + Optional.ofNullable(findAnnotation(Column.class)) // + .map(Column::value)) // + .filter(StringUtils::hasText) // + .findFirst() // + .map(name -> SqlIdentifier.quoted(name).withAdjustableLetterCasing())); // - this.collectionKeyColumnName = Lazy.of(() -> Optionals - .toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)).map(MappedCollection::keyColumn), + this.collectionKeyColumnName = Lazy.of(() -> Optionals // + .toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)).map(MappedCollection::keyColumn), // Optional.ofNullable(findAnnotation(Column.class)).map(Column::keyColumn)) // - .filter(StringUtils::hasText).findFirst().orElseGet(() -> context.getNamingStrategy().getKeyColumn(this))); + .filter(StringUtils::hasText).findFirst() // + .map(name -> (SqlIdentifier) SqlIdentifier.quoted(name).withAdjustableLetterCasing()) // + .orElseGet(() -> context.getNamingStrategy().getKeyColumn(this))); } /* @@ -128,7 +137,7 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent * @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentProperty#getColumnName() */ @Override - public String getColumnName() { + public SimpleSqlIdentifier getColumnName() { return columnName.get(); } @@ -178,18 +187,18 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent } @Override - public String getReverseColumnName() { + public SqlIdentifier getReverseColumnName() { return collectionIdColumnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(this)); } @Override - public String getReverseColumnName(PersistentPropertyPathExtension path) { + public SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) { return collectionIdColumnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(path)); } @Override - public String getKeyColumn() { + public SqlIdentifier getKeyColumn() { return isQualified() ? collectionKeyColumnName.get() : null; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/CachingNamingStrategy.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/CachingNamingStrategy.java index 79b05369..8069ad56 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/CachingNamingStrategy.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/CachingNamingStrategy.java @@ -18,6 +18,8 @@ package org.springframework.data.relational.core.mapping; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.springframework.data.relational.domain.SqlIdentifier; +import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier; import org.springframework.data.util.Lazy; import org.springframework.util.Assert; import org.springframework.util.ConcurrentReferenceHashMap; @@ -32,19 +34,19 @@ class CachingNamingStrategy implements NamingStrategy { private final NamingStrategy delegate; - private final Map columnNames = new ConcurrentHashMap<>(); - private final Map keyColumns = new ConcurrentHashMap<>(); - private final Map, String> qualifiedTableNames = new ConcurrentReferenceHashMap<>(); - private final Map, String> tableNames = new ConcurrentReferenceHashMap<>(); + private final Map columnNames = new ConcurrentHashMap<>(); + private final Map keyColumns = new ConcurrentHashMap<>(); + private final Map, SqlIdentifier> qualifiedTableNames = new ConcurrentReferenceHashMap<>(); + private final Map, SqlIdentifier> tableNames = new ConcurrentReferenceHashMap<>(); - private final Lazy schema; + private final Lazy schema; /** * Creates a new {@link CachingNamingStrategy} with the given delegate {@link NamingStrategy}. * * @param delegate must not be {@literal null}. */ - public CachingNamingStrategy(NamingStrategy delegate) { + CachingNamingStrategy(NamingStrategy delegate) { Assert.notNull(delegate, "Delegate must not be null!"); @@ -57,7 +59,7 @@ class CachingNamingStrategy implements NamingStrategy { * @see org.springframework.data.relational.core.mapping.NamingStrategy#getKeyColumn(org.springframework.data.relational.core.mapping.RelationalPersistentProperty) */ @Override - public String getKeyColumn(RelationalPersistentProperty property) { + public SqlIdentifier getKeyColumn(RelationalPersistentProperty property) { return keyColumns.computeIfAbsent(property, delegate::getKeyColumn); } @@ -66,7 +68,7 @@ class CachingNamingStrategy implements NamingStrategy { * @see org.springframework.data.relational.core.mapping.NamingStrategy#getQualifiedTableName(java.lang.Class) */ @Override - public String getQualifiedTableName(Class type) { + public SqlIdentifier getQualifiedTableName(Class type) { return qualifiedTableNames.computeIfAbsent(type, delegate::getQualifiedTableName); } @@ -75,7 +77,7 @@ class CachingNamingStrategy implements NamingStrategy { * @see org.springframework.data.relational.core.mapping.NamingStrategy#getTableName(java.lang.Class) */ @Override - public String getTableName(Class type) { + public SqlIdentifier getTableName(Class type) { return tableNames.computeIfAbsent(type, delegate::getTableName); } @@ -84,7 +86,7 @@ class CachingNamingStrategy implements NamingStrategy { * @see org.springframework.data.relational.core.mapping.NamingStrategy#getReverseColumnName(org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension) */ @Override - public String getReverseColumnName(PersistentPropertyPathExtension path) { + public SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) { return delegate.getReverseColumnName(path); } @@ -93,7 +95,7 @@ class CachingNamingStrategy implements NamingStrategy { * @see org.springframework.data.relational.core.mapping.NamingStrategy#getReverseColumnName(org.springframework.data.relational.core.mapping.RelationalPersistentProperty) */ @Override - public String getReverseColumnName(RelationalPersistentProperty property) { + public SqlIdentifier getReverseColumnName(RelationalPersistentProperty property) { return delegate.getReverseColumnName(property); } @@ -102,7 +104,7 @@ class CachingNamingStrategy implements NamingStrategy { * @see org.springframework.data.relational.core.mapping.NamingStrategy#getSchema() */ @Override - public String getSchema() { + public SqlIdentifier getSchema() { return schema.get(); } @@ -111,7 +113,7 @@ class CachingNamingStrategy implements NamingStrategy { * @see org.springframework.data.relational.core.mapping.NamingStrategy#getColumnName(org.springframework.data.relational.core.mapping.RelationalPersistentProperty) */ @Override - public String getColumnName(RelationalPersistentProperty property) { + public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) { return columnNames.computeIfAbsent(property, delegate::getColumnName); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/NamingStrategy.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/NamingStrategy.java index be5ee64d..c0618dc0 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/NamingStrategy.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/NamingStrategy.java @@ -15,6 +15,10 @@ */ package org.springframework.data.relational.core.mapping; +import static org.springframework.data.relational.domain.SqlIdentifier.*; + +import org.springframework.data.relational.domain.SqlIdentifier; +import org.springframework.data.relational.domain.SqlIdentifier.*; import org.springframework.data.util.ParsingUtils; import org.springframework.util.Assert; @@ -45,34 +49,34 @@ public interface NamingStrategy { * * @return Empty String representing no schema */ - default String getSchema() { - return ""; + default SqlIdentifier getSchema() { + return SqlIdentifier.EMPTY; } /** * The name of the table to be used for persisting entities having the type passed as an argument. The default * implementation takes the {@code type.getSimpleName()} and separates camel case parts with '_'. */ - default String getTableName(Class type) { + default SqlIdentifier getTableName(Class type) { Assert.notNull(type, "Type must not be null."); - return ParsingUtils.reconcatenateCamelCase(type.getSimpleName(), "_"); + return quoted(ParsingUtils.reconcatenateCamelCase(type.getSimpleName(), "_")).withAdjustableLetterCasing(); } /** * Defaults to return the given {@link RelationalPersistentProperty}'s name with the parts of a camel case name * separated by '_'; */ - default String getColumnName(RelationalPersistentProperty property) { + default SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) { Assert.notNull(property, "Property must not be null."); - return ParsingUtils.reconcatenateCamelCase(property.getName(), "_"); + return quoted(ParsingUtils.reconcatenateCamelCase(property.getName(), "_")).withAdjustableLetterCasing(); } - default String getQualifiedTableName(Class type) { - return this.getSchema() + (this.getSchema().equals("") ? "" : ".") + this.getTableName(type); + default SqlIdentifier getQualifiedTableName(Class type) { + return this.getSchema().concat(this.getTableName(type)); } /** @@ -81,14 +85,14 @@ public interface NamingStrategy { * @param property The property who's column name in the owner table is required * @return a column name. Must not be {@code null}. */ - default String getReverseColumnName(RelationalPersistentProperty property) { + default SqlIdentifier getReverseColumnName(RelationalPersistentProperty property) { Assert.notNull(property, "Property must not be null."); return property.getOwner().getTableName(); } - default String getReverseColumnName(PersistentPropertyPathExtension path) { + default SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) { return getTableName(path.getIdDefiningParentPath().getLeafEntity().getType()); } @@ -99,10 +103,10 @@ public interface NamingStrategy { * * @return name of the key column. Must not be {@code null}. */ - default String getKeyColumn(RelationalPersistentProperty property) { + default SqlIdentifier getKeyColumn(RelationalPersistentProperty property) { Assert.notNull(property, "Property must not be null."); - return getReverseColumnName(property) + "_key"; + return getReverseColumnName(property).suffix("_key"); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyPathExtension.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyPathExtension.java index 01901488..d233a324 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyPathExtension.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/PersistentPropertyPathExtension.java @@ -20,6 +20,8 @@ import lombok.EqualsAndHashCode; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.relational.domain.SqlIdentifier; +import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier; import org.springframework.data.util.Lazy; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -38,7 +40,7 @@ public class PersistentPropertyPathExtension { private final @Nullable PersistentPropertyPath path; private final MappingContext, RelationalPersistentProperty> context; - private final Lazy columnAlias = Lazy.of(() -> prefixWithTableAlias(getColumnName())); + private final Lazy columnAlias = Lazy.of(() -> prefixWithTableAlias(getColumnName())); /** * Creates the empty path referencing the root itself. @@ -156,10 +158,9 @@ public class PersistentPropertyPathExtension { * * @throws IllegalStateException when called on an empty path. */ - public String getReverseColumnName() { + public SqlIdentifier getReverseColumnName() { Assert.state(path != null, "Empty paths don't have a reverse column name"); - return path.getRequiredLeafProperty().getReverseColumnName(this); } @@ -168,7 +169,7 @@ public class PersistentPropertyPathExtension { * * @throws IllegalStateException when called on an empty path. */ - public String getReverseColumnNameAlias() { + public SqlIdentifier getReverseColumnNameAlias() { return prefixWithTableAlias(getReverseColumnName()); } @@ -178,7 +179,7 @@ public class PersistentPropertyPathExtension { * * @throws IllegalStateException when called on an empty path. */ - public String getColumnName() { + public SqlIdentifier getColumnName() { Assert.state(path != null, "Path is null"); @@ -190,7 +191,7 @@ public class PersistentPropertyPathExtension { * * @throws IllegalStateException when called on an empty path. */ - public String getColumnAlias() { + public SqlIdentifier getColumnAlias() { return columnAlias.get(); } @@ -228,7 +229,7 @@ public class PersistentPropertyPathExtension { * * @return the name of the table. Guaranteed to be not {@literal null}. */ - public String getTableName() { + public SqlIdentifier getTableName() { return getTableOwningAncestor().getRequiredLeafEntity().getTableName(); } @@ -238,7 +239,7 @@ public class PersistentPropertyPathExtension { * @return a table alias, {@literal null} if the table owning path is the empty path. */ @Nullable - public String getTableAlias() { + public SqlIdentifier getTableAlias() { PersistentPropertyPathExtension tableOwner = getTableOwningAncestor(); @@ -249,7 +250,7 @@ public class PersistentPropertyPathExtension { /** * The column name of the id column of the ancestor path that represents an actual table. */ - public String getIdColumnName() { + public SqlIdentifier getIdColumnName() { return getTableOwningAncestor().getRequiredLeafEntity().getIdColumn(); } @@ -257,7 +258,7 @@ public class PersistentPropertyPathExtension { * If the table owning ancestor has an id the column name of that id property is returned. Otherwise the reverse * column is returned. */ - public String getEffectiveIdColumnName() { + public SqlIdentifier getEffectiveIdColumnName() { PersistentPropertyPathExtension owner = getTableOwningAncestor(); return owner.path == null ? owner.getRequiredLeafEntity().getIdColumn() : owner.getReverseColumnName(); @@ -296,8 +297,8 @@ public class PersistentPropertyPathExtension { * @return May be {@literal null}. */ @Nullable - public String getQualifierColumn() { - return path == null ? "" : path.getRequiredLeafProperty().getKeyColumn(); + public SqlIdentifier getQualifierColumn() { + return path == null ? SqlIdentifier.EMPTY : path.getRequiredLeafProperty().getKeyColumn(); } /** @@ -384,7 +385,7 @@ public class PersistentPropertyPathExtension { return isEntity() && !isEmbedded() ? this : getParentPath().getTableOwningAncestor(); } - private String assembleTableAlias() { + private SqlIdentifier assembleTableAlias() { Assert.state(path != null, "Path is null"); @@ -393,15 +394,15 @@ public class PersistentPropertyPathExtension { if (path.getLength() == 1) { Assert.notNull(prefix, "Prefix mus not be null."); - return prefix; + return SqlIdentifier.quoted(prefix).withAdjustableLetterCasing(); } PersistentPropertyPathExtension parentPath = getParentPath(); - return parentPath.isEmbedded() ? parentPath.assembleTableAlias() + prefix - : parentPath.assembleTableAlias() + "_" + prefix; + return parentPath.isEmbedded() ? parentPath.assembleTableAlias().suffix(prefix) + : parentPath.assembleTableAlias().suffix("_" + prefix); } - private String assembleColumnName(String suffix) { + private SqlIdentifier assembleColumnName(SimpleSqlIdentifier suffix) { Assert.state(path != null, "Path is null"); @@ -418,17 +419,17 @@ public class PersistentPropertyPathExtension { String embeddedPrefix = parentLeaf.getEmbeddedPrefix(); - return getParentPath().assembleColumnName(embeddedPrefix + suffix); + return getParentPath().assembleColumnName(suffix.prefix(embeddedPrefix)); } private RelationalPersistentEntity getRequiredLeafEntity() { return path == null ? entity : context.getRequiredPersistentEntity(path.getRequiredLeafProperty().getActualType()); } - private String prefixWithTableAlias(String columnName) { + private SqlIdentifier prefixWithTableAlias(SqlIdentifier columnName) { - String tableAlias = getTableAlias(); - return tableAlias == null ? columnName : tableAlias + "_" + columnName; + SqlIdentifier tableAlias = getTableAlias(); + return tableAlias == null ? columnName : columnName.prefix(tableAlias, "_"); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java index 6009aefa..04ba5dd9 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntity.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.core.mapping; import org.springframework.data.mapping.model.MutablePersistentEntity; +import org.springframework.data.relational.domain.SqlIdentifier; /** * A {@link org.springframework.data.mapping.PersistentEntity} interface with additional methods for JDBC/RDBMS related @@ -31,12 +32,12 @@ public interface RelationalPersistentEntity extends MutablePersistentEntity extends BasicPersistentEntity { private final NamingStrategy namingStrategy; - private final Lazy> tableName; + private final Lazy> tableName; /** * Creates a new {@link RelationalPersistentEntityImpl} for the given {@link TypeInformation}. @@ -50,6 +51,7 @@ class RelationalPersistentEntityImpl extends BasicPersistentEntity SqlIdentifier.quoted(name).withAdjustableLetterCasing()) // ); } @@ -58,7 +60,7 @@ class RelationalPersistentEntityImpl extends BasicPersistentEntity namingStrategy.getQualifiedTableName(getType())); } @@ -67,7 +69,7 @@ class RelationalPersistentEntityImpl extends BasicPersistentEntity targetType) { + public static Identifier of(SqlIdentifier name, Object value, Class targetType) { - Assert.hasText(name, "Name must not be empty!"); + Assert.notNull(name, "Name must not be empty!"); Assert.notNull(targetType, "Target type must not be null!"); return new Identifier(Collections.singletonList(new SingleIdentifierValue(name, value, targetType))); @@ -83,7 +83,7 @@ public final class Identifier { * @param map must not be {@literal null}. * @return the {@link Identifier} from a {@link Map} of name to value tuples. */ - public static Identifier from(Map map) { + public static Identifier from(Map map) { Assert.notNull(map, "Map must not be null!"); @@ -111,9 +111,9 @@ public final class Identifier { * @return the {@link Identifier} containing all existing keys and the key part for {@code name}, {@code value}, and a * {@link Class target type}. */ - public Identifier withPart(String name, Object value, Class targetType) { + public Identifier withPart(SqlIdentifier name, Object value, Class targetType) { - Assert.hasText(name, "Name must not be empty!"); + Assert.notNull(name, "Name must not be null!"); Assert.notNull(targetType, "Target type must not be null!"); boolean overwritten = false; @@ -141,9 +141,9 @@ public final class Identifier { * * @return a {@link Map} containing the identifier name to value tuples. */ - public Map toMap() { + public Map toMap() { - Map result = new LinkedHashMap<>(); + Map result = new LinkedHashMap<>(); forEach((name, value, type) -> result.put(name, value)); return result; } @@ -188,7 +188,7 @@ public final class Identifier { @AllArgsConstructor(access = AccessLevel.PRIVATE) static class SingleIdentifierValue { - String name; + SqlIdentifier name; Object value; Class targetType; } @@ -204,11 +204,11 @@ public final class Identifier { /** * Performs this operation on the given arguments. - * + * * @param name * @param value * @param targetType */ - void accept(String name, Object value, Class targetType); + void accept(SqlIdentifier name, Object value, Class targetType); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/IdentifierProcessing.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/IdentifierProcessing.java new file mode 100644 index 00000000..78dccb89 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/IdentifierProcessing.java @@ -0,0 +1,151 @@ +/* + * 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.relational.domain; + +/** + * An interface describing the processing steps for the conversion of {@link SqlIdentifier} to SQL snippets or column + * names. + * + * @author Jens Schauder + * @since 2.0 + */ +public interface IdentifierProcessing { + + /** + * An {@link IdentifierProcessing} that can be used for databases adhering to the SQL standard which uses double + * quotes ({@literal "}) for quoting and makes unquoted literals equivalent to upper case. + */ + IdentifierProcessing ANSI = new DefaultIdentifierProcessing(Quoting.ANSI, LetterCasing.UPPER_CASE); + + /** + * Converts a {@link String} representing a bare name of an identifier to a {@link String} with proper quoting + * applied. + * + * @param identifier the name of an identifier. Must not be {@literal null}. + * @return a quoted name of an identifier. Guaranteed to be not {@literal null}. + */ + String quote(String identifier); + + /** + * Standardizes the use of upper and lower case letters in an identifier in such a way that semantically the same + * identifier results from the quoted and the unquoted version. If this is not possible use of + * {@link LetterCasing#AS_IS} is recommended. + * + * @param identifier an identifier with arbitrary upper and lower cases. must not be {@literal null}. + * @return an identifier with standardized use of upper and lower case letter. Guaranteed to be not {@literal null}. + */ + String standardizeLetterCase(String identifier); + + /** + * An {@link IdentifierProcessing} implementation based on two implementations for the quoting and for the letter case + * standardization. + */ + class DefaultIdentifierProcessing implements IdentifierProcessing { + + private final Quoting quoting; + private final LetterCasing letterCasing; + + public DefaultIdentifierProcessing(Quoting quoting, LetterCasing letterCasing) { + this.quoting = quoting; + this.letterCasing = letterCasing; + } + + @Override + public String quote(String identifier) { + return quoting.apply(identifier); + } + + @Override + public String standardizeLetterCase(String identifier) { + return letterCasing.apply(identifier); + } + } + + /** + * A conversion from unquoted identifiers to quoted identifiers. + * + * @author Jens Schauder + * @since 2.0 + */ + class Quoting { + + public static final Quoting ANSI = new Quoting("\""); + + private final String prefix; + private final String suffix; + + /** + * Constructs a {@literal Quoting} with potential different prefix and suffix used for quoting. + * + * @param prefix a {@literal String} prefixed before the name for quoting it. + * @param suffix a {@literal String} suffixed at the end of the name for quoting it. + */ + public Quoting(String prefix, String suffix) { + + this.prefix = prefix; + this.suffix = suffix; + } + + /** + * Constructs a {@literal Quoting} with the same {@literal String} appended in front and end of an identifier. + * + * @param quoteCharacter the value appended at the beginning and the end of a name in order to quote it. + */ + public Quoting(String quoteCharacter) { + this(quoteCharacter, quoteCharacter); + } + + public String apply(String identifier) { + return prefix + identifier + suffix; + } + } + + /** + * Encapsulates the three kinds of letter casing supported. + * + * @author Jens Schauder + * @since 2.0 + */ + enum LetterCasing { + + UPPER_CASE { + + @Override + public String apply(String identifier) { + return identifier.toUpperCase(); + } + }, + + LOWER_CASE { + + @Override + public String apply(String identifier) { + return identifier.toLowerCase(); + } + }, + + AS_IS { + + @Override + public String apply(String identifier) { + return identifier; + } + }; + + abstract String apply(String identifier); + } + +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlIdentifier.java new file mode 100644 index 00000000..d40f11cd --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlIdentifier.java @@ -0,0 +1,219 @@ +/* + * 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.relational.domain; + +import java.util.Arrays; +import java.util.Objects; +import java.util.StringJoiner; + +import org.springframework.util.Assert; + +/** + * Represents a named object that exists in the database like a table name or a column name + * + * @author Jens Schauder + * @since 2.0 + */ +public interface SqlIdentifier { + + SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator); + + SqlIdentifier suffix(String suffix); + + SqlIdentifier concat(SqlIdentifier second); + + String toSql(IdentifierProcessing processing); + + String toColumnName(IdentifierProcessing processing); + + static SimpleSqlIdentifier quoted(String name) { + return new SimpleSqlIdentifier(name, true, true); + } + + static SimpleSqlIdentifier unquoted(String name) { + return new SimpleSqlIdentifier(name, false, true); + } + + SqlIdentifier EMPTY = new SqlIdentifier() { + + @Override + public SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator) { + throw new UnsupportedOperationException("We can't prefix an empty DatabaseObjectIdentifier"); + } + + @Override + public SqlIdentifier suffix(String suffix) { + throw new UnsupportedOperationException("We can't suffix an empty DatabaseObjectIdentifier"); + } + + @Override + public SqlIdentifier concat(SqlIdentifier second) { + return second; + } + + @Override + public String toSql(IdentifierProcessing processing) { + throw new UnsupportedOperationException("An empty SqlIdentifier can't be used in to create SQL snippets"); + } + + @Override + public String toColumnName(IdentifierProcessing processing) { + throw new UnsupportedOperationException("An empty SqlIdentifier can't be used in to create column names"); + } + + public String toString() { + return ""; + } + }; + + final class SimpleSqlIdentifier implements SqlIdentifier { + + private final String name; + private final boolean quoted; + private final boolean fixedLetterCasing; + + private SimpleSqlIdentifier(String name, boolean quoted, boolean fixedLetterCasing) { + + Assert.hasText(name, "A database object must have at least on name part."); + + this.name = name; + this.quoted = quoted; + this.fixedLetterCasing = fixedLetterCasing; + } + + public SimpleSqlIdentifier withAdjustableLetterCasing() { + return new SimpleSqlIdentifier(name, quoted, false); + } + + public SimpleSqlIdentifier prefix(String prefix) { + return new SimpleSqlIdentifier(prefix + name, quoted, fixedLetterCasing); + } + + @Override + public SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator) { + + Assert.isInstanceOf(SimpleSqlIdentifier.class, prefix, "Prefixing is only supported for simple SqlIdentifier"); + + return new SimpleSqlIdentifier(((SimpleSqlIdentifier) prefix).name + separator + name, quoted, fixedLetterCasing); + } + + public SimpleSqlIdentifier suffix(String suffix) { + return new SimpleSqlIdentifier(name + suffix, quoted, fixedLetterCasing); + } + + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + SimpleSqlIdentifier that = (SimpleSqlIdentifier) o; + return quoted == that.quoted && Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + + int result = Objects.hash(quoted); + result = 31 * result + name.hashCode(); + return result; + } + + @Override + public String toString() { + return "DatabaseObjectIdentifier{" + "name=" + name + ", quoted=" + quoted + '}'; + } + + @Override + public SqlIdentifier concat(SqlIdentifier second) { + // TODO: this is completely broken and needs fixing. + return new CombinedSqlIdentifier(this, (SimpleSqlIdentifier) second); + } + + @Override + public String toSql(IdentifierProcessing processing) { + + return quoted ? processing.quote(toColumnName(processing)) : toColumnName(processing); + } + + @Override + public String toColumnName(IdentifierProcessing processing) { + return fixedLetterCasing ? name : processing.standardizeLetterCase(name); + } + } + + final class CombinedSqlIdentifier implements SqlIdentifier { + + private final SimpleSqlIdentifier[] parts; + + private CombinedSqlIdentifier(SimpleSqlIdentifier... parts) { + this.parts = parts; + } + + @Override + public SqlIdentifier concat(SqlIdentifier second) { + throw new UnsupportedOperationException(); + } + + @Override + public SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator) { + throw new UnsupportedOperationException(); + } + + @Override + public SqlIdentifier suffix(String suffix) { + throw new UnsupportedOperationException(); + } + + @Override + public String toSql(IdentifierProcessing processing) { + + StringJoiner stringJoiner = new StringJoiner("."); + + for (SimpleSqlIdentifier namePart : parts) { + stringJoiner.add(namePart.toSql(processing)); + } + + return stringJoiner.toString(); + } + + @Override + public String toColumnName(IdentifierProcessing processing) { + throw new UnsupportedOperationException("A CombinedSqlIdentifier can't be used as a column name"); + } + + @Override + public String toString() { + return "CombinedDatabaseObjectIdentifier{" + "parts=" + Arrays.toString(parts) + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + CombinedSqlIdentifier that = (CombinedSqlIdentifier) o; + return Arrays.equals(parts, that.parts); + } + + @Override + public int hashCode() { + return Arrays.hashCode(parts); + } + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/RelationalEntityInformation.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/RelationalEntityInformation.java index 55620b4b..049fc1b8 100755 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/RelationalEntityInformation.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/RelationalEntityInformation.java @@ -15,6 +15,7 @@ */ package org.springframework.data.relational.repository.query; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.repository.core.EntityInformation; /** @@ -29,5 +30,5 @@ public interface RelationalEntityInformation extends EntityInformation extends EntityMetadata { * * @return */ - String getTableName(); + SqlIdentifier getTableName(); /** * Returns the {@link RelationalPersistentEntity} that supposed to determine the table to be queried. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/SimpleRelationalEntityMetadata.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/SimpleRelationalEntityMetadata.java index 36fec020..4635f966 100755 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/SimpleRelationalEntityMetadata.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/query/SimpleRelationalEntityMetadata.java @@ -18,6 +18,7 @@ package org.springframework.data.relational.repository.query; import lombok.Getter; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.util.Assert; /** @@ -56,7 +57,7 @@ public class SimpleRelationalEntityMetadata implements RelationalEntityMetada /* (non-Javadoc) * @see org.springframework.data.relational.repository.query.RelationalEntityMetadata#getTableName() */ - public String getTableName() { + public SqlIdentifier getTableName() { return tableEntity.getTableName(); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java index 9ec5a9ba..39b583b5 100755 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/repository/support/MappingRelationalEntityInformation.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.repository.support; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.relational.repository.query.RelationalEntityInformation; import org.springframework.data.repository.core.support.PersistentEntityInformation; import org.springframework.lang.Nullable; @@ -32,7 +33,7 @@ public class MappingRelationalEntityInformation extends PersistentEntityI implements RelationalEntityInformation { private final RelationalPersistentEntity entityMetadata; - private final @Nullable String customTableName; + private final @Nullable SqlIdentifier customTableName; private final Class fallbackIdType; /** @@ -81,14 +82,14 @@ public class MappingRelationalEntityInformation extends PersistentEntityI super(entity); this.entityMetadata = entity; - this.customTableName = customTableName; + this.customTableName = customTableName == null ? null : SqlIdentifier.quoted(customTableName); this.fallbackIdType = idType != null ? idType : (Class) Long.class; } /* (non-Javadoc) * @see org.springframework.data.relational.repository.query.RelationalEntityInformation#getTableName() */ - public String getTableName() { + public SqlIdentifier getTableName() { return customTableName == null ? entityMetadata.getTableName() : customTableName; } diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/HsqlDbDialectUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/HsqlDbDialectUnitTests.java new file mode 100644 index 00000000..818bf406 --- /dev/null +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/HsqlDbDialectUnitTests.java @@ -0,0 +1,69 @@ +/* + * 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.relational.core.dialect; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Test; + +/** + * Unit tests for the {@link HsqlDbDialect}. + * + * @author Jens Schauder + */ +public class HsqlDbDialectUnitTests { + + @Test // DATAJDBC-386 + public void shouldNotSupportArrays() { + + ArrayColumns arrayColumns = HsqlDbDialect.INSTANCE.getArraySupport(); + + assertThat(arrayColumns.isSupported()).isFalse(); + } + + @Test // DATAJDBC-386 + public void shouldRenderLimit() { + + LimitClause limit = HsqlDbDialect.INSTANCE.limit(); + + assertThat(limit.getClausePosition()).isEqualTo(LimitClause.Position.AFTER_ORDER_BY); + assertThat(limit.getLimit(10)).isEqualTo("LIMIT 10"); + } + + @Test // DATAJDBC-386 + public void shouldRenderOffset() { + + LimitClause limit = HsqlDbDialect.INSTANCE.limit(); + + assertThat(limit.getOffset(10)).isEqualTo("OFFSET 10"); + } + + @Test // DATAJDBC-386 + public void shouldRenderLimitOffset() { + + LimitClause limit = HsqlDbDialect.INSTANCE.limit(); + + assertThat(limit.getLimitOffset(20, 10)).isEqualTo("OFFSET 10 LIMIT 20"); + } + + @Test // DATAJDBC-386 + public void shouldQuoteIdentifiersUsingBackticks() { + + String abcQuoted = HsqlDbDialect.INSTANCE.getIdentifierProcessing().quote("abc"); + + assertThat(abcQuoted).isEqualTo("\"abc\""); + } +} diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MariaDbDialectUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MariaDbDialectUnitTests.java new file mode 100644 index 00000000..4a2e9705 --- /dev/null +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MariaDbDialectUnitTests.java @@ -0,0 +1,69 @@ +/* + * 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.relational.core.dialect; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Test; + +/** + * Unit tests for {@link MariaDbDialect}. + * + * @author Jens Schauder + */ +public class MariaDbDialectUnitTests { + + @Test // DATAJDBC-278 + public void shouldNotSupportArrays() { + + ArrayColumns arrayColumns = MariaDbDialect.INSTANCE.getArraySupport(); + + assertThat(arrayColumns.isSupported()).isFalse(); + } + + @Test // DATAJDBC-278 + public void shouldRenderLimit() { + + LimitClause limit = MariaDbDialect.INSTANCE.limit(); + + assertThat(limit.getClausePosition()).isEqualTo(LimitClause.Position.AFTER_ORDER_BY); + assertThat(limit.getLimit(10)).isEqualTo("LIMIT 10"); + } + + @Test // DATAJDBC-278 + public void shouldRenderOffset() { + + LimitClause limit = MariaDbDialect.INSTANCE.limit(); + + assertThat(limit.getOffset(10)).isEqualTo("LIMIT 10, 18446744073709551615"); + } + + @Test // DATAJDBC-278 + public void shouldRenderLimitOffset() { + + LimitClause limit = MariaDbDialect.INSTANCE.limit(); + + assertThat(limit.getLimitOffset(20, 10)).isEqualTo("LIMIT 10, 20"); + } + + @Test // DATAJDBC-386 + public void shouldQuoteIdentifiersUsingBackticks() { + + String abcQuoted = MariaDbDialect.INSTANCE.getIdentifierProcessing().quote("abc"); + + assertThat(abcQuoted).isEqualTo("`abc`"); + } +} diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java index 4bb6b5ee..5e6d94ac 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/dialect/MySqlDialectUnitTests.java @@ -59,4 +59,12 @@ public class MySqlDialectUnitTests { assertThat(limit.getLimitOffset(20, 10)).isEqualTo("LIMIT 10, 20"); } + + @Test // DATAJDBC-386 + public void shouldQuoteIdentifiersUsingBackticks() { + + String abcQuoted = MySqlDialect.INSTANCE.getIdentifierProcessing().quote("abc"); + + assertThat(abcQuoted).isEqualTo("`abc`"); + } } diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java index c9855910..b7273392 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/BasicRelationalPersistentPropertyUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.core.mapping; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import lombok.Data; @@ -82,9 +83,9 @@ public class BasicRelationalPersistentPropertyUnitTests { @Test // DATAJDBC-106 public void detectsAnnotatedColumnName() { - assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo("dummy_name"); + assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo(quoted("dummy_name")); assertThat(entity.getRequiredPersistentProperty("localDateTime").getColumnName()) - .isEqualTo("dummy_last_updated_at"); + .isEqualTo(quoted("dummy_last_updated_at")); } @Test // DATAJDBC-218 @@ -92,8 +93,8 @@ public class BasicRelationalPersistentPropertyUnitTests { RelationalPersistentProperty listProperty = entity.getRequiredPersistentProperty("someList"); - assertThat(listProperty.getReverseColumnName()).isEqualTo("dummy_column_name"); - assertThat(listProperty.getKeyColumn()).isEqualTo("dummy_key_column_name"); + assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("dummy_column_name")); + assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("dummy_key_column_name")); } @Test // DATAJDBC-111 @@ -148,10 +149,10 @@ public class BasicRelationalPersistentPropertyUnitTests { .assertThat(p.isCollectionLike() && !p.isEntity()).describedAs(s + " contains either simple types or entities") .isNotEqualTo(p.isCollectionLike() && p.isEntity()); - checkEitherOr.accept(listOfString,"listOfString"); - checkEitherOr.accept(arrayOfString,"arrayOfString"); - checkEitherOr.accept(listOfEntity,"listOfEntity"); - checkEitherOr.accept(arrayOfEntity,"arrayOfEntity"); + checkEitherOr.accept(listOfString, "listOfString"); + checkEitherOr.accept(arrayOfString, "arrayOfString"); + checkEitherOr.accept(listOfEntity, "listOfEntity"); + checkEitherOr.accept(arrayOfEntity, "arrayOfEntity"); softly.assertThat(arrayOfString.getColumnType()).isEqualTo(String[].class); softly.assertThat(listOfString.getColumnType()).isEqualTo(String[].class); @@ -183,7 +184,8 @@ public class BasicRelationalPersistentPropertyUnitTests { private final List listOfEntity; private final OtherEntity[] arrayOfEntity; - @MappedCollection(idColumn = "dummy_column_name", keyColumn = "dummy_key_column_name") private List someList; + @MappedCollection(idColumn = "dummy_column_name", + keyColumn = "dummy_key_column_name") private List someList; // DATACMNS-106 private @Column("dummy_name") String name; diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/NamingStrategyUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/NamingStrategyUnitTests.java index 852b9826..2599dbd4 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/NamingStrategyUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/NamingStrategyUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.core.mapping; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import java.time.LocalDateTime; import java.util.List; @@ -23,6 +24,7 @@ import java.util.List; import org.junit.Test; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.RelationalPersistentEntityImplUnitTests.DummySubEntity; +import org.springframework.data.relational.domain.SqlIdentifier; /** * Unit tests for the {@link NamingStrategy}. @@ -40,51 +42,53 @@ public class NamingStrategyUnitTests { @Test public void getTableName() { - assertThat(target.getTableName(persistentEntity.getType())).isEqualTo("dummy_entity"); - assertThat(target.getTableName(DummySubEntity.class)).isEqualTo("dummy_sub_entity"); + assertThat(target.getTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity")); + assertThat(target.getTableName(DummySubEntity.class)).isEqualTo(quoted("dummy_sub_entity")); } @Test public void getColumnName() { - assertThat(target.getColumnName(persistentEntity.getPersistentProperty("id"))).isEqualTo("id"); - assertThat(target.getColumnName(persistentEntity.getPersistentProperty("createdAt"))).isEqualTo("created_at"); + assertThat(target.getColumnName(persistentEntity.getPersistentProperty("id"))).isEqualTo(quoted("id")); + assertThat(target.getColumnName(persistentEntity.getPersistentProperty("createdAt"))) + .isEqualTo(quoted("created_at")); assertThat(target.getColumnName(persistentEntity.getPersistentProperty("dummySubEntities"))) - .isEqualTo("dummy_sub_entities"); + .isEqualTo(quoted("dummy_sub_entities")); } @Test public void getReverseColumnName() { assertThat(target.getReverseColumnName(persistentEntity.getPersistentProperty("dummySubEntities"))) - .isEqualTo("dummy_entity"); + .isEqualTo(quoted("dummy_entity")); } @Test public void getKeyColumn() { assertThat(target.getKeyColumn(persistentEntity.getPersistentProperty("dummySubEntities"))) - .isEqualTo("dummy_entity_key"); + .isEqualTo(quoted("dummy_entity_key")); } @Test public void getSchema() { - assertThat(target.getSchema()).isEmpty(); + assertThat(target.getSchema()).isEqualTo(SqlIdentifier.EMPTY); } @Test public void getQualifiedTableName() { - assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo("dummy_entity"); + assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity")); NamingStrategy strategy = new NamingStrategy() { @Override - public String getSchema() { - return "schema"; + public SqlIdentifier getSchema() { + return quoted("schema"); } }; - assertThat(strategy.getQualifiedTableName(persistentEntity.getType())).isEqualTo("schema.dummy_entity"); + assertThat(strategy.getQualifiedTableName(persistentEntity.getType())) + .isEqualTo(quoted("schema").concat(quoted("dummy_entity"))); } static class DummyEntity { diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java index 74b644b3..e3fcd4f3 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/RelationalPersistentEntityImplUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.core.mapping; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import org.junit.Test; import org.springframework.data.annotation.Id; @@ -36,7 +37,7 @@ public class RelationalPersistentEntityImplUnitTests { RelationalPersistentEntity entity = mappingContext.getPersistentEntity(DummySubEntity.class); - assertThat(entity.getTableName()).isEqualTo("dummy_sub_entity"); + assertThat(entity.getTableName()).isEqualTo(quoted("dummy_sub_entity")); } @Test // DATAJDBC-294 @@ -44,7 +45,7 @@ public class RelationalPersistentEntityImplUnitTests { RelationalPersistentEntity entity = mappingContext.getPersistentEntity(DummySubEntity.class); - assertThat(entity.getIdColumn()).isEqualTo("renamedId"); + assertThat(entity.getIdColumn()).isEqualTo(quoted("renamedId")); } @Test // DATAJDBC-296 @@ -52,7 +53,7 @@ public class RelationalPersistentEntityImplUnitTests { RelationalPersistentEntity entity = mappingContext.getPersistentEntity(DummyEntityWithEmptyAnnotation.class); - assertThat(entity.getTableName()).isEqualTo("dummy_entity_with_empty_annotation"); + assertThat(entity.getTableName()).isEqualTo(quoted("dummy_entity_with_empty_annotation")); } @Table("dummy_sub_entity") diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/SqlIdentifierUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/SqlIdentifierUnitTests.java new file mode 100644 index 00000000..909cef12 --- /dev/null +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/SqlIdentifierUnitTests.java @@ -0,0 +1,123 @@ +/* + * 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.relational.core.mapping; + +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; + +import org.assertj.core.api.SoftAssertions; +import org.junit.Test; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; +import org.springframework.data.relational.domain.SqlIdentifier; + +/** + * Unit tests for SqlIdentifier. + * + * @author Jens Schauder + */ +public class SqlIdentifierUnitTests { + + public static final DefaultIdentifierProcessing BRACKETS_LOWER_CASE = new DefaultIdentifierProcessing( + new Quoting("[", "]"), LetterCasing.LOWER_CASE); + + @Test // DATAJDBC-386 + public void quotedSimpleObjectIdentifier() { + + SimpleSqlIdentifier identifier = quoted("someName"); + + assertThat(identifier.toSql(BRACKETS_LOWER_CASE)).isEqualTo("[someName]"); + assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("someName"); + + } + + @Test // DATAJDBC-386 + public void unquotedSimpleObjectIdentifier() { + + SimpleSqlIdentifier identifier = unquoted("someName"); + String sql = identifier.toSql(BRACKETS_LOWER_CASE); + + assertThat(sql).isEqualTo("someName"); + assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("someName"); + } + + @Test // DATAJDBC-386 + public void quotedSimpleObjectIdentifierWithAdjustableLetterCasing() { + + SimpleSqlIdentifier identifier = quoted("someName").withAdjustableLetterCasing(); + + assertThat(identifier.toSql(BRACKETS_LOWER_CASE)).isEqualTo("[somename]"); + assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("somename"); + + } + + @Test // DATAJDBC-386 + public void unquotedSimpleObjectIdentifierWithAdjustableLetterCasing() { + + SimpleSqlIdentifier identifier = unquoted("someName").withAdjustableLetterCasing(); + String sql = identifier.toSql(BRACKETS_LOWER_CASE); + + assertThat(sql).isEqualTo("somename"); + assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("somename"); + } + + @Test // DATAJDBC-386 + public void quotedMultipartObjectIdentifierWithAdjustableLetterCase() { + + SqlIdentifier identifier = quoted("some").withAdjustableLetterCasing() + .concat(quoted("name").withAdjustableLetterCasing()); + String sql = identifier.toSql(IdentifierProcessing.ANSI); + + assertThat(sql).isEqualTo("\"SOME\".\"NAME\""); + } + + @Test // DATAJDBC-386 + public void quotedMultipartObjectIdentifier() { + + SqlIdentifier identifier = quoted("some").concat(quoted("name")); + String sql = identifier.toSql(IdentifierProcessing.ANSI); + + assertThat(sql).isEqualTo("\"some\".\"name\""); + } + + @Test // DATAJDBC-386 + public void unquotedMultipartObjectIdentifier() { + + SqlIdentifier identifier = unquoted("some").concat(unquoted("name")); + String sql = identifier.toSql(IdentifierProcessing.ANSI); + + assertThat(sql).isEqualTo("some.name"); + } + + @Test // DATAJDBC-386 + public void equality() { + + SqlIdentifier basis = SqlIdentifier.unquoted("simple"); + SqlIdentifier equal = SqlIdentifier.unquoted("simple"); + SqlIdentifier quoted = quoted("simple"); + SqlIdentifier notSimple = SqlIdentifier.unquoted("simple").concat(unquoted("not")); + + SoftAssertions.assertSoftly(softly -> { + + softly.assertThat(basis).isEqualTo(equal); + softly.assertThat(equal).isEqualTo(basis); + softly.assertThat(basis).isNotEqualTo(quoted); + softly.assertThat(basis).isNotEqualTo(notSimple); + }); + } +} diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/domain/DefaultIdentifierProcessingUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/domain/DefaultIdentifierProcessingUnitTests.java new file mode 100644 index 00000000..e153abd2 --- /dev/null +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/domain/DefaultIdentifierProcessingUnitTests.java @@ -0,0 +1,49 @@ +/* + * 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.relational.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.Test; +import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing; +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; + +/** + * unit tests for {@link DefaultIdentifierProcessing}. + * + * @author Jens Schauder + */ +public class DefaultIdentifierProcessingUnitTests { + + @Test // DATAJDBC-386 + public void ansiConformProcessing() { + + DefaultIdentifierProcessing processing = new DefaultIdentifierProcessing(Quoting.ANSI, LetterCasing.UPPER_CASE); + + assertThat(processing.quote("something")).isEqualTo("\"something\""); + assertThat(processing.standardizeLetterCase("aBc")).isEqualTo("ABC"); + } + + @Test // DATAJDBC-386 + public void twoCharacterAsIs() { + + DefaultIdentifierProcessing processing = new DefaultIdentifierProcessing(new Quoting("[", "]"), LetterCasing.AS_IS); + + assertThat(processing.quote("something")).isEqualTo("[something]"); + assertThat(processing.standardizeLetterCase("aBc")).isEqualTo("aBc"); + } +} diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/domain/IdentifierUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/domain/IdentifierUnitTests.java index c9363f23..340187be 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/domain/IdentifierUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/domain/IdentifierUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.domain; import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.relational.domain.SqlIdentifier.*; import java.util.ArrayList; import java.util.Collections; @@ -35,50 +36,55 @@ public class IdentifierUnitTests { @Test // DATAJDBC-326 public void getParametersByName() { - Identifier identifier = Identifier.of("aName", "aValue", String.class); + Identifier identifier = Identifier.of(unquoted("aName"), "aValue", String.class); - assertThat(identifier.toMap()).hasSize(1).containsEntry("aName", "aValue"); + assertThat(identifier.toMap()).hasSize(1).containsEntry(unquoted("aName"), "aValue"); } @Test // DATAJDBC-326 public void parametersWithStringKeysUseObjectAsTypeForNull() { - HashMap parameters = new HashMap<>(); - parameters.put("one", null); + HashMap parameters = new HashMap<>(); + parameters.put(unquoted("one"), null); Identifier identifier = Identifier.from(parameters); assertThat(identifier.getParts()) // .extracting("name", "value", "targetType") // .containsExactly( // - tuple("one", null, Object.class) // + tuple(unquoted("one"), null, Object.class) // ); } @Test // DATAJDBC-326 public void createsIdentifierFromMap() { - Identifier identifier = Identifier.from(Collections.singletonMap("aName", "aValue")); + Identifier identifier = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue")); - assertThat(identifier.toMap()).hasSize(1).containsEntry("aName", "aValue"); + assertThat(identifier.toMap()).hasSize(1).containsEntry(unquoted("aName"), "aValue"); } @Test // DATAJDBC-326 public void withAddsNewEntries() { - Identifier identifier = Identifier.from(Collections.singletonMap("aName", "aValue")).withPart("foo", "bar", - String.class); + Identifier identifier = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue")) + .withPart(unquoted("foo"), "bar", String.class); - assertThat(identifier.toMap()).hasSize(2).containsEntry("aName", "aValue").containsEntry("foo", "bar"); + assertThat(identifier.toMap()) // + .hasSize(2) // + .containsEntry(unquoted("aName"), "aValue") // + .containsEntry(unquoted("foo"), "bar"); } @Test // DATAJDBC-326 public void withOverridesExistingEntries() { - Identifier identifier = Identifier.from(Collections.singletonMap("aName", "aValue")).withPart("aName", "bar", - String.class); + Identifier identifier = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue")) + .withPart(unquoted("aName"), "bar", String.class); - assertThat(identifier.toMap()).hasSize(1).containsEntry("aName", "bar"); + assertThat(identifier.toMap()) // + .hasSize(1) // + .containsEntry(unquoted("aName"), "bar"); } @Test // DATAJDBC-326 @@ -86,7 +92,8 @@ public class IdentifierUnitTests { List keys = new ArrayList<>(); - Identifier.from(Collections.singletonMap("aName", "aValue")).forEach((name, value, targetType) -> keys.add(name)); + Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue")) + .forEach((name, value, targetType) -> keys.add(name.toSql(IdentifierProcessing.ANSI))); assertThat(keys).containsOnly("aName"); } @@ -94,9 +101,9 @@ public class IdentifierUnitTests { @Test // DATAJDBC-326 public void equalsConsidersEquality() { - Identifier one = Identifier.from(Collections.singletonMap("aName", "aValue")); - Identifier two = Identifier.from(Collections.singletonMap("aName", "aValue")); - Identifier three = Identifier.from(Collections.singletonMap("aName", "different")); + Identifier one = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue")); + Identifier two = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue")); + Identifier three = Identifier.from(Collections.singletonMap(unquoted("aName"), "different")); assertThat(one).isEqualTo(two); assertThat(one).isNotEqualTo(three);