diff --git a/pom.xml b/pom.xml index c7d878511b..a248105651 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 1.2.0.BUILD-SNAPSHOT + 1.2.0.DATAJDBC-386-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index f2bbb72319..a932f78c27 100644 --- a/spring-data-jdbc-distribution/pom.xml +++ b/spring-data-jdbc-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 1.2.0.BUILD-SNAPSHOT + 1.2.0.DATAJDBC-386-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index 0904eb8136..f6d2cf9cb1 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -5,7 +5,7 @@ 4.0.0 spring-data-jdbc - 1.2.0.BUILD-SNAPSHOT + 1.2.0.DATAJDBC-386-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 1.2.0.BUILD-SNAPSHOT + 1.2.0.DATAJDBC-386-SNAPSHOT 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 439724e3d8..0bc2a6e921 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 @@ private Object getIdFrom(DbAction.WithEntity idOwningAction) { .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 703ec5c552..4512c40f9d 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.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 @@ private Object readFrom(RelationalPersistentProperty property) { return readEntityFrom(property, path); } - Object value = getObjectFromResultSet(path.extendBy(property).getColumnAlias()); + Object value = getObjectFromResultSet( + path.extendBy(property).getColumnAlias().getReference(identifierProcessing)); return readValue(value, property.getTypeInformation()); } @@ -428,7 +432,8 @@ private Object readEntityFrom(RelationalPersistentProperty property, PersistentP if (idProperty != null) { idValue = newContext.readFrom(idProperty); } else { - idValue = newContext.getObjectFromResultSet(path.extendBy(property).getReverseColumnNameAlias()); + idValue = newContext.getObjectFromResultSet( + path.extendBy(property).getReverseColumnNameAlias().getReference(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 dee263106a..0deff63fc1 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 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 CascadingDataAccessStrategy(List strategies) { * @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 fde16ad40c..dc612fc71d 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.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 @@ default Object insert(T instance, Class domainType, Identifier identifier 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 @@ default Object insert(T instance, Class domainType, Identifier identifier * @param previousVersion The previous version assigned to the instance being saved. * @param the type of the instance to save. * @return whether the update actually updated a row. - * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the optimistic locking version check failed. + * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the + * optimistic locking version check failed. * @since 2.0 */ boolean updateWithVersion(T instance, Class domainType, Number previousVersion); @@ -113,7 +116,8 @@ default Object insert(T instance, Class domainType, Identifier identifier * @param domainType the type of entity to be deleted. Implicitly determines the table to operate on. Must not be * {@code null}. * @param previousVersion The previous version assigned to the instance being saved. - * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the optimistic locking version check failed. + * @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the + * optimistic locking version check failed. * @since 2.0 */ void deleteWithVersion(Object id, Class domainType, Number previousVersion); 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 298fd6ac6f..12179bd1f1 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.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 DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, Relation * @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map) */ @Override - public Object insert(T instance, Class domainType, Map additionalParameters) { + @Nullable + public Object insert(T instance, Class domainType, Map additionalParameters) { return insert(instance, domainType, Identifier.from(additionalParameters)); } @@ -109,11 +109,11 @@ public Object insert(T instance, Class domainType, Map ad @Override public Object insert(T instance, Class domainType, Identifier identifier) { - KeyHolder holder = new GeneratedKeyHolder(); + SqlGenerator sqlGenerator = sql(domainType); RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType); - MapSqlParameterSource parameterSource = getParameterSource(instance, persistentEntity, "", - PersistentProperty::isIdProperty); + SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "", + PersistentProperty::isIdProperty, getIdentifierProcessing()); identifier.forEach((name, value, type) -> addConvertedPropertyValue(parameterSource, name, value, type)); @@ -124,8 +124,10 @@ public Object insert(T instance, Class domainType, Identifier identifier) addConvertedPropertyValue(parameterSource, idProperty, idValue, idProperty.getColumnName()); } + KeyHolder holder = new GeneratedKeyHolder(); + operations.update( // - sql(domainType).getInsert(new HashSet<>(Arrays.asList(parameterSource.getParameterNames()))), // + sqlGenerator.getInsert(new HashSet<>(parameterSource.getIdentifiers())), // parameterSource, // holder // ); @@ -142,7 +144,7 @@ public boolean update(S instance, Class domainType) { RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType); return operations.update(sql(domainType).getUpdate(), - getParameterSource(instance, persistentEntity, "", Predicates.includeAll())) != 0; + getParameterSource(instance, persistentEntity, "", Predicates.includeAll(), getIdentifierProcessing())) != 0; } /* @@ -155,13 +157,14 @@ public boolean updateWithVersion(S instance, Class domainType, Number pre RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType); // Adjust update statement to set the new version and use the old version in where clause. - MapSqlParameterSource parameterSource = getParameterSource(instance, persistentEntity, "", - Predicates.includeAll()); - parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, previousVersion); + SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "", + Predicates.includeAll(), getIdentifierProcessing()); + parameterSource.addValue(VERSION_SQL_PARAMETER, previousVersion); int affectedRows = operations.update(sql(domainType).getUpdateWithVersion(), parameterSource); if (affectedRows == 0) { + throw new OptimisticLockingFailureException( String.format("Optimistic lock exception on saving entity of type %s.", persistentEntity.getName())); } @@ -177,7 +180,7 @@ public boolean updateWithVersion(S instance, Class domainType, Number pre public void delete(Object id, Class domainType) { String deleteByIdSql = sql(domainType).getDeleteById(); - MapSqlParameterSource parameter = createIdParameterSource(id, domainType); + SqlParameterSource parameter = createIdParameterSource(id, domainType); operations.update(deleteByIdSql, parameter); } @@ -193,8 +196,8 @@ public void deleteWithVersion(Object id, Class domainType, Number previou RelationalPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType); - MapSqlParameterSource parameterSource = createIdParameterSource(id, domainType); - parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, previousVersion); + SqlIdentifierParameterSource parameterSource = createIdParameterSource(id, domainType); + parameterSource.addValue(VERSION_SQL_PARAMETER, previousVersion); int affectedRows = operations.update(sql(domainType).getDeleteByIdAndVersion(), parameterSource); if (affectedRows == 0) { @@ -216,11 +219,11 @@ public void delete(Object rootId, PersistentPropertyPath parameters = new HashMap<>(); - parameters.put("rootId", rootId); - operations.update(format, parameters); + SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(getIdentifierProcessing()); + parameters.addValue(ROOT_ID_PARAMETER, rootId); + operations.update(delete, parameters); } /* @@ -265,7 +268,7 @@ public long count(Class domainType) { public T findById(Object id, Class domainType) { String findOneSql = sql(domainType).getFindOne(); - MapSqlParameterSource parameter = createIdParameterSource(id, domainType); + SqlIdentifierParameterSource parameter = createIdParameterSource(id, domainType); try { return operations.queryForObject(findOneSql, parameter, (RowMapper) getEntityRowMapper(domainType)); @@ -297,9 +300,9 @@ public Iterable findAllById(Iterable ids, Class domainType) { } RelationalPersistentProperty idProperty = getRequiredPersistentEntity(domainType).getRequiredIdProperty(); - MapSqlParameterSource parameterSource = new MapSqlParameterSource(); + SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(getIdentifierProcessing()); - addConvertedPropertyValuesAsList(parameterSource, idProperty, ids, "ids"); + addConvertedPropertyValuesAsList(parameterSource, idProperty, ids, IDS_SQL_PARAMETER); String findAllInListSql = sql(domainType).getFindAllInList(); @@ -324,12 +327,20 @@ public Iterable findAllByPath(Identifier identifier, String findAllByProperty = sql(actualType) // .getFindAllByProperty(identifier, path.getQualifierColumn(), path.isOrdered()); - MapSqlParameterSource parameters = new MapSqlParameterSource(identifier.toMap()); - RowMapper rowMapper = path.isMap() ? this.getMapEntityRowMapper(path, identifier) : this.getEntityRowMapper(path, identifier); - return operations.query(findAllByProperty, parameters, (RowMapper) rowMapper); + return operations.query(findAllByProperty, createParameterSource(identifier, getIdentifierProcessing()), + (RowMapper) rowMapper); + } + + private SqlParameterSource createParameterSource(Identifier identifier, IdentifierProcessing identifierProcessing) { + + SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(identifierProcessing); + + identifier.toMap().forEach(parameterSource::addValue); + + return parameterSource; } /* @@ -355,7 +366,7 @@ public Iterable findAllByProperty(Object rootId, RelationalPersistentPro public boolean existsById(Object id, Class domainType) { String existsSql = sql(domainType).getExists(); - MapSqlParameterSource parameter = createIdParameterSource(id, domainType); + SqlParameterSource parameter = createIdParameterSource(id, domainType); Boolean result = operations.queryForObject(existsSql, parameter, Boolean.class); Assert.state(result != null, "The result of an exists query must not be null"); @@ -363,10 +374,11 @@ public boolean existsById(Object id, Class domainType) { 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 @@ private MapSqlParameterSource getParameterSource(@Nullable S instance, Re Object value = propertyAccessor.getProperty(property); RelationalPersistentEntity embeddedEntity = context.getPersistentEntity(property.getType()); - MapSqlParameterSource additionalParameters = getParameterSource((T) value, - (RelationalPersistentEntity) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty); - parameters.addValues(additionalParameters.getValues()); + SqlIdentifierParameterSource additionalParameters = getParameterSource((T) value, + (RelationalPersistentEntity) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty, + identifierProcessing); + parameters.addAll(additionalParameters); } else { Object value = propertyAccessor.getProperty(property); - String paramName = prefix + property.getColumnName(); + SqlIdentifier paramName = property.getColumnName().transform(prefix::concat); addConvertedPropertyValue(parameters, property, value, paramName); } @@ -434,7 +447,7 @@ private Object getIdFromHolder(KeyHolder holder, RelationalPersistentEntity< return null; } - return keys.get(persistentEntity.getIdColumn()); + return keys.get(persistentEntity.getIdColumn().getReference(getIdentifierProcessing())); } } @@ -448,55 +461,58 @@ private EntityRowMapper getEntityRowMapper(PersistentPropertyPathExtension pa private RowMapper getMapEntityRowMapper(PersistentPropertyPathExtension path, Identifier identifier) { - String keyColumn = path.getQualifierColumn(); + SqlIdentifier keyColumn = path.getQualifierColumn(); Assert.notNull(keyColumn, () -> "KeyColumn must not be null for " + path); - return new MapEntityRowMapper<>(path, converter, identifier, keyColumn); + return new MapEntityRowMapper<>(path, converter, identifier, keyColumn, getIdentifierProcessing()); } - private MapSqlParameterSource createIdParameterSource(Object id, Class domainType) { + private SqlIdentifierParameterSource createIdParameterSource(Object id, Class domainType) { - MapSqlParameterSource parameterSource = new MapSqlParameterSource(); + SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(getIdentifierProcessing()); addConvertedPropertyValue( // parameterSource, // getRequiredPersistentEntity(domainType).getRequiredIdProperty(), // id, // - "id" // + ID_SQL_PARAMETER // ); return parameterSource; } - private void addConvertedPropertyValue(MapSqlParameterSource parameterSource, RelationalPersistentProperty property, - @Nullable Object value, String paramName) { + private IdentifierProcessing getIdentifierProcessing() { + return sqlGeneratorSource.getDialect().getIdentifierProcessing(); + } - JdbcValue jdbcValue = converter.writeJdbcValue( // - value, // - property.getColumnType(), // - property.getSqlType() // - ); + private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, + RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) { - parameterSource.addValue(paramName, jdbcValue.getValue(), JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType())); + addConvertedValue(parameterSource, value, name, property.getColumnType(), property.getSqlType()); } - private void addConvertedPropertyValue(MapSqlParameterSource parameterSource, String name, Object value, - Class type) { + private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value, + Class javaType) { + + addConvertedValue(parameterSource, value, name, javaType, JdbcUtil.sqlTypeFor(javaType)); + } + + private void addConvertedValue(SqlIdentifierParameterSource parameterSource, @Nullable Object value, + SqlIdentifier paramName, Class javaType, int sqlType) { JdbcValue jdbcValue = converter.writeJdbcValue( // value, // - type, // - JdbcUtil.sqlTypeFor(type) // + javaType, // + sqlType // ); parameterSource.addValue( // - name, // + paramName, // jdbcValue.getValue(), // - JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType()) // - ); + JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType())); } - private void addConvertedPropertyValuesAsList(MapSqlParameterSource parameterSource, - RelationalPersistentProperty property, Iterable values, String paramName) { + private void addConvertedPropertyValuesAsList(SqlIdentifierParameterSource parameterSource, + RelationalPersistentProperty property, Iterable values, SqlIdentifier paramName) { List convertedIds = new ArrayList<>(); JdbcValue jdbcValue = null; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java index d98536aa05..2b3cd5754a 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 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 boolean updateWithVersion(S instance, Class domainType, Number nex return delegate.updateWithVersion(instance, domainType, nextVersion); } + /* * (non-Javadoc) * @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, org.springframework.data.mapping.PersistentPropertyPath) 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 2125a1b2a5..27dc8b755b 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MapEntityRowMapper.java @@ -24,6 +24,8 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension; import org.springframework.data.relational.domain.Identifier; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.jdbc.core.RowMapper; import org.springframework.lang.NonNull; @@ -40,13 +42,14 @@ class MapEntityRowMapper implements RowMapper> { private final PersistentPropertyPathExtension path; private final JdbcConverter converter; private final Identifier identifier; - - private final String keyColumn; + private final SqlIdentifier keyColumn; + private final IdentifierProcessing identifierProcessing; @NonNull @Override public Map.Entry mapRow(ResultSet rs, int rowNum) throws SQLException { - Object key = rs.getObject(keyColumn); + + Object key = rs.getObject(keyColumn.getReference(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 2a65c4b2e5..35673fb5c3 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlContext.java @@ -20,6 +20,8 @@ import org.springframework.data.relational.core.sql.Column; import org.springframework.data.relational.core.sql.SQL; import org.springframework.data.relational.core.sql.Table; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.SqlIdentifier; /** * Utility to get from path to SQL DSL elements. @@ -33,36 +35,41 @@ class SqlContext { private final RelationalPersistentEntity entity; private final Table table; + private final IdentifierProcessing identifierProcessing; - SqlContext(RelationalPersistentEntity entity) { + SqlContext(RelationalPersistentEntity entity, IdentifierProcessing identifierProcessing) { + + this.identifierProcessing = identifierProcessing; this.entity = entity; - this.table = SQL.table(entity.getTableName()); + this.table = SQL.table(entity.getTableName().toSql(this.identifierProcessing)); } Column getIdColumn() { - return table.column(entity.getIdColumn()); + return table.column(entity.getIdColumn().toSql(identifierProcessing)); } Column getVersionColumn() { - return table.column(entity.getRequiredVersionProperty().getColumnName()); + return table.column(entity.getRequiredVersionProperty().getColumnName().toSql(identifierProcessing)); } - + Table getTable() { return table; } Table getTable(PersistentPropertyPathExtension path) { - String tableAlias = path.getTableAlias(); - Table table = SQL.table(path.getTableName()); - return tableAlias == null ? table : table.as(tableAlias); + SqlIdentifier tableAlias = path.getTableAlias(); + Table table = SQL.table(path.getTableName().toSql(identifierProcessing)); + return tableAlias == null ? table : table.as(tableAlias.toSql(identifierProcessing)); } Column getColumn(PersistentPropertyPathExtension path) { - return getTable(path).column(path.getColumnName()).as(path.getColumnAlias()); + return getTable(path).column(path.getColumnName().toSql(identifierProcessing)) + .as(path.getColumnAlias().toSql(identifierProcessing)); } Column getReverseColumn(PersistentPropertyPathExtension path) { - return getTable(path).column(path.getReverseColumnName()).as(path.getReverseColumnNameAlias()); + return getTable(path).column(path.getReverseColumnName().toSql(identifierProcessing)) + .as(path.getReverseColumnNameAlias().toSql(identifierProcessing)); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java index 069fd457c6..facc15822e 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 @@ -20,11 +20,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -40,6 +42,8 @@ import org.springframework.data.relational.core.sql.*; import org.springframework.data.relational.core.sql.render.SqlRenderer; import org.springframework.data.relational.domain.Identifier; +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.util.Lazy; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -57,11 +61,15 @@ */ 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; @@ -85,12 +93,15 @@ class SqlGenerator { * * @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 +114,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 +126,10 @@ private static Condition getSubselectCondition(PersistentPropertyPathExtension p return rootCondition.apply(filterColumn); } - Table subSelectTable = SQL.table(parentPath.getTableName()); - Column idColumn = subSelectTable.column(parentPath.getIdColumnName()); - Column selectFilterColumn = subSelectTable.column(parentPath.getEffectiveIdColumnName()); + Table subSelectTable = SQL.table(parentPath.getTableName().toSql(identifierProcessing)); + Column idColumn = subSelectTable.column(parentPath.getIdColumnName().toSql(identifierProcessing)); + Column selectFilterColumn = subSelectTable + .column(parentPath.getEffectiveIdColumnName().toSql(identifierProcessing)); Condition innerCondition; @@ -139,8 +151,8 @@ private static Condition getSubselectCondition(PersistentPropertyPathExtension p return filterColumn.in(select); } - private static BindMarker getBindMarker(String columnName) { - return SQL.bindMarker(":" + parameterPattern.matcher(columnName).replaceAll("")); + private BindMarker getBindMarker(SqlIdentifier columnName) { + return SQL.bindMarker(":" + parameterPattern.matcher(columnName.getReference(identifierProcessing)).replaceAll("")); } /** @@ -174,7 +186,7 @@ String getFindAll() { * keyColumn must not be {@code null}. * @return a SQL String. */ - String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyColumn, boolean ordered) { + String getFindAllByProperty(Identifier parentIdentifier, @Nullable SqlIdentifier keyColumn, boolean ordered) { Assert.isTrue(keyColumn != null || !ordered, "If the SQL statement should be ordered a keyColumn to order by must be provided."); @@ -182,7 +194,7 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyCol SelectBuilder.SelectWhere builder = selectBuilder( // keyColumn == null // ? Collections.emptyList() // - : Collections.singleton(keyColumn) // + : Collections.singleton(keyColumn.toSql(identifierProcessing)) // ); Table table = getTable(); @@ -191,7 +203,9 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyCol SelectBuilder.SelectWhereAndOr withWhereClause = builder.where(condition); Select select = ordered // - ? withWhereClause.orderBy(table.column(keyColumn).as(keyColumn)).build() // + ? withWhereClause + .orderBy(table.column(keyColumn.toSql(identifierProcessing)).as(keyColumn.toSql(identifierProcessing))) + .build() // : withWhereClause.build(); return render(select); @@ -200,9 +214,10 @@ String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyCol private Condition buildConditionForBackReference(Identifier parentIdentifier, Table table) { Condition condition = null; - for (String backReferenceColumn : parentIdentifier.toMap().keySet()) { + for (SqlIdentifier backReferenceColumn : parentIdentifier.toMap().keySet()) { - Condition newCondition = table.column(backReferenceColumn).isEqualTo(getBindMarker(backReferenceColumn)); + Condition newCondition = table.column(backReferenceColumn.toSql(identifierProcessing)) + .isEqualTo(getBindMarker(backReferenceColumn)); condition = condition == null ? newCondition : condition.and(newCondition); } @@ -214,7 +229,7 @@ private Condition buildConditionForBackReference(Identifier parentIdentifier, Ta /** * Create a {@code SELECT COUNT(id) FROM … WHERE :id = …} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getExists() { return existsSql.get(); @@ -223,7 +238,7 @@ String getExists() { /** * Create a {@code SELECT … FROM … WHERE :id = …} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getFindOne() { return findOneSql.get(); @@ -232,16 +247,16 @@ String getFindOne() { /** * Create a {@code INSERT INTO … (…) VALUES(…)} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ - String getInsert(Set additionalColumns) { + String getInsert(Set additionalColumns) { return createInsertSql(additionalColumns); } /** * Create a {@code UPDATE … SET …} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getUpdate() { return updateSql.get(); @@ -250,7 +265,7 @@ String getUpdate() { /** * Create a {@code UPDATE … SET … WHERE ID = :id and VERSION_COLUMN = :___oldOptimisticLockingVersion } statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getUpdateWithVersion() { return updateWithVersionSql.get(); @@ -259,7 +274,7 @@ String getUpdateWithVersion() { /** * Create a {@code SELECT COUNT(*) FROM …} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getCount() { return countSql.get(); @@ -268,7 +283,7 @@ String getCount() { /** * Create a {@code DELETE FROM … WHERE :id = …} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getDeleteById() { return deleteByIdSql.get(); @@ -277,7 +292,7 @@ String getDeleteById() { /** * Create a {@code DELETE FROM … WHERE :id = … and :___oldOptimisticLockingVersion = ...} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getDeleteByIdAndVersion() { return deleteByIdAndVersionSql.get(); @@ -286,7 +301,7 @@ String getDeleteByIdAndVersion() { /** * Create a {@code DELETE FROM … WHERE :ids in (…)} statement. * - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String getDeleteByList() { return deleteByListSql.get(); @@ -296,7 +311,7 @@ String getDeleteByList() { * Create a {@code DELETE} query and optionally filter by {@link PersistentPropertyPath}. * * @param path can be {@literal null}. - * @return + * @return the statement as a {@link String}. Guaranteed to be not {@literal null}. */ String createDeleteAllSql(@Nullable PersistentPropertyPath path) { @@ -315,16 +330,16 @@ String createDeleteAllSql(@Nullable PersistentPropertyPath path) { return createDeleteByPathAndCriteria(new PersistentPropertyPathExtension(mappingContext, path), - filterColumn -> filterColumn.isEqualTo(getBindMarker("rootId"))); + filterColumn -> filterColumn.isEqualTo(getBindMarker(ROOT_ID_PARAMETER))); } private String createFindOneSql() { - Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker("id"))) // + Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) // .build(); return render(select); @@ -379,8 +394,8 @@ private SelectBuilder.SelectWhere selectBuilder(Collection keyColumns) { /** * 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 +440,14 @@ Join getJoin(PersistentPropertyPathExtension path) { return new Join( // currentTable, // - currentTable.column(path.getReverseColumnName()), // - parentTable.column(idDefiningParentPath.getIdColumnName()) // + currentTable.column(path.getReverseColumnName().toSql(identifierProcessing)), // + parentTable.column(idDefiningParentPath.getIdColumnName().toSql(identifierProcessing)) // ); } private String createFindAllInListSql() { - Select select = selectBuilder().where(getIdColumn().in(getBindMarker("ids"))).build(); + Select select = selectBuilder().where(getIdColumn().in(getBindMarker(IDS_SQL_PARAMETER))).build(); return render(select); } @@ -444,7 +459,7 @@ private String createExistsSql() { Select select = StatementBuilder // .select(Functions.count(getIdColumn())) // .from(table) // - .where(getIdColumn().isEqualTo(getBindMarker("id"))) // + .where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) // .build(); return render(select); @@ -462,21 +477,22 @@ private String createCountSql() { return render(select); } - private String createInsertSql(Set additionalColumns) { + private String createInsertSql(Set additionalColumns) { Table table = getTable(); - Set columnNamesForInsert = new LinkedHashSet<>(columns.getInsertableColumns()); + Set columnNamesForInsert = new TreeSet<>(Comparator.comparing(id -> id.toSql(identifierProcessing))); + columnNamesForInsert.addAll(columns.getInsertableColumns()); columnNamesForInsert.addAll(additionalColumns); InsertBuilder.InsertIntoColumnsAndValuesWithBuild insert = Insert.builder().into(table); - for (String cn : columnNamesForInsert) { - insert = insert.column(table.column(cn)); + for (SqlIdentifier cn : columnNamesForInsert) { + insert = insert.column(table.column(cn.toSql(identifierProcessing))); } InsertBuilder.InsertValuesWithBuild insertWithValues = null; - for (String cn : columnNamesForInsert) { + for (SqlIdentifier cn : columnNamesForInsert) { insertWithValues = (insertWithValues == null ? insert : insertWithValues).values(getBindMarker(cn)); } @@ -490,7 +506,8 @@ private String createUpdateSql() { private String createUpdateWithVersionSql() { Update update = createBaseUpdate() // - .and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) // + .and(getVersionColumn() + .isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.getReference(identifierProcessing)))) // .build(); return render(update); @@ -503,7 +520,7 @@ private UpdateBuilder.UpdateWhereAndOr createBaseUpdate() { List assignments = columns.getUpdateableColumns() // .stream() // .map(columnName -> Assignments.value( // - table.column(columnName), // + table.column(columnName.toSql(identifierProcessing)), // getBindMarker(columnName))) // .collect(Collectors.toList()); @@ -520,26 +537,28 @@ private String createDeleteSql() { private String createDeleteByIdAndVersionSql() { Delete delete = createBaseDeleteById(getTable()) // - .and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) // + .and(getVersionColumn() + .isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.getReference(identifierProcessing)))) // .build(); return render(delete); } private DeleteBuilder.DeleteWhereAndOr createBaseDeleteById(Table table) { - return Delete.builder().from(table).where(getIdColumn().isEqualTo(SQL.bindMarker(":id"))); + return Delete.builder().from(table) + .where(getIdColumn().isEqualTo(SQL.bindMarker(":" + ID_SQL_PARAMETER.getReference(identifierProcessing)))); } 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 +580,7 @@ private String createDeleteByListSql() { Delete delete = Delete.builder() // .from(table) // - .where(getIdColumn().in(getBindMarker("ids"))) // + .where(getIdColumn().in(getBindMarker(IDS_SQL_PARAMETER))) // .build(); return render(delete); @@ -609,17 +628,18 @@ static class Join { * Value object encapsulating column name caches. * * @author Mark Paluch + * @author Jens Schauder */ static class Columns { private final MappingContext, RelationalPersistentProperty> mappingContext; - private final List columnNames = new ArrayList<>(); - private final List idColumnNames = new ArrayList<>(); - private final List nonIdColumnNames = new ArrayList<>(); - private final Set readOnlyColumnNames = new HashSet<>(); - private final Set insertableColumns; - private final Set updateableColumns; + private final List columnNames = new ArrayList<>(); + private final List idColumnNames = new ArrayList<>(); + private final List nonIdColumnNames = new ArrayList<>(); + private final Set readOnlyColumnNames = new HashSet<>(); + private final Set insertableColumns; + private final Set updateableColumns; Columns(RelationalPersistentEntity entity, MappingContext, RelationalPersistentProperty> mappingContext) { @@ -628,12 +648,12 @@ static class Columns { populateColumnNameCache(entity, ""); - Set insertable = new LinkedHashSet<>(nonIdColumnNames); + Set insertable = new LinkedHashSet<>(nonIdColumnNames); insertable.removeAll(readOnlyColumnNames); this.insertableColumns = Collections.unmodifiableSet(insertable); - Set updateable = new LinkedHashSet<>(columnNames); + Set updateable = new LinkedHashSet<>(columnNames); updateable.removeAll(idColumnNames); updateable.removeAll(readOnlyColumnNames); @@ -656,7 +676,7 @@ private void populateColumnNameCache(RelationalPersistentEntity entity, Strin private void initSimpleColumnName(RelationalPersistentProperty property, String prefix) { - String columnName = prefix + property.getColumnName(); + SqlIdentifier columnName = property.getColumnName().transform(prefix::concat); columnNames.add(columnName); @@ -684,14 +704,14 @@ private void initEmbeddedColumnNames(RelationalPersistentProperty property, Stri /** * @return Column names that can be used for {@code INSERT}. */ - Set getInsertableColumns() { + Set getInsertableColumns() { return insertableColumns; } /** * @return Column names that can be used for {@code UPDATE}. */ - Set getUpdateableColumns() { + Set getUpdateableColumns() { return updateableColumns; } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGeneratorSource.java index de4aa6b4d0..3dae16b3ce 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 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 0000000000..d38ccc17aa --- /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.getReference(identifierProcessing); + namesToValues.put(name, value); + registerSqlType(name, sqlType); + } + + void addAll(SqlIdentifierParameterSource others) { + + for (SqlIdentifier identifier : others.getIdentifiers()) { + + String name = identifier.getReference(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 ea2b810445..69d134ec38 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.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 static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC */ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession, - NamespaceStrategy namespaceStrategy) { + NamespaceStrategy namespaceStrategy, Dialect dialect) { // the DefaultDataAccessStrategy needs a reference to the returned DataAccessStrategy. This creates a dependency // cycle. In order to create it, we need something that allows to defer closing the cycle until all the elements are // created. That is the purpose of the DelegatingAccessStrategy. DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy(); - MyBatisDataAccessStrategy myBatisDataAccessStrategy = new MyBatisDataAccessStrategy(sqlSession, context, converter); + MyBatisDataAccessStrategy myBatisDataAccessStrategy = new MyBatisDataAccessStrategy(sqlSession, + dialect.getIdentifierProcessing()); myBatisDataAccessStrategy.setNamespaceStrategy(namespaceStrategy); CascadingDataAccessStrategy cascadingDataAccessStrategy = new CascadingDataAccessStrategy( asList(myBatisDataAccessStrategy, delegatingDataAccessStrategy)); - SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context); + SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, dialect); DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( // sqlGeneratorSource, // context, // @@ -109,13 +115,17 @@ public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingC * Use a {@link SqlSessionTemplate} for {@link SqlSession} or a similar implementation tying the session to the proper * transaction. Note that the resulting {@link DataAccessStrategy} only handles MyBatis. It does not include the * functionality of the {@link DefaultDataAccessStrategy} which one normally still wants. Use - * {@link #createCombinedAccessStrategy(RelationalMappingContext, JdbcConverter, NamedParameterJdbcOperations, SqlSession, NamespaceStrategy)} + * {@link #createCombinedAccessStrategy(RelationalMappingContext, JdbcConverter, NamedParameterJdbcOperations, SqlSession, NamespaceStrategy, Dialect)} * to create such a {@link DataAccessStrategy}. * * @param sqlSession Must be non {@literal null}. + * @param identifierProcessing the {@link IdentifierProcessing} applied to {@link SqlIdentifier} instances in order to + * turn them into {@link String} */ - public MyBatisDataAccessStrategy(SqlSession sqlSession, RelationalMappingContext context, JdbcConverter converter) { + public MyBatisDataAccessStrategy(SqlSession sqlSession, IdentifierProcessing identifierProcessing) { + this.sqlSession = sqlSession; + this.identifierProcessing = identifierProcessing; } /** @@ -135,9 +145,10 @@ public void setNamespaceStrategy(NamespaceStrategy namespaceStrategy) { * @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map) */ @Override - public Object insert(T instance, Class domainType, Map additionalParameters) { + public Object insert(T instance, Class domainType, Map additionalParameters) { - MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, additionalParameters); + MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, + convertToParameterMap(additionalParameters)); sqlSession().insert(namespace(domainType) + ".insert", myBatisContext); return myBatisContext.getId(); @@ -150,7 +161,8 @@ public Object insert(T instance, Class domainType, Map ad @Override public Object insert(T instance, Class domainType, Identifier identifier) { - MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, identifier.toMap()); + MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, + convertToParameterMap(identifier.toMap())); sqlSession().insert(namespace(domainType) + ".insert", myBatisContext); return myBatisContext.getId(); @@ -174,9 +186,10 @@ public boolean update(S instance, Class domainType) { @Override public boolean updateWithVersion(S instance, Class domainType, Number previousVersion) { - - return sqlSession().update(namespace(domainType) + ".updateWithVersion", - new MyBatisContext(null, instance, domainType, Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion))) != 0; + String statement = namespace(domainType) + ".updateWithVersion"; + MyBatisContext parameter = new MyBatisContext(null, instance, domainType, + Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion)); + return sqlSession().update(statement, parameter) != 0; } /* @@ -186,8 +199,9 @@ public boolean updateWithVersion(S instance, Class domainType, Number pre @Override public void delete(Object id, Class domainType) { - sqlSession().delete(namespace(domainType) + ".delete", - new MyBatisContext(id, null, domainType, Collections.emptyMap())); + String statement = namespace(domainType) + ".delete"; + MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap()); + sqlSession().delete(statement, parameter); } /* @@ -197,8 +211,10 @@ public void delete(Object id, Class domainType) { @Override public void deleteWithVersion(Object id, Class domainType, Number previousVersion) { - sqlSession().delete(namespace(domainType) + ".deleteWithVersion", - new MyBatisContext(id, null, domainType, Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion))); + String statement = namespace(domainType) + ".deleteWithVersion"; + MyBatisContext parameter = new MyBatisContext(id, null, domainType, + Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion)); + sqlSession().delete(statement, parameter); } /* @@ -208,10 +224,12 @@ public void deleteWithVersion(Object id, Class domainType, Number previou @Override public void delete(Object rootId, PersistentPropertyPath propertyPath) { - sqlSession().delete( - namespace(propertyPath.getBaseProperty().getOwner().getType()) + ".delete-" + toDashPath(propertyPath), - new MyBatisContext(rootId, null, propertyPath.getRequiredLeafProperty().getTypeInformation().getType(), - Collections.emptyMap())); + Class ownerType = propertyPath.getBaseProperty().getOwner().getType(); + String statement = namespace(ownerType) + ".delete-" + toDashPath(propertyPath); + Class leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType(); + MyBatisContext parameter = new MyBatisContext(rootId, null, leafType, Collections.emptyMap()); + + sqlSession().delete(statement, parameter); } /* @@ -221,10 +239,9 @@ public void delete(Object rootId, PersistentPropertyPath void deleteAll(Class domainType) { - sqlSession().delete( // - namespace(domainType) + ".deleteAll", // - new MyBatisContext(null, null, domainType, Collections.emptyMap()) // - ); + String statement = namespace(domainType) + ".deleteAll"; + MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap()); + sqlSession().delete(statement, parameter); } /* @@ -237,10 +254,9 @@ public void deleteAll(PersistentPropertyPath prope Class baseType = propertyPath.getBaseProperty().getOwner().getType(); Class leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType(); - sqlSession().delete( // - namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath), // - new MyBatisContext(null, null, leafType, Collections.emptyMap()) // - ); + String statement = namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath); + MyBatisContext parameter = new MyBatisContext(null, null, leafType, Collections.emptyMap()); + sqlSession().delete(statement, parameter); } /* @@ -249,8 +265,10 @@ public void deleteAll(PersistentPropertyPath prope */ @Override public T findById(Object id, Class domainType) { - return sqlSession().selectOne(namespace(domainType) + ".findById", - new MyBatisContext(id, null, domainType, Collections.emptyMap())); + + String statement = namespace(domainType) + ".findById"; + MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap()); + return sqlSession().selectOne(statement, parameter); } /* @@ -259,8 +277,10 @@ public T findById(Object id, Class domainType) { */ @Override public Iterable findAll(Class domainType) { - return sqlSession().selectList(namespace(domainType) + ".findAll", - new MyBatisContext(null, null, domainType, Collections.emptyMap())); + + String statement = namespace(domainType) + ".findAll"; + MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap()); + return sqlSession().selectList(statement, parameter); } /* @@ -299,9 +319,9 @@ public Iterable findAllByPath(Identifier identifier, @Override public Iterable findAllByProperty(Object rootId, RelationalPersistentProperty property) { - return sqlSession().selectList( - namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(), - new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap())); + String statement = namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(); + MyBatisContext parameter = new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap()); + return sqlSession().selectList(statement, parameter); } /* @@ -311,8 +331,9 @@ public Iterable findAllByProperty(Object rootId, RelationalPersistentProp @Override public boolean existsById(Object id, Class domainType) { - return sqlSession().selectOne(namespace(domainType) + ".existsById", - new MyBatisContext(id, null, domainType, Collections.emptyMap())); + String statement = namespace(domainType) + ".existsById"; + MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap()); + return sqlSession().selectOne(statement, parameter); } /* @@ -321,8 +342,16 @@ public boolean existsById(Object id, Class domainType) { */ @Override public long count(Class domainType) { - return sqlSession().selectOne(namespace(domainType) + ".count", - new MyBatisContext(null, null, domainType, Collections.emptyMap())); + + String statement = namespace(domainType) + ".count"; + MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap()); + return sqlSession().selectOne(statement, parameter); + } + + private Map convertToParameterMap(Map additionalParameters) { + + return additionalParameters.entrySet().stream() // + .collect(Collectors.toMap(e -> e.getKey().toSql(identifierProcessing), Map.Entry::getValue)); } private String namespace(Class domainType) { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java index 08148fb7e3..983ac4c150 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java @@ -34,6 +34,8 @@ import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.relational.core.conversion.RelationalConverter; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -71,8 +73,8 @@ public JdbcMappingContext jdbcMappingContext(Optional namingStra } /** - * Creates a {@link RelationalConverter} using the configured {@link #jdbcMappingContext(Optional)}. Will get - * {@link #jdbcCustomConversions()} applied. + * Creates a {@link RelationalConverter} using the configured + * {@link #jdbcMappingContext(Optional, JdbcCustomConversions)}. Will get {@link #jdbcCustomConversions()} applied. * * @see #jdbcMappingContext(Optional, JdbcCustomConversions) * @see #jdbcCustomConversions() @@ -125,7 +127,12 @@ public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationContext applicatio */ @Bean public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter, - RelationalMappingContext context) { - return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, jdbcConverter, operations); + RelationalMappingContext context, Dialect dialect) { + return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, jdbcConverter, operations); + } + + @Bean + Dialect dialect() { + return HsqlDbDialect.INSTANCE; } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java index 6bd600565f..7d8125b7d7 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/JdbcConfiguration.java @@ -34,6 +34,8 @@ import org.springframework.data.jdbc.core.convert.SqlGeneratorSource; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.relational.core.conversion.RelationalConverter; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.dialect.HsqlDbDialect; import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -102,12 +104,15 @@ public JdbcCustomConversions jdbcCustomConversions() { * @param context * @param converter * @param operations + * @param dialect * @return */ @Bean public JdbcAggregateOperations jdbcAggregateOperations(ApplicationEventPublisher publisher, - RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations) { - return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy(context, converter, operations)); + RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations, + Dialect dialect) { + return new JdbcAggregateTemplate(publisher, context, converter, + dataAccessStrategy(context, converter, operations, dialect)); } /** @@ -117,11 +122,17 @@ public JdbcAggregateOperations jdbcAggregateOperations(ApplicationEventPublisher * @param context * @param converter * @param operations + * @param dialect * @return */ @Bean public DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, JdbcConverter converter, - NamedParameterJdbcOperations operations) { - return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, converter, operations); + NamedParameterJdbcOperations operations, Dialect dialect) { + return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, converter, operations); + } + + @Bean + public Dialect dialect() { + return HsqlDbDialect.INSTANCE; } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java index b840977fff..c5cc23d52d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/MyBatisJdbcConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -43,8 +44,8 @@ public class MyBatisJdbcConfiguration extends AbstractJdbcConfiguration { @Bean @Override public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter, - RelationalMappingContext context) { + RelationalMappingContext context, Dialect dialect) { - return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session); + return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session, dialect); } } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java index bbaf86ea0a..efe66fb60c 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java @@ -28,6 +28,7 @@ import org.springframework.data.jdbc.repository.QueryMappingConfiguration; import org.springframework.data.jdbc.repository.RowMapperMap; import org.springframework.data.mapping.callback.EntityCallbacks; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactorySupport; @@ -56,6 +57,7 @@ public class JdbcRepositoryFactoryBean, S, ID extend private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY; private NamedParameterJdbcOperations operations; private EntityCallbacks entityCallbacks; + private Dialect dialect; /** * Creates a new {@link JdbcRepositoryFactoryBean} for the given repository interface. @@ -99,6 +101,11 @@ protected void setMappingContext(RelationalMappingContext mappingContext) { this.mappingContext = mappingContext; } + @Autowired + protected void setDialect(Dialect dialect) { + this.dialect = dialect; + } + /** * @param dataAccessStrategy can be {@literal null}. */ @@ -167,7 +174,9 @@ public void afterPropertiesSet() { this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class) // .getIfAvailable(() -> { - SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext); + Assert.state(this.dialect != null, "Dialect is required and must not be null!"); + + SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext, this.dialect); return new DefaultDataAccessStrategy(sqlGeneratorSource, this.mappingContext, this.converter, this.operations); }); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java index d9e89a3f29..49a9b96446 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationImmutableUnitTests.java @@ -40,6 +40,7 @@ import org.springframework.data.relational.core.conversion.DbAction; import org.springframework.data.relational.core.conversion.Interpreter; import org.springframework.data.relational.core.conversion.RelationalConverter; +import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; @@ -446,7 +447,7 @@ private static class DummyEntity { List contentList; Map contentMap; List contentNoIdList; - @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL) ContentNoId embedded; + @Embedded(onEmpty = Embedded.OnEmpty.USE_NULL, prefix = "fooBar") ContentNoId embedded; DummyEntity() { @@ -485,8 +486,7 @@ private static class Content { @With @AllArgsConstructor private static class ContentNoId { - - Tag single; + @Column("single") Tag single; Set tagSet; List tagList; Map tagMap; 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 9bfdf98e30..a5da71b1df 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; @@ -41,6 +38,7 @@ 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.SqlIdentifier; /** * Unit tests for {@link DefaultJdbcInterpreter} @@ -52,9 +50,8 @@ */ public class DefaultJdbcInterpreterUnitTests { + public static final SqlIdentifier 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 +153,10 @@ public void generateCascadingIds() { 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 b7e1e49f21..af89e71edb 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 void shouldDeleteChainOfListsWithoutIds() { }); } - /** - * 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,11 @@ private void saveAndUpdateAggregateWithVersion(VersionedAggre .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))); - } - + @Table("ARRAY_OWNER") private static class ArrayOwner { @Id Long id; @@ -836,18 +849,6 @@ private static class SetOwner { 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 { @@ -1039,7 +1040,6 @@ static class AggregateWithImmutableVersion { @Id private Long id; @Version private final Long version; - } @Data @@ -1060,13 +1060,13 @@ static class AggregateWithPrimitiveLongVersion extends VersionedAggregate { @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 @@ static class AggregateWithPrimitiveIntegerVersion extends VersionedAggregate { @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 @@ static class AggregateWithPrimitiveShortVersion extends VersionedAggregate { @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 a8f616b3db..542aa5e1ce 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.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; -import static org.assertj.core.api.SoftAssertions.*; - /** * @author Jens Schauder */ @@ -99,13 +99,13 @@ public void getTableName() { 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 void getTableAlias() { 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 void getColumnName() { 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 void reverseColumnName() { 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 void extendBy() { }); } - @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 fa42af0c61..c039973983 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 @@ 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; @@ -26,6 +27,7 @@ import java.util.HashMap; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.springframework.core.convert.converter.Converter; @@ -33,7 +35,10 @@ 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 +59,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 +67,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 +85,12 @@ public void before() { @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 +98,16 @@ public void additionalParametersGetAddedToStatement() { 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 +120,7 @@ public void considersConfiguredWriteConverter() { new DefaultJdbcTypeFactory(jdbcOperations)); DefaultDataAccessStrategy accessStrategy = new DefaultDataAccessStrategy( // - new SqlGeneratorSource(context), // + new SqlGeneratorSource(context, HsqlDbDialect.INSTANCE), // context, // converter, // namedJdbcOperations); @@ -128,8 +135,8 @@ public void considersConfiguredWriteConverter() { 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 bc47a266e5..cc69a2bfa0 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 @@ -81,14 +81,14 @@ public class EntityRowMapperUnitTests { public static final NamingStrategy X_APPENDING_NAMINGSTRATEGY = new NamingStrategy() { @Override public String getColumnName(RelationalPersistentProperty property) { - return NamingStrategy.super.getColumnName(property) + "x"; + return NamingStrategy.super.getColumnName(property).concat("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 +103,7 @@ public void simpleEntitiesGetProperlyExtracted() throws SQLException { @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 +118,7 @@ public void namingStrategyGetsHonored() throws SQLException { @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 +133,7 @@ public void namingStrategyGetsHonoredForConstructor() throws SQLException { @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 +148,7 @@ public void simpleWithReferenceGetProperlyExtracted() throws SQLException { @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 +163,7 @@ public void simpleOneToOneGetsProperlyExtracted() throws SQLException { @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 +178,7 @@ public void immutableOneToOneGetsProperlyExtracted() throws SQLException { @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 +194,7 @@ public void immutableWithReferenceGetsProperlyExtracted() throws SQLException { @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 +209,7 @@ public void simpleEmbeddedGetsProperlyExtracted() throws SQLException { @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 +224,7 @@ public void collectionReferenceGetsLoadedWithAdditionalSelect() throws SQLExcept @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 +239,7 @@ public void mapReferenceGetsLoadedWithAdditionalSelect() throws SQLException { @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 +254,7 @@ public void listReferenceGetsLoadedWithAdditionalSelect() throws SQLException { @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 +267,7 @@ public void doesNotTryToSetPropertiesThatAreSetViaConstructor() throws SQLExcept @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 +281,7 @@ public void handlesMixedProperties() throws SQLException { @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 +297,22 @@ public void chainedEntitiesWithoutId() throws SQLException { 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 +329,7 @@ public void chainedEntitiesWithoutId() throws SQLException { @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 +345,7 @@ public void simpleNullableImmutableEmbeddedGetsProperlyExtracted() throws SQLExc @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 +361,7 @@ public void simpleEmptyImmutableEmbeddedGetsProperlyExtracted() throws SQLExcept @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 +377,7 @@ public void simplePrimitiveImmutableEmbeddedGetsProperlyExtracted() { @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 +394,7 @@ public void simpleImmutableEmbeddedShouldBeNullIfAllOfTheEmbeddableAreNull() thr @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 +410,7 @@ public void embeddedShouldBeNullWhenFieldsAreNull() { @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 +426,7 @@ public void embeddedShouldNotBeNullWhenAtLeastOneFieldIsNotNull() { @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 +443,7 @@ public void primitiveEmbeddedShouldBeNullWhenNoValuePresent() { @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 0bf2879d4a..fe066a76a2 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 @@ 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 void parametersWithPropertyKeysUseTheParentPropertyJdbcType() { 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 void qualifiersForMaps() { 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 void qualifiersForLists() { 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 void backreferenceAcrossEmbeddable() { 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 void backreferenceAcrossNoId() { 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 ce65d661b4..7425eab572 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 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.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; +import org.springframework.data.relational.domain.IdentifierProcessing; +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 @@ -87,8 +91,11 @@ public void cascadingDeleteFirstLevel() { 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 @@ private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) { RelationalMappingContext context = new JdbcMappingContext(namingStrategy); RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); - return new SqlGenerator(context, persistentEntity); + return new SqlGenerator(context, persistentEntity, + IdentifierProcessing.create(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 85bbc3ff24..bb7182607e 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.RelationalMappingContext; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.sql.Aliased; +import org.springframework.data.relational.domain.IdentifierProcessing; +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 void setUp() { SqlGenerator createSqlGenerator(Class type) { RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(type); - return new SqlGenerator(context, persistentEntity); + return new SqlGenerator(context, persistentEntity, + IdentifierProcessing.create(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 f361312cd8..300940490c 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 @@ -19,6 +19,7 @@ import org.assertj.core.api.SoftAssertions; import org.junit.Test; + import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils; @@ -27,12 +28,14 @@ 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; /** * Unit tests the {@link SqlGenerator} with a fixed {@link NamingStrategy} implementation containing a hard wired * schema, table, and property prefix. * * @author Greg Turnquist + * @author Mark Paluch */ public class SqlGeneratorFixedNamingStrategyUnitTests { @@ -78,14 +81,17 @@ public void findOneWithOverriddenFixedTableName() { SoftAssertions softAssertions = new SoftAssertions(); softAssertions.assertThat(sql) // - .startsWith("SELECT") // - .contains( - "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("FROM FixedCustomSchema.FixedCustomTablePrefix_DummyEntity"); + .isEqualTo( + "SELECT \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\".\"FIXEDCUSTOMPROPERTYPREFIX_ID\" AS \"FIXEDCUSTOMPROPERTYPREFIX_ID\", " + + "\"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\".\"FIXEDCUSTOMPROPERTYPREFIX_NAME\" AS \"FIXEDCUSTOMPROPERTYPREFIX_NAME\", " + + "\"ref\".\"FIXEDCUSTOMPROPERTYPREFIX_L1ID\" AS \"REF_FIXEDCUSTOMPROPERTYPREFIX_L1ID\", " + + "\"ref\".\"FIXEDCUSTOMPROPERTYPREFIX_CONTENT\" AS \"REF_FIXEDCUSTOMPROPERTYPREFIX_CONTENT\", " + + "\"ref_further\".\"FIXEDCUSTOMPROPERTYPREFIX_L2ID\" AS \"REF_FURTHER_FIXEDCUSTOMPROPERTYPREFIX_L2ID\", " + + "\"ref_further\".\"FIXEDCUSTOMPROPERTYPREFIX_SOMETHING\" AS \"REF_FURTHER_FIXEDCUSTOMPROPERTYPREFIX_SOMETHING\" " + + "FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\" " + + "LEFT OUTER JOIN \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\" AS \"ref\" ON \"ref\".\"FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\" = \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\".\"FIXEDCUSTOMPROPERTYPREFIX_ID\" L" + + "EFT OUTER JOIN \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_SECONDLEVELREFERENCEDENTITY\" AS \"ref_further\" ON \"ref_further\".\"FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\" = \"ref\".\"FIXEDCUSTOMPROPERTYPREFIX_L1ID\" " + + "WHERE \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\".\"FIXEDCUSTOMPROPERTYPREFIX_ID\" = :id"); softAssertions.assertAll(); } @@ -98,12 +104,13 @@ public void findOneWithUppercasedTablesAndLowercasedColumns() { SoftAssertions softAssertions = new SoftAssertions(); softAssertions.assertThat(sql) // - .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("FROM DUMMYENTITY"); + .isEqualTo( + "SELECT \"DUMMYENTITY\".\"ID\" AS \"ID\", \"DUMMYENTITY\".\"NAME\" AS \"NAME\", \"ref\".\"L1ID\" AS \"REF_L1ID\", \"ref\".\"CONTENT\" AS \"REF_CONTENT\", " + + "\"ref_further\".\"L2ID\" AS \"REF_FURTHER_L2ID\", \"ref_further\".\"SOMETHING\" AS \"REF_FURTHER_SOMETHING\" " + + "FROM \"DUMMYENTITY\" " + + "LEFT OUTER JOIN \"REFERENCEDENTITY\" AS \"ref\" ON \"ref\".\"DUMMYENTITY\" = \"DUMMYENTITY\".\"ID\" " + + "LEFT OUTER JOIN \"SECONDLEVELREFERENCEDENTITY\" AS \"ref_further\" ON \"ref_further\".\"REFERENCEDENTITY\" = \"ref\".\"L1ID\" " + + "WHERE \"DUMMYENTITY\".\"ID\" = :id"); softAssertions.assertAll(); } @@ -114,8 +121,8 @@ public void cascadingDeleteFirstLevel() { String sql = sqlGenerator.createDeleteByPath(getPath("ref")); - assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity = :rootId"); + assertThat(sql).isEqualTo("DELETE FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\" " + + "WHERE \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\".\"DUMMY_ENTITY\" = :rootId"); } @Test // DATAJDBC-107 @@ -125,11 +132,11 @@ public void cascadingDeleteAllSecondLevel() { String sql = sqlGenerator.createDeleteByPath(getPath("ref.further")); - assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.referenced_entity IN " - + "(SELECT FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.FixedCustomPropertyPrefix_l1id " - + "FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity = :rootId)"); + assertThat(sql).isEqualTo("DELETE FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_SECONDLEVELREFERENCEDENTITY\" " + + "WHERE \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_SECONDLEVELREFERENCEDENTITY\".\"REFERENCED_ENTITY\" IN " + + "(SELECT \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\".\"FIXEDCUSTOMPROPERTYPREFIX_L1ID\" " + + "FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\" " + + "WHERE \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\".\"DUMMY_ENTITY\" = :rootId)"); } @Test // DATAJDBC-107 @@ -139,7 +146,7 @@ public void deleteAll() { String sql = sqlGenerator.createDeleteAllSql(null); - assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_DummyEntity"); + assertThat(sql).isEqualTo("DELETE FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\""); } @Test // DATAJDBC-107 @@ -149,8 +156,8 @@ public void cascadingDeleteAllFirstLevel() { String sql = sqlGenerator.createDeleteAllSql(getPath("ref")); - assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity " - + "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity IS NOT NULL"); + assertThat(sql).isEqualTo("DELETE FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\" " + + "WHERE \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_REFERENCEDENTITY\".\"DUMMY_ENTITY\" IS NOT NULL"); } @Test // DATAJDBC-107 @@ -160,11 +167,11 @@ public void cascadingDeleteSecondLevel() { String sql = sqlGenerator.createDeleteAllSql(getPath("ref.further")); - assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity " - + "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)"); + assertThat(sql).isEqualTo("DELETE FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_SECONDLEVELREFERENCEDENTITY\" " + + "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)"); } @Test // DATAJDBC-113 @@ -175,7 +182,7 @@ public void deleteByList() { String sql = sqlGenerator.getDeleteByList(); assertThat(sql).isEqualTo( - "DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_DummyEntity WHERE FixedCustomSchema.FixedCustomTablePrefix_DummyEntity.FixedCustomPropertyPrefix_id IN (:ids)"); + "DELETE FROM \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\" WHERE \"FIXEDCUSTOMSCHEMA.FIXEDCUSTOMTABLEPREFIX_DUMMYENTITY\".\"FIXEDCUSTOMPROPERTYPREFIX_ID\" IN (:ids)"); } private PersistentPropertyPath getPath(String path) { @@ -189,7 +196,7 @@ private SqlGenerator configureSqlGenerator(NamingStrategy namingStrategy) { 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 5fa155f863..a46ee4d5a3 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 @@ 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; @@ -24,6 +25,7 @@ import org.assertj.core.api.SoftAssertions; import org.junit.Before; import org.junit.Test; + import org.springframework.data.annotation.Id; import org.springframework.data.annotation.ReadOnlyProperty; import org.springframework.data.annotation.Version; @@ -41,6 +43,9 @@ 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.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; /** * Unit tests for the {@link SqlGenerator}. @@ -54,7 +59,7 @@ */ 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 +72,14 @@ public void setUp() { SqlGenerator createSqlGenerator(Class type) { + return createSqlGenerator(type, IdentifierProcessing.create(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 +184,8 @@ public void findAllByProperty() { 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 +207,7 @@ public void findAllByPropertyWithMultipartIdentifier() { 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 +229,7 @@ public void findAllByPropertyOrderedWithoutKey() { 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 +245,16 @@ public void findAllByPropertyWithKeyOrdered() { @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 +270,69 @@ public void getInsertForEmptyColumnList() { @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\" = :id1"); } @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 +353,7 @@ public void readOnlyPropertyIncludedIntoQuery_when_generateFindAllByPropertySql( 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 +447,15 @@ public void noJoinForSimpleColumn() { @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 +479,39 @@ public void noJoinForMappedReference() { @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 +520,7 @@ public void simpleColumn() { 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 +528,7 @@ public void columnForIndirectProperty() { 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 +542,8 @@ public void columnForReferencedEntityWithoutId() { 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 +556,7 @@ private String getAlias(Object maybeAliased) { 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))); } @@ -621,8 +638,10 @@ static class EntityWithReadOnlyProperty { 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 0000000000..f4aaed9bde --- /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 d3eefe2327..05816debf2 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 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 void detectsAnnotatedColumnName() { 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 void detectsAnnotatedColumnAndKeyName() { .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 void detectsKeyColumnNameFromColumnAnnotation() { .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 void detectsKeyColumnOverrideNameFromMappedCollectionAnnotation() { .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 @@ private void checkTargetType(SoftAssertions softly, RelationalPersistentEntity 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 bdb79273e0..a410009466 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 @@ -23,10 +23,12 @@ import java.util.List; import org.junit.Test; + 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}. @@ -73,7 +75,7 @@ public void getKeyColumn() { @Test // DATAJDBC-184 public void getSchema() { - assertThat(target.getSchema()).isEmpty(); + assertThat(target.getSchema()).isEqualTo(""); } @Test // DATAJDBC-184 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 2de583ad6c..be8c18e437 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.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 void myBatisGetsUsedForInsertAndSelect() { 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 @@ MyBatisDataAccessStrategy dataAccessStrategy(SqlSession sqlSession) { 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 String getNamespace(Class domainType) { 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 2c048f6375..45d62633d1 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,24 +15,19 @@ */ 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; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.session.SqlSession; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -44,6 +39,7 @@ 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 +56,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 +70,7 @@ public void before() { @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 2014ac1856..a6f7d88454 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.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 @@ @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 @@ DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, JdbcConv 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/JdbcRepositoryEmbeddedImmutableIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedImmutableIntegrationTests.java index 3f90d6cd8a..c670396487 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedImmutableIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedImmutableIntegrationTests.java @@ -100,7 +100,7 @@ static class DummyEntity { @Id Long id; - @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") Embeddable prefixedEmbeddable; + @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "PREFIX_") Embeddable prefixedEmbeddable; } @Value diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java index db4bcc9ec9..516b7f8c2e 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedIntegrationTests.java @@ -251,7 +251,7 @@ static class DummyEntity { @Id Long id; - @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") CascadedEmbeddable prefixedEmbeddable; + @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "PREFIX_") CascadedEmbeddable prefixedEmbeddable; @Embedded(onEmpty = OnEmpty.USE_NULL) CascadedEmbeddable embeddable; } @@ -260,7 +260,7 @@ static class DummyEntity { static class CascadedEmbeddable { String test; - @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix2_") + @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "PREFIX2_") Embeddable embeddable; } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java index c797a4cda8..68a00fa9b6 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests.java @@ -20,6 +20,8 @@ import lombok.Data; +import java.sql.SQLException; + import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -30,9 +32,11 @@ import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -74,17 +78,24 @@ DummyEntityRepository dummyEntityRepository() { @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; + @Autowired Dialect dialect; @Test // DATAJDBC-111 - public void savesAnEntity() { + public void savesAnEntity() throws SQLException { DummyEntity entity = repository.save(createDummyEntity()); - assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity", - "id = " + entity.getId())).isEqualTo(1); + assertThat(countRowsInTable("dummy_entity", entity.getId())).isEqualTo(1); + assertThat(countRowsInTable("dummy_entity2", entity.getId())).isEqualTo(1); + } + + private int countRowsInTable(String name, long idValue) { + + SqlIdentifier id = SqlIdentifier.quoted("ID"); + String whereClause = id.toSql(dialect.getIdentifierProcessing()) + " = " + idValue; - assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity2", - "id = " + entity.getId())).isEqualTo(1); + return JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), + name, whereClause); } @Test // DATAJDBC-111 @@ -95,7 +106,8 @@ public void saveAndLoadAnEntity() { assertThat(repository.findById(entity.getId())).hasValueSatisfying(it -> { assertThat(it.getId()).isEqualTo(entity.getId()); assertThat(it.getDummyEntity2().getTest()).isEqualTo(entity.getDummyEntity2().getTest()); - assertThat(it.getDummyEntity2().getEmbeddable().getAttr()).isEqualTo(entity.getDummyEntity2().getEmbeddable().getAttr()); + assertThat(it.getDummyEntity2().getEmbeddable().getAttr()) + .isEqualTo(entity.getDummyEntity2().getEmbeddable().getAttr()); }); } @@ -130,7 +142,8 @@ public void update() { assertThat(repository.findById(entity.getId())).hasValueSatisfying(it -> { assertThat(it.getDummyEntity2().getTest()).isEqualTo(saved.getDummyEntity2().getTest()); - assertThat(it.getDummyEntity2().getEmbeddable().getAttr()).isEqualTo(saved.getDummyEntity2().getEmbeddable().getAttr()); + assertThat(it.getDummyEntity2().getEmbeddable().getAttr()) + .isEqualTo(saved.getDummyEntity2().getEmbeddable().getAttr()); }); } @@ -154,7 +167,8 @@ public void updateMany() { assertThat(repository.findAll()) // .extracting(d -> d.getDummyEntity2().getEmbeddable().getAttr()) // - .containsExactlyInAnyOrder(entity.getDummyEntity2().getEmbeddable().getAttr(), other.getDummyEntity2().getEmbeddable().getAttr()); + .containsExactlyInAnyOrder(entity.getDummyEntity2().getEmbeddable().getAttr(), + other.getDummyEntity2().getEmbeddable().getAttr()); } @Test // DATAJDBC-111 @@ -233,22 +247,20 @@ interface DummyEntityRepository extends CrudRepository {} @Data static class DummyEntity { - @Id Long id; + @Column("ID") @Id Long id; String test; - @Column("id") - DummyEntity2 dummyEntity2; + @Column("ID") DummyEntity2 dummyEntity2; } @Data static class DummyEntity2 { - @Id Long id; + @Column("ID") @Id Long id; String test; - @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") - Embeddable embeddable; + @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") Embeddable embeddable; } @Data diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java index 6f9b466c95..39912f6a87 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests.java @@ -20,6 +20,10 @@ import lombok.Data; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -30,10 +34,12 @@ import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; import org.springframework.data.relational.core.mapping.MappedCollection; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -43,9 +49,6 @@ import org.springframework.test.jdbc.JdbcTestUtils; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.List; - /** * Very simple use cases for creation and usage of JdbcRepositories with test {@link Embedded} annotation in Entities. * @@ -78,17 +81,23 @@ DummyEntityRepository dummyEntityRepository() { @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; + @Autowired Dialect dialect; @Test // DATAJDBC-111 - public void savesAnEntity() { + public void savesAnEntity() throws SQLException { DummyEntity entity = repository.save(createDummyEntity()); - assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity", - "id = " + entity.getId())).isEqualTo(1); + assertThat(countRowsInTable("dummy_entity", entity.getId())).isEqualTo(1); + assertThat(countRowsInTable("dummy_entity2", entity.getId())).isEqualTo(2); + } + + private int countRowsInTable(String name, long idValue) { + + SqlIdentifier id = SqlIdentifier.quoted("ID"); + String whereClause = id.toSql(dialect.getIdentifierProcessing()) + " = " + idValue; - assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity2", - "id = " + entity.getId())).isEqualTo(2); + return JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), name, whereClause); } @Test // DATAJDBC-111 @@ -100,8 +109,10 @@ public void saveAndLoadAnEntity() { assertThat(it.getId()).isEqualTo(entity.getId()); assertThat(it.getEmbeddable().getTest()).isEqualTo(entity.getEmbeddable().getTest()); assertThat(it.getEmbeddable().getList().size()).isEqualTo(entity.getEmbeddable().getList().size()); - assertThat(it.getEmbeddable().getList().get(0).getTest()).isEqualTo(entity.getEmbeddable().getList().get(0).getTest()); - assertThat(it.getEmbeddable().getList().get(1).getTest()).isEqualTo(entity.getEmbeddable().getList().get(1).getTest()); + assertThat(it.getEmbeddable().getList().get(0).getTest()) + .isEqualTo(entity.getEmbeddable().getList().get(0).getTest()); + assertThat(it.getEmbeddable().getList().get(1).getTest()) + .isEqualTo(entity.getEmbeddable().getList().get(1).getTest()); }); } @@ -138,8 +149,10 @@ public void update() { assertThat(it.getId()).isEqualTo(saved.getId()); assertThat(it.getEmbeddable().getTest()).isEqualTo(saved.getEmbeddable().getTest()); assertThat(it.getEmbeddable().getList().size()).isEqualTo(saved.getEmbeddable().getList().size()); - assertThat(it.getEmbeddable().getList().get(0).getTest()).isEqualTo(saved.getEmbeddable().getList().get(0).getTest()); - assertThat(it.getEmbeddable().getList().get(1).getTest()).isEqualTo(saved.getEmbeddable().getList().get(1).getTest()); + assertThat(it.getEmbeddable().getList().get(0).getTest()) + .isEqualTo(saved.getEmbeddable().getList().get(0).getTest()); + assertThat(it.getEmbeddable().getList().get(1).getTest()) + .isEqualTo(saved.getEmbeddable().getList().get(1).getTest()); }); } @@ -163,7 +176,8 @@ public void updateMany() { assertThat(repository.findAll()) // .extracting(d -> d.getEmbeddable().getList().get(0).getTest()) // - .containsExactlyInAnyOrder(entity.getEmbeddable().getList().get(0).getTest(), other.getEmbeddable().getList().get(0).getTest()); + .containsExactlyInAnyOrder(entity.getEmbeddable().getList().get(0).getTest(), + other.getEmbeddable().getList().get(0).getTest()); } @Test // DATAJDBC-111 @@ -246,18 +260,17 @@ interface DummyEntityRepository extends CrudRepository {} @Data private static class DummyEntity { + @Column("ID") @Id Long id; String test; - @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") - Embeddable embeddable; + @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "PREFIX_") Embeddable embeddable; } @Data private static class Embeddable { - @MappedCollection(idColumn = "id", keyColumn = "order_key") - List list = new ArrayList<>(); + @MappedCollection(idColumn = "ID", keyColumn = "ORDER_KEY") List list = new ArrayList<>(); String test; } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java index 4611830643..e1ed076f4f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests.java @@ -20,6 +20,8 @@ import lombok.Data; +import java.sql.SQLException; + import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -30,9 +32,11 @@ import org.springframework.data.annotation.Id; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.testing.TestConfiguration; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Embedded; import org.springframework.data.relational.core.mapping.Embedded.OnEmpty; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -74,17 +78,23 @@ DummyEntityRepository dummyEntityRepository() { @Autowired NamedParameterJdbcTemplate template; @Autowired DummyEntityRepository repository; + @Autowired Dialect dialect; @Test // DATAJDBC-111 public void savesAnEntity() { DummyEntity entity = repository.save(createDummyEntity()); - assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity", - "id = " + entity.getId())).isEqualTo(1); + assertThat(countRowsInTable("dummy_entity", entity.getId())).isEqualTo(1); + assertThat(countRowsInTable("dummy_entity2", entity.getId())).isEqualTo(1); + } + + private int countRowsInTable(String name, long idValue) { - assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity2", - "id = " + entity.getId())).isEqualTo(1); + SqlIdentifier id = SqlIdentifier.quoted("ID"); + String whereClause = id.toSql(dialect.getIdentifierProcessing()) + " = " + idValue; + + return JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), name, whereClause); } @Test // DATAJDBC-111 @@ -95,7 +105,8 @@ public void saveAndLoadAnEntity() { assertThat(repository.findById(entity.getId())).hasValueSatisfying(it -> { assertThat(it.getId()).isEqualTo(entity.getId()); assertThat(it.getEmbeddable().getTest()).isEqualTo(entity.getEmbeddable().getTest()); - assertThat(it.getEmbeddable().getDummyEntity2().getTest()).isEqualTo(entity.getEmbeddable().getDummyEntity2().getTest()); + assertThat(it.getEmbeddable().getDummyEntity2().getTest()) + .isEqualTo(entity.getEmbeddable().getDummyEntity2().getTest()); }); } @@ -130,7 +141,8 @@ public void update() { assertThat(repository.findById(entity.getId())).hasValueSatisfying(it -> { assertThat(it.getEmbeddable().getTest()).isEqualTo(saved.getEmbeddable().getTest()); - assertThat(it.getEmbeddable().getDummyEntity2().getTest()).isEqualTo(saved.getEmbeddable().getDummyEntity2().getTest()); + assertThat(it.getEmbeddable().getDummyEntity2().getTest()) + .isEqualTo(saved.getEmbeddable().getDummyEntity2().getTest()); }); } @@ -154,7 +166,8 @@ public void updateMany() { assertThat(repository.findAll()) // .extracting(d -> d.getEmbeddable().getDummyEntity2().getTest()) // - .containsExactlyInAnyOrder(entity.getEmbeddable().getDummyEntity2().getTest(), other.getEmbeddable().getDummyEntity2().getTest()); + .containsExactlyInAnyOrder(entity.getEmbeddable().getDummyEntity2().getTest(), + other.getEmbeddable().getDummyEntity2().getTest()); } @Test // DATAJDBC-111 @@ -233,19 +246,18 @@ interface DummyEntityRepository extends CrudRepository {} @Data private static class DummyEntity { - @Id Long id; + + @Column("ID") @Id Long id; String test; - @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "prefix_") - Embeddable embeddable; + @Embedded(onEmpty = OnEmpty.USE_NULL, prefix = "PREFIX_") Embeddable embeddable; } @Data private static class Embeddable { - @Column("id") - DummyEntity2 dummyEntity2; + @Column("ID") DummyEntity2 dummyEntity2; String test; } @@ -253,7 +265,7 @@ private static class Embeddable { @Data private static class DummyEntity2 { - @Id Long id; + @Column("ID") @Id Long id; String test; } 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 12a77ef485..b9e5779011 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 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.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; 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 dac687929d..1329178c3a 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.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 void before() { 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 352839967e..78b39eb365 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.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; 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 45aa3c420e..13a0ab6db3 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.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 @@ NamedParameterJdbcOperations qualifierJdbcOperations(DataSource dataSource) { @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 f479ac1521..43403b5cf9 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.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 void setsUpBasicInstanceCorrectly() { 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 void afterPropertiesSetDefaultsNullablePropertiesCorrectly() { 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 9d4b71374f..3be5ef4128 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.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 @@ DataSource dataSource() { .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 8a0b8a709a..385c0a561c 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.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 @@ protected DataSource createDataSource() { } } + @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 94ce8ea45c..f62872bab6 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.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 @@ protected DataSource createDataSource() { 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 f9b4cb71e3..46f238afa0 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 @@ 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 @@ protected DataSource createDataSource() { 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 66803880ba..e134a86486 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.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 @@ PlatformTransactionManager transactionManager() { @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/logback.xml b/spring-data-jdbc/src/test/resources/logback.xml index 8ef5ba0910..ade0cc6ed6 100644 --- a/spring-data-jdbc/src/test/resources/logback.xml +++ b/spring-data-jdbc/src/test/resources/logback.xml @@ -8,7 +8,7 @@ - + diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-hsql.sql index aa63a84436..07ee8a71f2 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-hsql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-hsql.sql @@ -1,23 +1,23 @@ CREATE TABLE LEGO_SET ( - id1 BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, + "id1" BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, NAME VARCHAR(30) ); CREATE TABLE MANUAL ( - id2 BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, + "id2" BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, LEGO_SET BIGINT, - ALTERNATIVE BIGINT, + "alternative" BIGINT, CONTENT VARCHAR(2000) ); ALTER TABLE MANUAL ADD FOREIGN KEY (LEGO_SET) - REFERENCES LEGO_SET (id1); + REFERENCES LEGO_SET ("id1"); CREATE TABLE ONE_TO_ONE_PARENT ( - id3 BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, + "id3" BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, content VARCHAR(30) ); CREATE TABLE Child_No_Id @@ -28,18 +28,18 @@ CREATE TABLE Child_No_Id CREATE TABLE LIST_PARENT ( - id4 BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, + "id4" BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100) ); CREATE TABLE ELEMENT_NO_ID ( - content VARCHAR(100), + CONTENT VARCHAR(100), LIST_PARENT_KEY BIGINT, LIST_PARENT BIGINT ); ALTER TABLE ELEMENT_NO_ID ADD FOREIGN KEY (LIST_PARENT) - REFERENCES LIST_PARENT (id4); + REFERENCES LIST_PARENT ("id4"); CREATE TABLE ARRAY_OWNER ( diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mariadb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mariadb.sql index 63494fd12a..31bf495f8b 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mariadb.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mariadb.sql @@ -1,11 +1,11 @@ CREATE TABLE LEGO_SET ( - id1 BIGINT AUTO_INCREMENT PRIMARY KEY, + `id1` BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(30) ); CREATE TABLE MANUAL ( - id2 BIGINT AUTO_INCREMENT PRIMARY KEY, + `id2` BIGINT AUTO_INCREMENT PRIMARY KEY, LEGO_SET BIGINT, ALTERNATIVE BIGINT, CONTENT VARCHAR(2000) @@ -13,27 +13,27 @@ CREATE TABLE MANUAL ALTER TABLE MANUAL ADD FOREIGN KEY (LEGO_SET) - REFERENCES LEGO_SET (id1); + 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) + `content` VARCHAR(30) ); CREATE TABLE LIST_PARENT ( - id4 BIGINT AUTO_INCREMENT PRIMARY KEY, + `id4` BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100) ); CREATE TABLE element_no_id ( - content VARCHAR(100), + CONTENT VARCHAR(100), LIST_PARENT_key BIGINT, LIST_PARENT BIGINT ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mssql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mssql.sql index 1edfd32f00..b22766e0fd 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mssql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.core/JdbcAggregateTemplateIntegrationTests-mssql.sql @@ -2,12 +2,12 @@ DROP TABLE IF EXISTS MANUAL; DROP TABLE IF EXISTS LEGO_SET; CREATE TABLE LEGO_SET ( - id1 BIGINT IDENTITY PRIMARY KEY, + [id1] BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(30) ); CREATE TABLE MANUAL ( - id2 BIGINT IDENTITY PRIMARY KEY, + [id2] BIGINT IDENTITY PRIMARY KEY, LEGO_SET BIGINT, ALTERNATIVE BIGINT, CONTENT VARCHAR(2000) @@ -19,25 +19,25 @@ DROP TABLE IF EXISTS Child_No_Id; DROP TABLE IF EXISTS ONE_TO_ONE_PARENT; CREATE TABLE ONE_TO_ONE_PARENT ( - id3 BIGINT IDENTITY PRIMARY KEY, + [id3] BIGINT IDENTITY PRIMARY KEY, content VARCHAR(30) ); CREATE TABLE Child_No_Id ( ONE_TO_ONE_PARENT BIGINT PRIMARY KEY, - content VARCHAR(30) + [content] VARCHAR(30) ); DROP TABLE IF EXISTS element_no_id; DROP TABLE IF EXISTS LIST_PARENT; CREATE TABLE LIST_PARENT ( - id4 BIGINT IDENTITY PRIMARY KEY, + [id4] BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(100) ); CREATE TABLE element_no_id ( - content VARCHAR(100), + CONTENT VARCHAR(100), LIST_PARENT_key BIGINT, LIST_PARENT BIGINT ); @@ -297,4 +297,4 @@ CREATE TABLE VERSIONED_AGGREGATE ( ID BIGINT IDENTITY PRIMARY KEY, VERSION BIGINT -); \ No newline at end of file +); 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 db71d7a10f..9d8ba80f35 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' +); 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 9ac303a0ed..30b158fc3d 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,310 @@ 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 +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 ); -CREATE TABLE VERSIONED_AGGREGATE +CREATE TABLE WITH_READ_ONLY ( - ID SERIAL PRIMARY KEY, - VERSION BIGINT -); \ No newline at end of file + ID SERIAL PRIMARY KEY, + NAME VARCHAR(200), + READ_ONLY VARCHAR(200) DEFAULT 'from-db' +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-mysql.sql index c9a201e612..c8ae948db3 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-mysql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-mysql.sql @@ -1,2 +1,11 @@ -CREATE TABLE dummy_entity (id BIGINT AUTO_INCREMENT PRIMARY KEY, TEST VARCHAR(100)); -CREATE TABLE dummy_entity2 (id BIGINT AUTO_INCREMENT PRIMARY KEY, TEST VARCHAR(100), PREFIX_ATTR BIGINT); +CREATE TABLE dummy_entity +( + ID BIGINT AUTO_INCREMENT PRIMARY KEY, + TEST VARCHAR(100) +); +CREATE TABLE dummy_entity2 +( + ID BIGINT PRIMARY KEY, + TEST VARCHAR(100), + PREFIX_ATTR BIGINT +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-postgres.sql index 13d7c6a110..fa4b1a1392 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-postgres.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedNotInAggregateRootIntegrationTests-postgres.sql @@ -1,4 +1,13 @@ DROP TABLE dummy_entity; -CREATE TABLE dummy_entity (id SERIAL PRIMARY KEY, TEST VARCHAR(100)); +CREATE TABLE dummy_entity +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100) +); DROP TABLE dummy_entity2; -CREATE TABLE dummy_entity2 (id SERIAL PRIMARY KEY, TEST VARCHAR(100), PREFIX_ATTR BIGINT); +CREATE TABLE dummy_entity2 +( + "ID" INTEGER PRIMARY KEY, + TEST VARCHAR(100), + PREFIX_ATTR BIGINT +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-postgres.sql index fa9cff08b2..4b49f1ef89 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-postgres.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithCollectionIntegrationTests-postgres.sql @@ -1,4 +1,15 @@ DROP TABLE dummy_entity; -CREATE TABLE dummy_entity (id SERIAL PRIMARY KEY, TEST VARCHAR(100), PREFIX_TEST VARCHAR(100)); +CREATE TABLE dummy_entity +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100), + PREFIX_TEST VARCHAR(100) +); DROP TABLE dummy_entity2; -CREATE TABLE dummy_entity2 (id BIGINT, ORDER_KEY BIGINT, TEST VARCHAR(100), PRIMARY KEY (id, ORDER_KEY)); +CREATE TABLE dummy_entity2 +( + "ID" BIGINT, + "ORDER_KEY" BIGINT, + TEST VARCHAR(100), + PRIMARY KEY ("ID", "ORDER_KEY") +); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-postgres.sql index c4c8cef0c0..c8128208d3 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-postgres.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryEmbeddedWithReferenceIntegrationTests-postgres.sql @@ -1,4 +1,22 @@ DROP TABLE dummy_entity; -CREATE TABLE dummy_entity (id SERIAL PRIMARY KEY, TEST VARCHAR(100), PREFIX_TEST VARCHAR(100)); +CREATE TABLE dummy_entity +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100), + PREFIX_TEST VARCHAR(100) +); DROP TABLE dummy_entity2; -CREATE TABLE dummy_entity2 (id SERIAL PRIMARY KEY, TEST VARCHAR(100)); +CREATE TABLE dummy_entity2 +( + "ID" SERIAL PRIMARY KEY, + TEST VARCHAR(100) +); +-- +-- SELECT "dummy_entity"."ID" AS "ID", +-- "dummy_entity"."test" AS "test", +-- "dummy_entity"."prefix_test" AS "prefix_test", +-- "PREFIX_dummyEntity2"."id" AS "prefix_dummyentity2_id", +-- "PREFIX_dummyEntity2"."test" AS "prefix_dummyentity2_test" +-- FROM "dummy_entity" +-- LEFT OUTER JOIN "dummy_entity2" AS "PREFIX_dummyEntity2" ON +-- "PREFIX_dummyEntity2"."ID" = "dummy_entity"."ID" \ No newline at end of file diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 1751c4244e..206a8c2628 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -5,7 +5,7 @@ 4.0.0 spring-data-relational - 1.2.0.BUILD-SNAPSHOT + 1.2.0.DATAJDBC-386-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -13,7 +13,7 @@ org.springframework.data spring-data-relational-parent - 1.2.0.BUILD-SNAPSHOT + 1.2.0.DATAJDBC-386-SNAPSHOT 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 8f6f45d9d2..21c1634e63 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 @@ default ArrayColumns getArraySupport() { * @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 0000000000..11ae9c8a6e --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java @@ -0,0 +1,58 @@ +/* + * 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 org.springframework.data.relational.domain.IdentifierProcessing; + +/** + * 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 0000000000..dd42bb2eda --- /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 50f6ff9efb..e03e5e5820 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,12 @@ */ package org.springframework.data.relational.core.dialect; +import org.springframework.data.relational.domain.IdentifierProcessing; +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 +33,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 +86,9 @@ public Position getClausePosition() { public LimitClause limit() { return LIMIT_CLAUSE; } + + @Override + public IdentifierProcessing getIdentifierProcessing() { + return IdentifierProcessing.create(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 25e398df14..d81cc6e2ad 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,9 @@ import lombok.RequiredArgsConstructor; +import org.springframework.data.relational.domain.IdentifierProcessing; +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 +36,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 +121,9 @@ public Class getArrayType(Class userType) { return ClassUtils.resolvePrimitiveIfNecessary(userType); } } + + @Override + public IdentifierProcessing getIdentifierProcessing() { + return IdentifierProcessing.create(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 b7ea294640..9242d4a0af 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 c0aa4b6fdb..24ea2585c1 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,7 @@ 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.util.Lazy; import org.springframework.data.util.Optionals; import org.springframework.lang.Nullable; @@ -58,12 +59,13 @@ 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); + private boolean forceQuote = true; /** * Creates a new {@link AnnotationBasedPersistentProperty}. @@ -91,17 +93,32 @@ public BasicRelationalPersistentProperty(Property property, PersistentEntity Optional.ofNullable(findAnnotation(Column.class)) // .map(Column::value) // .filter(StringUtils::hasText) // - .orElseGet(() -> context.getNamingStrategy().getColumnName(this))); + .map(this::createSqlIdentifier) // + .orElseGet(() -> createDerivedSqlIdentifier(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(this::createSqlIdentifier)); // - 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(this::createSqlIdentifier) // + .orElseGet(() -> createDerivedSqlIdentifier(context.getNamingStrategy().getKeyColumn(this)))); + } + + private SqlIdentifier createSqlIdentifier(String name) { + return isForceQuote() ? SqlIdentifier.quoted(name) : SqlIdentifier.unquoted(name); + } + + private SqlIdentifier createDerivedSqlIdentifier(String name) { + return new DerivedSqlIdentifier(name, isForceQuote()); } /* @@ -113,6 +130,14 @@ protected Association createAssociation() { throw new UnsupportedOperationException(); } + boolean isForceQuote() { + return forceQuote; + } + + void setForceQuote(boolean forceQuote) { + this.forceQuote = forceQuote; + } + @Override public boolean isEntity() { return super.isEntity() && !isReference(); @@ -128,7 +153,7 @@ public boolean isReference() { * @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentProperty#getColumnName() */ @Override - public String getColumnName() { + public SqlIdentifier getColumnName() { return columnName.get(); } @@ -178,18 +203,20 @@ public RelationalPersistentEntity getOwner() { } @Override - public String getReverseColumnName() { - return collectionIdColumnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(this)); + public SqlIdentifier getReverseColumnName() { + return collectionIdColumnName.get() + .orElseGet(() -> createDerivedSqlIdentifier(context.getNamingStrategy().getReverseColumnName(this))); } @Override - public String getReverseColumnName(PersistentPropertyPathExtension path) { + public SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) { - return collectionIdColumnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(path)); + return collectionIdColumnName.get() + .orElseGet(() -> createDerivedSqlIdentifier(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 79b053696d..c58f8ad193 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 @@ -44,7 +44,7 @@ class CachingNamingStrategy implements NamingStrategy { * * @param delegate must not be {@literal null}. */ - public CachingNamingStrategy(NamingStrategy delegate) { + CachingNamingStrategy(NamingStrategy delegate) { Assert.notNull(delegate, "Delegate must not be null!"); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java new file mode 100644 index 0000000000..1f42835eb5 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifier.java @@ -0,0 +1,105 @@ +/* + * Copyright 2020 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 java.util.function.UnaryOperator; + +import org.springframework.data.relational.domain.IdentifierProcessing; +import org.springframework.data.relational.domain.SqlIdentifier; +import org.springframework.util.Assert; + +/** + * {@link SqlIdentifier} that is derived from a property name or class name to infer the defaults provided by a + * {@link NamingStrategy}. + * + * @author Mark Paluch + * @since 2.0 + */ +class DerivedSqlIdentifier implements SqlIdentifier { + + private final String name; + private final boolean quoted; + + DerivedSqlIdentifier(String name, boolean quoted) { + + Assert.hasText(name, "A database object must have at least on name part."); + this.name = name; + this.quoted = quoted; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#transform(java.util.function.UnaryOperator) + */ + @Override + public SqlIdentifier transform(UnaryOperator transformationFunction) { + + Assert.notNull(transformationFunction, "Transformation function must not be null"); + + return new DerivedSqlIdentifier(transformationFunction.apply(name), quoted); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#toSql(org.springframework.data.relational.domain.IdentifierProcessing) + */ + @Override + public String toSql(IdentifierProcessing processing) { + return quoted ? processing.quote(getReference(processing)) : getReference(processing); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#getReference(org.springframework.data.relational.domain.IdentifierProcessing) + */ + @Override + public String getReference(IdentifierProcessing processing) { + return processing.standardizeLetterCase(name); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o instanceof SqlIdentifier) { + return toString().equals(o.toString()); + } + return false; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return toString().hashCode(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return toSql(IdentifierProcessing.ANSI); + } +} 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 be5ee64d45..e43dfdeff6 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,11 @@ */ package org.springframework.data.relational.core.mapping; +import static org.springframework.data.relational.domain.SqlIdentifier.*; + +import org.springframework.data.relational.domain.IdentifierProcessing; +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; @@ -24,7 +29,7 @@ *

* NOTE: Can also be used as an adapter. Create a lambda or an anonymous subclass and override any settings to implement * a different strategy on the fly. - * + * * @author Greg Turnquist * @author Michael Simons * @author Kazuki Shimizu @@ -85,7 +90,7 @@ default String getReverseColumnName(RelationalPersistentProperty property) { Assert.notNull(property, "Property must not be null."); - return property.getOwner().getTableName(); + return property.getOwner().getTableName().getReference(IdentifierProcessing.NONE); } default String getReverseColumnName(PersistentPropertyPathExtension path) { @@ -96,7 +101,7 @@ default String getReverseColumnName(PersistentPropertyPathExtension path) { /** * For a map valued reference A -> Map>X,B< this is the name of the column in the table for B holding the key of * the map. - * + * * @return name of the key column. Must not be {@code null}. */ default String getKeyColumn(RelationalPersistentProperty property) { 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 01901488cc..db9ecda72b 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 org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.context.MappingContext; +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; @@ -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 boolean isCollectionLike() { * * @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 String getReverseColumnName() { * * @throws IllegalStateException when called on an empty path. */ - public String getReverseColumnNameAlias() { + public SqlIdentifier getReverseColumnNameAlias() { return prefixWithTableAlias(getReverseColumnName()); } @@ -178,7 +179,7 @@ public String getReverseColumnNameAlias() { * * @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 String getColumnName() { * * @throws IllegalStateException when called on an empty path. */ - public String getColumnAlias() { + public SqlIdentifier getColumnAlias() { return columnAlias.get(); } @@ -228,7 +229,7 @@ public PersistentPropertyPathExtension getIdDefiningParentPath() { * * @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 String getTableName() { * @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 String getTableAlias() { /** * 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 String getIdColumnName() { * 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 RelationalPersistentProperty getRequiredIdProperty() { * @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 @@ private PersistentPropertyPathExtension getTableOwningAncestor() { return isEntity() && !isEmbedded() ? this : getParentPath().getTableOwningAncestor(); } - private String assembleTableAlias() { + private SqlIdentifier assembleTableAlias() { Assert.state(path != null, "Path is null"); @@ -393,15 +394,18 @@ private String assembleTableAlias() { if (path.getLength() == 1) { Assert.notNull(prefix, "Prefix mus not be null."); - return prefix; + return SqlIdentifier.quoted(prefix); } PersistentPropertyPathExtension parentPath = getParentPath(); - return parentPath.isEmbedded() ? parentPath.assembleTableAlias() + prefix - : parentPath.assembleTableAlias() + "_" + prefix; + SqlIdentifier sqlIdentifier = parentPath.assembleTableAlias(); + + return parentPath.isEmbedded() ? sqlIdentifier.transform(name -> name.concat(prefix)) + : sqlIdentifier.transform(name -> name + "_" + prefix); + } - private String assembleColumnName(String suffix) { + private SqlIdentifier assembleColumnName(SqlIdentifier suffix) { Assert.state(path != null, "Path is null"); @@ -418,17 +422,18 @@ private String assembleColumnName(String suffix) { String embeddedPrefix = parentLeaf.getEmbeddedPrefix(); - return getParentPath().assembleColumnName(embeddedPrefix + suffix); + return getParentPath().assembleColumnName(suffix.transform(embeddedPrefix::concat)); } 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.transform(name -> tableAlias.getReference(IdentifierProcessing.NONE) + "_" + name); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java index 9a01c3d745..56080fd239 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalMappingContext.java @@ -37,6 +37,7 @@ public class RelationalMappingContext extends AbstractMappingContext, RelationalPersistentProperty> { @Getter private final NamingStrategy namingStrategy; + private boolean forceQuote = true; /** * Creates a new {@link RelationalMappingContext}. @@ -59,13 +60,37 @@ public RelationalMappingContext(NamingStrategy namingStrategy) { setSimpleTypeHolder(SimpleTypeHolder.DEFAULT); } + /** + * Return whether quoting should be enabled for all table and column names. Quoting is enabled by default. + * + * @return + * @since 2.0 + */ + public boolean isForceQuote() { + return forceQuote; + } + + /** + * Enable/disable quoting for all tables and column names. + * + * @param forceQuote + */ + public void setForceQuote(boolean forceQuote) { + this.forceQuote = forceQuote; + } + /* * (non-Javadoc) * @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation) */ @Override protected RelationalPersistentEntity createPersistentEntity(TypeInformation typeInformation) { - return new RelationalPersistentEntityImpl<>(typeInformation, this.namingStrategy); + + RelationalPersistentEntityImpl entity = new RelationalPersistentEntityImpl<>(typeInformation, + this.namingStrategy); + entity.setForceQuote(isForceQuote()); + + return entity; } /* @@ -75,6 +100,11 @@ protected RelationalPersistentEntity createPersistentEntity(TypeInformati @Override protected RelationalPersistentProperty createPersistentProperty(Property property, RelationalPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { - return new BasicRelationalPersistentProperty(property, owner, simpleTypeHolder, this); + + BasicRelationalPersistentProperty persistentProperty = new BasicRelationalPersistentProperty(property, owner, + simpleTypeHolder, this); + persistentProperty.setForceQuote(isForceQuote()); + + return persistentProperty; } } 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 6009aefa85..04ba5dd91f 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; + private boolean forceQuote = true; /** * Creates a new {@link RelationalPersistentEntityImpl} for the given {@link TypeInformation}. @@ -50,16 +52,33 @@ class RelationalPersistentEntityImpl extends BasicPersistentEntity namingStrategy.getQualifiedTableName(getType())); + public SqlIdentifier getTableName() { + return tableName.get().orElseGet(() -> createDerivedSqlIdentifier(namingStrategy.getQualifiedTableName(getType()))); } /* @@ -67,7 +86,7 @@ public String getTableName() { * @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentEntity#getIdColumn() */ @Override - public String getIdColumn() { + public SqlIdentifier getIdColumn() { return getRequiredIdProperty().getColumnName(); } @@ -80,7 +99,7 @@ public String toString() { return String.format("JdbcPersistentEntityImpl<%s>", getType()); } - /* + /* * (non-Javadoc) * @see org.springframework.data.mapping.model.BasicPersistentEntity#setPersistentPropertyAccessorFactory(org.springframework.data.mapping.model.PersistentPropertyAccessorFactory) */ diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java index c829358e4c..484eabf228 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/RelationalPersistentProperty.java @@ -16,6 +16,7 @@ package org.springframework.data.relational.core.mapping; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.relational.domain.SqlIdentifier; import org.springframework.lang.Nullable; /** @@ -34,7 +35,7 @@ public interface RelationalPersistentProperty extends PersistentProperty 0, "SqlIdentifier parts must not be empty"); + + this.parts = parts; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#transform(java.util.function.UnaryOperator) + */ + @Override + public SqlIdentifier transform(UnaryOperator transformationFunction) { + throw new UnsupportedOperationException("Composite SQL Identifiers cannot be transformed"); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#toSql(org.springframework.data.relational.domain.IdentifierProcessing) + */ + @Override + public String toSql(IdentifierProcessing processing) { + + StringJoiner stringJoiner = new StringJoiner("."); + + for (SqlIdentifier namePart : parts) { + stringJoiner.add(namePart.toSql(processing)); + } + + return stringJoiner.toString(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#getReference(org.springframework.data.relational.domain.IdentifierProcessing) + */ + @Override + public String getReference(IdentifierProcessing processing) { + throw new UnsupportedOperationException("A Composite SQL Identifiers can't be used as a reference name"); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o instanceof SqlIdentifier) { + return toString().equals(o.toString()); + } + return false; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return toString().hashCode(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return toSql(IdentifierProcessing.ANSI); + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/DefaultIdentifierProcessing.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/DefaultIdentifierProcessing.java new file mode 100644 index 0000000000..7e22257716 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/DefaultIdentifierProcessing.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 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 {@link IdentifierProcessing} implementation based on two implementations for the quoting and for the letter case + * standardization. + * + * @author Jens Schauder + * @since 2.0 + */ +class DefaultIdentifierProcessing implements IdentifierProcessing { + + private final Quoting quoting; + private final LetterCasing letterCasing; + + DefaultIdentifierProcessing(Quoting quoting, LetterCasing letterCasing) { + this.quoting = quoting; + this.letterCasing = letterCasing; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.IdentifierProcessing#quote(java.lang.String) + */ + @Override + public String quote(String identifier) { + return quoting.apply(identifier); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.IdentifierProcessing#standardizeLetterCase(java.lang.String) + */ + @Override + public String standardizeLetterCase(String identifier) { + return letterCasing.apply(identifier); + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/DefaultSqlIdentifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/DefaultSqlIdentifier.java new file mode 100644 index 0000000000..4679ea2f69 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/DefaultSqlIdentifier.java @@ -0,0 +1,104 @@ +/* + * Copyright 2020 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.function.UnaryOperator; + +import org.springframework.util.Assert; + +/** + * Default {@link SqlIdentifier} implementation using a {@code name} and whether the identifier is quoted. + * + * @author Jens Schauder + * @author Mark Paluch + * @since 2.0 + */ +class DefaultSqlIdentifier implements SqlIdentifier { + + private final String name; + private final boolean quoted; + + DefaultSqlIdentifier(String name, boolean quoted) { + + Assert.hasText(name, "A database object must have at least on name part."); + + this.name = name; + this.quoted = quoted; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#transform(java.util.function.UnaryOperator) + */ + @Override + public SqlIdentifier transform(UnaryOperator transformationFunction) { + + Assert.notNull(transformationFunction, "Transformation function must not be null"); + + return new DefaultSqlIdentifier(transformationFunction.apply(name), quoted); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#toSql(org.springframework.data.relational.domain.IdentifierProcessing) + */ + @Override + public String toSql(IdentifierProcessing processing) { + return quoted ? processing.quote(getReference(processing)) : getReference(processing); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.relational.domain.SqlIdentifier#getReference(org.springframework.data.relational.domain.IdentifierProcessing) + */ + @Override + public String getReference(IdentifierProcessing processing) { + return name; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + + if (this == o) + return true; + if (o instanceof SqlIdentifier) { + return toString().equals(o.toString()); + } + return false; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return toString().hashCode(); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return toSql(IdentifierProcessing.ANSI); + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/Identifier.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/Identifier.java index 655973eb49..7922779991 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/Identifier.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/Identifier.java @@ -69,9 +69,9 @@ public static Identifier empty() { * @param targetType must not be {@literal null}. * @return the {@link Identifier} for {@code name}, {@code value}, and a {@link Class target type}. */ - public static Identifier of(String name, Object value, Class 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 static Identifier of(String name, Object value, Class targetType) { * @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 static Identifier from(Map map) { * @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 Identifier withPart(String name, Object value, Class targetType) { * * @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 int size() { @AllArgsConstructor(access = AccessLevel.PRIVATE) static class SingleIdentifierValue { - String name; + SqlIdentifier name; Object value; Class targetType; } @@ -204,11 +204,11 @@ public interface IdentifierConsumer { /** * 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 0000000000..0ee86f594b --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/IdentifierProcessing.java @@ -0,0 +1,144 @@ +/* + * 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 = create(Quoting.ANSI, LetterCasing.UPPER_CASE); + + /** + * An {@link IdentifierProcessing} without applying transformations. + */ + IdentifierProcessing NONE = create(Quoting.NONE, LetterCasing.AS_IS); + + /** + * Create a {@link IdentifierProcessing} rule given {@link Quoting} and {@link LetterCasing} rules. + * + * @param quoting quoting rules. + * @param letterCasing {@link LetterCasing} rules for identifier normalization. + * @return a new {@link IdentifierProcessing} object. + */ + static DefaultIdentifierProcessing create(Quoting quoting, LetterCasing letterCasing) { + return new DefaultIdentifierProcessing(quoting, letterCasing); + } + + /** + * 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); + + /** + * A conversion from unquoted identifiers to quoted identifiers. + * + * @author Jens Schauder + * @since 2.0 + */ + class Quoting { + + public static final Quoting ANSI = new Quoting("\""); + + public static final Quoting NONE = 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 0000000000..4536c42756 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/SqlIdentifier.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.domain; + +import java.util.function.UnaryOperator; + +/** + * Represents a named object that exists in the database like a table name or a column name. SQL identifiers are created + * from a {@link String name} with specifying whether the name should be quoted or unquoted. + *

+ * {@link SqlIdentifier} renders its name using {@link IdentifierProcessing} rules. Use + * {@link #getReference(IdentifierProcessing)} to refer to an object using the identifier when e.g. obtaining values + * from a result or providing values for a prepared statement. {@link #toSql(IdentifierProcessing)} renders the + * identifier for SQL statement usage. + *

+ * {@link SqlIdentifier} objects are immutable. Calling transformational methods such as + * {@link #transform(UnaryOperator)} creates a new instance. + * + * @author Jens Schauder + * @author Mark Paluch + * @since 2.0 + */ +public interface SqlIdentifier { + + /** + * Null-object. + */ + SqlIdentifier EMPTY = new SqlIdentifier() { + + @Override + public SqlIdentifier transform(UnaryOperator transformationFunction) { + return this; + } + + @Override + public String toSql(IdentifierProcessing processing) { + throw new UnsupportedOperationException("An empty SqlIdentifier can't be used in to create SQL snippets"); + } + + @Override + public String getReference(IdentifierProcessing processing) { + throw new UnsupportedOperationException("An empty SqlIdentifier can't be used in to create column names"); + } + + public String toString() { + return ""; + } + }; + + /** + * Return the reference name after applying {@link IdentifierProcessing} rules. The reference name is used for + * programmatic access to the object identified by this {@link SqlIdentifier}. + * + * @param processing identifier processing rules. + * @return + */ + String getReference(IdentifierProcessing processing); + + /** + * Return the identifier for SQL usage after applying {@link IdentifierProcessing} rules. The identifier name is used + * to construct SQL statements. + * + * @param processing identifier processing rules. + * @return + */ + String toSql(IdentifierProcessing processing); + + /** + * Transform the SQL identifier name by applying a {@link UnaryOperator transformation function}. The transformation + * function must return a valid, {@literal non-null} identifier {@link String}. + * + * @param transformationFunction the transformation function. Must return a {@literal non-null} identifier + * {@link String}. + * @return a new {@link SqlIdentifier} with the transformation applied. + */ + SqlIdentifier transform(UnaryOperator transformationFunction); + + /** + * Create a new quoted identifier given {@code name}. + * + * @param name the identifier. + * @return a new quoted identifier given {@code name}. + */ + static SqlIdentifier quoted(String name) { + return new DefaultSqlIdentifier(name, true); + } + + /** + * Create a new unquoted identifier given {@code name}. + * + * @param name the identifier. + * @return a new unquoted identifier given {@code name}. + */ + static SqlIdentifier unquoted(String name) { + return new DefaultSqlIdentifier(name, false); + } + + /** + * Create a new composite {@link SqlIdentifier} from one or more {@link SqlIdentifier}s. + *

+ * Composite identifiers do not allow {@link #transform(UnaryOperator)} transformation. + *

+ * + * @param sqlIdentifiers the elements of the new identifier. + * @return the new composite identifier. + */ + static SqlIdentifier from(SqlIdentifier... sqlIdentifiers) { + return new CompositeSqlIdentifier(sqlIdentifiers); + } +} 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 55620b4b9f..049fc1b8fe 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 36fec02061..4635f966a7 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 @@ 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 getJavaType() { /* (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 9ec5a9baff..39b583b507 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 @@ private MappingRelationalEntityInformation(RelationalPersistentEntity entity, 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 0000000000..818bf406ae --- /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 0000000000..4a2e97053b --- /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 4bb6b5ee9d..5e6d94ac44 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 void shouldRenderLimitOffset() { 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 c9855910f4..b727339272 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 void testTargetTypesForPropertyType() { @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 void detectsAnnotatedColumnAndKeyName() { 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 void classificationOfCollectionLikeProperties() { .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 @@ private static class DummyEntity { 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/DerivedSqlIdentifierUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifierUnitTests.java new file mode 100644 index 0000000000..4b40445984 --- /dev/null +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifierUnitTests.java @@ -0,0 +1,86 @@ +/* + * 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 org.assertj.core.api.SoftAssertions; +import org.junit.Test; + +import org.springframework.data.relational.domain.IdentifierProcessing; +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 {@link DerivedSqlIdentifier}. + * + * @author Jens Schauder + * @author Mark Paluch + */ +public class DerivedSqlIdentifierUnitTests { + + public static final IdentifierProcessing BRACKETS_LOWER_CASE = IdentifierProcessing.create(new Quoting("[", "]"), + LetterCasing.LOWER_CASE); + + @Test // DATAJDBC-386 + public void quotedSimpleObjectIdentifierWithAdjustableLetterCasing() { + + SqlIdentifier identifier = new DerivedSqlIdentifier("someName", true); + + assertThat(identifier.toSql(BRACKETS_LOWER_CASE)).isEqualTo("[somename]"); + assertThat(identifier.getReference(BRACKETS_LOWER_CASE)).isEqualTo("somename"); + + } + + @Test // DATAJDBC-386 + public void unquotedSimpleObjectIdentifierWithAdjustableLetterCasing() { + + SqlIdentifier identifier = new DerivedSqlIdentifier("someName", false); + String sql = identifier.toSql(BRACKETS_LOWER_CASE); + + assertThat(sql).isEqualTo("somename"); + assertThat(identifier.getReference(BRACKETS_LOWER_CASE)).isEqualTo("somename"); + } + + @Test // DATAJDBC-386 + public void quotedMultipartObjectIdentifierWithAdjustableLetterCase() { + + SqlIdentifier identifier = SqlIdentifier.from(new DerivedSqlIdentifier("some", true), + new DerivedSqlIdentifier("name", true)); + String sql = identifier.toSql(IdentifierProcessing.ANSI); + + assertThat(sql).isEqualTo("\"SOME\".\"NAME\""); + } + + @Test // DATAJDBC-386 + public void equality() { + + SqlIdentifier basis = new DerivedSqlIdentifier("simple", false); + SqlIdentifier equal = new DerivedSqlIdentifier("simple", false); + SqlIdentifier quoted = new DerivedSqlIdentifier("simple", true); + SqlIdentifier notSimple = SqlIdentifier.from(new DerivedSqlIdentifier("simple", false), + new DerivedSqlIdentifier("not", false)); + + 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/core/mapping/NamingStrategyUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/NamingStrategyUnitTests.java index 852b982605..bb30fd2c63 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 @@ -21,8 +21,10 @@ 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}. @@ -69,7 +71,7 @@ public void getKeyColumn() { @Test public void getSchema() { - assertThat(target.getSchema()).isEmpty(); + assertThat(target.getSchema()).isEqualTo(""); } @Test 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 74b644b38a..1ca3030dd2 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,13 +16,14 @@ 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; /** * Unit tests for {@link RelationalPersistentEntityImpl}. - * + * * @author Oliver Gierke * @author Kazuki Shimizu * @author Bastian Wilhelm @@ -36,7 +37,7 @@ public void discoversAnnotatedTableName() { 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 void considerIdColumnName() { 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 void emptyTableAnnotationFallsBackToNamingStrategy() { 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/domain/DefaultIdentifierProcessingUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/domain/DefaultIdentifierProcessingUnitTests.java new file mode 100644 index 0000000000..399ef39dab --- /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.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 = IdentifierProcessing.create(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 = IdentifierProcessing.create(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 c9363f239a..340187bede 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 void forEachIteratesOverKeys() { 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 void forEachIteratesOverKeys() { @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); diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/domain/SqlIdentifierUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/domain/SqlIdentifierUnitTests.java new file mode 100644 index 0000000000..67c4cc6ea3 --- /dev/null +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/domain/SqlIdentifierUnitTests.java @@ -0,0 +1,91 @@ +/* + * 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 static org.springframework.data.relational.domain.SqlIdentifier.*; + +import org.assertj.core.api.SoftAssertions; +import org.junit.Test; + +import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing; +import org.springframework.data.relational.domain.IdentifierProcessing.Quoting; + +/** + * Unit tests for {@link SqlIdentifier}. + * + * @author Jens Schauder + * @author Mark Paluch + */ +public class SqlIdentifierUnitTests { + + public static final IdentifierProcessing BRACKETS_LOWER_CASE = IdentifierProcessing.create(new Quoting("[", "]"), + LetterCasing.LOWER_CASE); + + @Test // DATAJDBC-386 + public void quotedSimpleObjectIdentifier() { + + SqlIdentifier identifier = quoted("someName"); + + assertThat(identifier.toSql(BRACKETS_LOWER_CASE)).isEqualTo("[someName]"); + assertThat(identifier.getReference(BRACKETS_LOWER_CASE)).isEqualTo("someName"); + } + + @Test // DATAJDBC-386 + public void unquotedSimpleObjectIdentifier() { + + SqlIdentifier identifier = unquoted("someName"); + String sql = identifier.toSql(BRACKETS_LOWER_CASE); + + assertThat(sql).isEqualTo("someName"); + assertThat(identifier.getReference(BRACKETS_LOWER_CASE)).isEqualTo("someName"); + } + + @Test // DATAJDBC-386 + public void quotedMultipartObjectIdentifier() { + + SqlIdentifier identifier = SqlIdentifier.from(quoted("some"), quoted("name")); + String sql = identifier.toSql(IdentifierProcessing.ANSI); + + assertThat(sql).isEqualTo("\"some\".\"name\""); + } + + @Test // DATAJDBC-386 + public void unquotedMultipartObjectIdentifier() { + + SqlIdentifier identifier = SqlIdentifier.from(unquoted("some"), 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.from(unquoted("simple"), 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); + }); + } +}