DATAJDBC-386 - Introduce quoting for all database identifier.

All database identifiers, i.e. table names, column names and so on, now get quoted.
For most databases this means they get enclosed with double quotes.
For some databases this makes the the identifiers case sensitive.
In order to minimize the impact we convert identifiers their default letter casing.
This should be upper case according to the SQL standard but isn't for some databases.

The exact behavior regarding quoting and default letter casing gets controlled by a database specific `Dialect`.

Future changes will make the quoting of annotated columns and the default quoting behavior configurable.

Original pull request: #182.
This commit is contained in:
Jens Schauder
2019-12-02 14:50:53 +01:00
committed by Mark Paluch
parent a7bb561dec
commit 558e7386dc
73 changed files with 2418 additions and 1119 deletions

View File

@@ -266,7 +266,7 @@ class DefaultJdbcInterpreter implements Interpreter {
.getRequiredPersistentEntity(idOwningAction.getEntityType());
Object identifier = persistentEntity.getIdentifierAccessor(idOwningAction.getEntity()).getIdentifier();
Assert.state(identifier != null, "Couldn't get obtain a required id value");
Assert.state(identifier != null, "Couldn't obtain a required id value");
return identifier;
}

View File

@@ -37,12 +37,14 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.relational.core.conversion.BasicRelationalConverter;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.Embedded;
import org.springframework.data.relational.core.mapping.Embedded.OnEmpty;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
@@ -69,6 +71,7 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
private static final Converter<Iterable<?>, Map<?, ?>> ITERABLE_OF_ENTRY_TO_MAP_CONVERTER = new IterableOfEntryToMapConverter();
private final JdbcTypeFactory typeFactory;
private final IdentifierProcessing identifierProcessing = HsqlDbDialect.INSTANCE.getIdentifierProcessing();
private RelationResolver relationResolver;
@@ -374,7 +377,8 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
return readEntityFrom(property, path);
}
Object value = getObjectFromResultSet(path.extendBy(property).getColumnAlias());
Object value = getObjectFromResultSet(
path.extendBy(property).getColumnAlias().toColumnName(identifierProcessing));
return readValue(value, property.getTypeInformation());
}
@@ -428,7 +432,8 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
if (idProperty != null) {
idValue = newContext.readFrom(idProperty);
} else {
idValue = newContext.getObjectFromResultSet(path.extendBy(property).getReverseColumnNameAlias());
idValue = newContext.getObjectFromResultSet(
path.extendBy(property).getReverseColumnNameAlias().toColumnName(identifierProcessing));
}
if (idValue == null) {

View File

@@ -24,6 +24,7 @@ import java.util.function.Function;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Delegates each methods to the {@link DataAccessStrategy}s passed to the constructor in turn until the first that does
@@ -47,7 +48,7 @@ public class CascadingDataAccessStrategy implements DataAccessStrategy {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
public <T> Object insert(T instance, Class<T> domainType, Map<SqlIdentifier, Object> additionalParameters) {
return collect(das -> das.insert(instance, domainType, additionalParameters));
}

View File

@@ -22,6 +22,7 @@ import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.lang.Nullable;
/**
@@ -46,7 +47,8 @@ public interface DataAccessStrategy extends RelationResolver {
* @deprecated since 1.1, use {@link #insert(Object, Class, Identifier)} instead.
*/
@Deprecated
<T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters);
@Nullable
<T> Object insert(T instance, Class<T> domainType, Map<SqlIdentifier, Object> additionalParameters);
/**
* Inserts a the data of a single entity. Referenced entities don't get handled.
@@ -76,8 +78,8 @@ public interface DataAccessStrategy extends RelationResolver {
<T> boolean update(T instance, Class<T> 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.
* <P>
* The statement will be of the form : {@code UPDATE … SET … WHERE ID = :id and VERSION_COLUMN = :previousVersion }
* and throw an optimistic record locking exception if no rows have been updated.
@@ -87,7 +89,8 @@ public interface DataAccessStrategy extends RelationResolver {
* @param previousVersion The previous version assigned to the instance being saved.
* @param <T> 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
*/
<T> boolean updateWithVersion(T instance, Class<T> domainType, Number previousVersion);
@@ -113,7 +116,8 @@ public interface DataAccessStrategy extends RelationResolver {
* @param domainType the type of entity to be deleted. Implicitly determines the table to operate on. Must not be
* {@code null}.
* @param previousVersion The previous version assigned to the instance being saved.
* @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the optimistic locking version check failed.
* @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the
* optimistic locking version check failed.
* @since 2.0
*/
<T> void deleteWithVersion(Object id, Class<T> domainType, Number previousVersion);

View File

@@ -15,13 +15,11 @@
*/
package org.springframework.data.jdbc.core.convert;
import static org.springframework.data.jdbc.core.convert.SqlGenerator.VERSION_SQL_PARAMETER_NAME;
import static org.springframework.data.jdbc.core.convert.SqlGenerator.*;
import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -36,15 +34,16 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.relational.core.conversion.RelationalEntityVersionUtils;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.KeyHolder;
@@ -98,7 +97,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
@Nullable
public <T> Object insert(T instance, Class<T> domainType, Map<SqlIdentifier, Object> additionalParameters) {
return insert(instance, domainType, Identifier.from(additionalParameters));
}
@@ -109,11 +109,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
KeyHolder holder = new GeneratedKeyHolder();
SqlGenerator sqlGenerator = sql(domainType);
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
MapSqlParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
PersistentProperty::isIdProperty);
SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
PersistentProperty::isIdProperty, getIdentifierProcessing());
identifier.forEach((name, value, type) -> addConvertedPropertyValue(parameterSource, name, value, type));
@@ -124,8 +124,10 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
addConvertedPropertyValue(parameterSource, idProperty, idValue, idProperty.getColumnName());
}
KeyHolder holder = new GeneratedKeyHolder();
operations.update( //
sql(domainType).getInsert(new HashSet<>(Arrays.asList(parameterSource.getParameterNames()))), //
sqlGenerator.getInsert(new HashSet<>(parameterSource.getIdentifiers())), //
parameterSource, //
holder //
);
@@ -142,7 +144,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
RelationalPersistentEntity<S> persistentEntity = getRequiredPersistentEntity(domainType);
return operations.update(sql(domainType).getUpdate(),
getParameterSource(instance, persistentEntity, "", Predicates.includeAll())) != 0;
getParameterSource(instance, persistentEntity, "", Predicates.includeAll(), getIdentifierProcessing())) != 0;
}
/*
@@ -155,13 +157,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
RelationalPersistentEntity<S> persistentEntity = getRequiredPersistentEntity(domainType);
// Adjust update statement to set the new version and use the old version in where clause.
MapSqlParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
Predicates.includeAll());
parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, previousVersion);
SqlIdentifierParameterSource parameterSource = getParameterSource(instance, persistentEntity, "",
Predicates.includeAll(), getIdentifierProcessing());
parameterSource.addValue(VERSION_SQL_PARAMETER, previousVersion);
int affectedRows = operations.update(sql(domainType).getUpdateWithVersion(), parameterSource);
if (affectedRows == 0) {
throw new OptimisticLockingFailureException(
String.format("Optimistic lock exception on saving entity of type %s.", persistentEntity.getName()));
}
@@ -177,7 +180,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
public void delete(Object id, Class<?> domainType) {
String deleteByIdSql = sql(domainType).getDeleteById();
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
SqlParameterSource parameter = createIdParameterSource(id, domainType);
operations.update(deleteByIdSql, parameter);
}
@@ -193,8 +196,8 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
MapSqlParameterSource parameterSource = createIdParameterSource(id, domainType);
parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, previousVersion);
SqlIdentifierParameterSource parameterSource = createIdParameterSource(id, domainType);
parameterSource.addValue(VERSION_SQL_PARAMETER, previousVersion);
int affectedRows = operations.update(sql(domainType).getDeleteByIdAndVersion(), parameterSource);
if (affectedRows == 0) {
@@ -216,11 +219,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
RelationalPersistentProperty referencingProperty = propertyPath.getLeafProperty();
Assert.notNull(referencingProperty, "No property found matching the PropertyPath " + propertyPath);
String format = sql(rootEntity.getType()).createDeleteByPath(propertyPath);
String delete = sql(rootEntity.getType()).createDeleteByPath(propertyPath);
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("rootId", rootId);
operations.update(format, parameters);
SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(getIdentifierProcessing());
parameters.addValue(ROOT_ID_PARAMETER, rootId);
operations.update(delete, parameters);
}
/*
@@ -265,7 +268,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
public <T> T findById(Object id, Class<T> domainType) {
String findOneSql = sql(domainType).getFindOne();
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
SqlIdentifierParameterSource parameter = createIdParameterSource(id, domainType);
try {
return operations.queryForObject(findOneSql, parameter, (RowMapper<T>) getEntityRowMapper(domainType));
@@ -297,9 +300,9 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
}
RelationalPersistentProperty idProperty = getRequiredPersistentEntity(domainType).getRequiredIdProperty();
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(getIdentifierProcessing());
addConvertedPropertyValuesAsList(parameterSource, idProperty, ids, "ids");
addConvertedPropertyValuesAsList(parameterSource, idProperty, ids, IDS_SQL_PARAMETER);
String findAllInListSql = sql(domainType).getFindAllInList();
@@ -324,12 +327,20 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
String findAllByProperty = sql(actualType) //
.getFindAllByProperty(identifier, path.getQualifierColumn(), path.isOrdered());
MapSqlParameterSource parameters = new MapSqlParameterSource(identifier.toMap());
RowMapper<?> rowMapper = path.isMap() ? this.getMapEntityRowMapper(path, identifier)
: this.getEntityRowMapper(path, identifier);
return operations.query(findAllByProperty, parameters, (RowMapper<Object>) rowMapper);
return operations.query(findAllByProperty, createParameterSource(identifier, getIdentifierProcessing()),
(RowMapper<Object>) rowMapper);
}
private SqlParameterSource createParameterSource(Identifier identifier, IdentifierProcessing identifierProcessing) {
SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(identifierProcessing);
identifier.toMap().forEach(parameterSource::addValue);
return parameterSource;
}
/*
@@ -355,7 +366,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
public <T> boolean existsById(Object id, Class<T> domainType) {
String existsSql = sql(domainType).getExists();
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
SqlParameterSource parameter = createIdParameterSource(id, domainType);
Boolean result = operations.queryForObject(existsSql, parameter, Boolean.class);
Assert.state(result != null, "The result of an exists query must not be null");
@@ -363,10 +374,11 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
return result;
}
private <S, T> MapSqlParameterSource getParameterSource(@Nullable S instance, RelationalPersistentEntity<S> persistentEntity,
String prefix, Predicate<RelationalPersistentProperty> skipProperty) {
private <S, T> SqlIdentifierParameterSource getParameterSource(@Nullable S instance,
RelationalPersistentEntity<S> persistentEntity, String prefix,
Predicate<RelationalPersistentProperty> skipProperty, IdentifierProcessing identifierProcessing) {
MapSqlParameterSource parameters = new MapSqlParameterSource();
SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing);
PersistentPropertyAccessor<S> propertyAccessor = instance != null ? persistentEntity.getPropertyAccessor(instance)
: NoValuePropertyAccessor.instance();
@@ -384,13 +396,14 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
Object value = propertyAccessor.getProperty(property);
RelationalPersistentEntity<?> embeddedEntity = context.getPersistentEntity(property.getType());
MapSqlParameterSource additionalParameters = getParameterSource((T) value,
(RelationalPersistentEntity<T>) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty);
parameters.addValues(additionalParameters.getValues());
SqlIdentifierParameterSource additionalParameters = getParameterSource((T) value,
(RelationalPersistentEntity<T>) embeddedEntity, prefix + property.getEmbeddedPrefix(), skipProperty,
identifierProcessing);
parameters.addAll(additionalParameters);
} else {
Object value = propertyAccessor.getProperty(property);
String paramName = prefix + property.getColumnName();
SqlIdentifier paramName = property.getColumnName().prefix(prefix);
addConvertedPropertyValue(parameters, property, value, paramName);
}
@@ -434,7 +447,7 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
return null;
}
return keys.get(persistentEntity.getIdColumn());
return keys.get(persistentEntity.getIdColumn().toColumnName(getIdentifierProcessing()));
}
}
@@ -448,55 +461,58 @@ public class DefaultDataAccessStrategy implements DataAccessStrategy {
private RowMapper<?> getMapEntityRowMapper(PersistentPropertyPathExtension path, Identifier identifier) {
String keyColumn = path.getQualifierColumn();
SqlIdentifier keyColumn = path.getQualifierColumn();
Assert.notNull(keyColumn, () -> "KeyColumn must not be null for " + path);
return new MapEntityRowMapper<>(path, converter, identifier, keyColumn);
return new MapEntityRowMapper<>(path, converter, identifier, keyColumn, getIdentifierProcessing());
}
private <T> MapSqlParameterSource createIdParameterSource(Object id, Class<T> domainType) {
private <T> SqlIdentifierParameterSource createIdParameterSource(Object id, Class<T> domainType) {
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
SqlIdentifierParameterSource parameterSource = new SqlIdentifierParameterSource(getIdentifierProcessing());
addConvertedPropertyValue( //
parameterSource, //
getRequiredPersistentEntity(domainType).getRequiredIdProperty(), //
id, //
"id" //
ID_SQL_PARAMETER //
);
return parameterSource;
}
private void addConvertedPropertyValue(MapSqlParameterSource parameterSource, RelationalPersistentProperty property,
@Nullable Object value, String paramName) {
JdbcValue jdbcValue = converter.writeJdbcValue( //
value, //
property.getColumnType(), //
property.getSqlType() //
);
parameterSource.addValue(paramName, jdbcValue.getValue(), JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType()));
private IdentifierProcessing getIdentifierProcessing() {
return sqlGeneratorSource.getDialect().getIdentifierProcessing();
}
private void addConvertedPropertyValue(MapSqlParameterSource parameterSource, String name, Object value,
Class<?> type) {
private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource,
RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) {
addConvertedValue(parameterSource, value, name, property.getColumnType(), property.getSqlType());
}
private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value,
Class<?> javaType) {
addConvertedValue(parameterSource, value, name, javaType, JdbcUtil.sqlTypeFor(javaType));
}
private void addConvertedValue(SqlIdentifierParameterSource parameterSource, @Nullable Object value,
SqlIdentifier paramName, Class<?> javaType, int sqlType) {
JdbcValue jdbcValue = converter.writeJdbcValue( //
value, //
type, //
JdbcUtil.sqlTypeFor(type) //
javaType, //
sqlType //
);
parameterSource.addValue( //
name, //
paramName, //
jdbcValue.getValue(), //
JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType()) //
);
JdbcUtil.sqlTypeFor(jdbcValue.getJdbcType()));
}
private void addConvertedPropertyValuesAsList(MapSqlParameterSource parameterSource,
RelationalPersistentProperty property, Iterable<?> values, String paramName) {
private void addConvertedPropertyValuesAsList(SqlIdentifierParameterSource parameterSource,
RelationalPersistentProperty property, Iterable<?> values, SqlIdentifier paramName) {
List<Object> convertedIds = new ArrayList<>();
JdbcValue jdbcValue = null;

View File

@@ -20,6 +20,7 @@ import java.util.Map;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.util.Assert;
/**
@@ -39,7 +40,7 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
public <T> Object insert(T instance, Class<T> domainType, Map<SqlIdentifier, Object> additionalParameters) {
return delegate.insert(instance, domainType, additionalParameters);
}
@@ -70,6 +71,7 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
return delegate.updateWithVersion(instance, domainType, nextVersion);
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#delete(java.lang.Object, org.springframework.data.mapping.PersistentPropertyPath)

View File

@@ -24,6 +24,8 @@ import java.util.Map;
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.lang.NonNull;
@@ -40,13 +42,14 @@ class MapEntityRowMapper<T> implements RowMapper<Map.Entry<Object, T>> {
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<Object, T> mapRow(ResultSet rs, int rowNum) throws SQLException {
Object key = rs.getObject(keyColumn);
Object key = rs.getObject(keyColumn.toColumnName(identifierProcessing));
return new HashMap.SimpleEntry<>(key, mapEntity(rs, key));
}

View File

@@ -20,6 +20,8 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentEnti
import org.springframework.data.relational.core.sql.Column;
import org.springframework.data.relational.core.sql.SQL;
import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Utility to get from path to SQL DSL elements.
@@ -33,36 +35,41 @@ class SqlContext {
private final RelationalPersistentEntity<?> entity;
private final Table table;
private final IdentifierProcessing identifierProcessing;
SqlContext(RelationalPersistentEntity<?> entity) {
SqlContext(RelationalPersistentEntity<?> entity, IdentifierProcessing identifierProcessing) {
this.identifierProcessing = identifierProcessing;
this.entity = entity;
this.table = SQL.table(entity.getTableName());
this.table = SQL.table(entity.getTableName().toSql(this.identifierProcessing));
}
Column getIdColumn() {
return table.column(entity.getIdColumn());
return table.column(entity.getIdColumn().toSql(identifierProcessing));
}
Column getVersionColumn() {
return table.column(entity.getRequiredVersionProperty().getColumnName());
return table.column(entity.getRequiredVersionProperty().getColumnName().toSql(identifierProcessing));
}
Table getTable() {
return table;
}
Table getTable(PersistentPropertyPathExtension path) {
String tableAlias = path.getTableAlias();
Table table = SQL.table(path.getTableName());
return tableAlias == null ? table : table.as(tableAlias);
SqlIdentifier tableAlias = path.getTableAlias();
Table table = SQL.table(path.getTableName().toSql(identifierProcessing));
return tableAlias == null ? table : table.as(tableAlias.toSql(identifierProcessing));
}
Column getColumn(PersistentPropertyPathExtension path) {
return getTable(path).column(path.getColumnName()).as(path.getColumnAlias());
return getTable(path).column(path.getColumnName().toSql(identifierProcessing))
.as(path.getColumnAlias().toSql(identifierProcessing));
}
Column getReverseColumn(PersistentPropertyPathExtension path) {
return getTable(path).column(path.getReverseColumnName()).as(path.getReverseColumnNameAlias());
return getTable(path).column(path.getReverseColumnName().toSql(identifierProcessing))
.as(path.getReverseColumnNameAlias().toSql(identifierProcessing));
}
}

View File

@@ -17,14 +17,7 @@ package org.springframework.data.jdbc.core.convert;
import lombok.Value;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -40,6 +33,8 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp
import org.springframework.data.relational.core.sql.*;
import org.springframework.data.relational.core.sql.render.SqlRenderer;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -57,11 +52,15 @@ import org.springframework.util.Assert;
*/
class SqlGenerator {
static final String VERSION_SQL_PARAMETER_NAME = "___oldOptimisticLockingVersion";
static final SqlIdentifier VERSION_SQL_PARAMETER = SqlIdentifier.unquoted("___oldOptimisticLockingVersion");
static final SqlIdentifier ID_SQL_PARAMETER = SqlIdentifier.unquoted("id");
static final SqlIdentifier IDS_SQL_PARAMETER = SqlIdentifier.unquoted("ids");
static final SqlIdentifier ROOT_ID_PARAMETER = SqlIdentifier.unquoted("rootId");
private static final Pattern parameterPattern = Pattern.compile("\\W");
private final RelationalPersistentEntity<?> entity;
private final MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext;
private final IdentifierProcessing identifierProcessing;
private final SqlContext sqlContext;
private final Columns columns;
@@ -82,15 +81,18 @@ class SqlGenerator {
/**
* Create a new {@link SqlGenerator} given {@link RelationalMappingContext} and {@link RelationalPersistentEntity}.
*
*
* @param mappingContext must not be {@literal null}.
* @param entity must not be {@literal null}.
* @param identifierProcessing must not be {@literal null}.
*/
SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity<?> entity) {
SqlGenerator(RelationalMappingContext mappingContext, RelationalPersistentEntity<?> entity,
IdentifierProcessing identifierProcessing) {
this.mappingContext = mappingContext;
this.entity = entity;
this.sqlContext = new SqlContext(entity);
this.identifierProcessing = identifierProcessing;
this.sqlContext = new SqlContext(entity, identifierProcessing);
this.columns = new Columns(entity, mappingContext);
}
@@ -103,7 +105,7 @@ class SqlGenerator {
* @param filterColumn the column to apply the IN-condition to.
* @return the IN condition
*/
private static Condition getSubselectCondition(PersistentPropertyPathExtension path,
private Condition getSubselectCondition(PersistentPropertyPathExtension path,
Function<Column, Condition> rootCondition, Column filterColumn) {
PersistentPropertyPathExtension parentPath = path.getParentPath();
@@ -115,9 +117,10 @@ class SqlGenerator {
return rootCondition.apply(filterColumn);
}
Table subSelectTable = SQL.table(parentPath.getTableName());
Column idColumn = subSelectTable.column(parentPath.getIdColumnName());
Column selectFilterColumn = subSelectTable.column(parentPath.getEffectiveIdColumnName());
Table subSelectTable = SQL.table(parentPath.getTableName().toSql(identifierProcessing));
Column idColumn = subSelectTable.column(parentPath.getIdColumnName().toSql(identifierProcessing));
Column selectFilterColumn = subSelectTable
.column(parentPath.getEffectiveIdColumnName().toSql(identifierProcessing));
Condition innerCondition;
@@ -139,8 +142,8 @@ class SqlGenerator {
return filterColumn.in(select);
}
private static BindMarker getBindMarker(String columnName) {
return SQL.bindMarker(":" + parameterPattern.matcher(columnName).replaceAll(""));
private BindMarker getBindMarker(SqlIdentifier columnName) {
return SQL.bindMarker(":" + parameterPattern.matcher(columnName.toColumnName(identifierProcessing)).replaceAll(""));
}
/**
@@ -174,7 +177,7 @@ class SqlGenerator {
* keyColumn must not be {@code null}.
* @return a SQL String.
*/
String getFindAllByProperty(Identifier parentIdentifier, @Nullable String keyColumn, boolean ordered) {
String getFindAllByProperty(Identifier parentIdentifier, @Nullable SqlIdentifier keyColumn, boolean ordered) {
Assert.isTrue(keyColumn != null || !ordered,
"If the SQL statement should be ordered a keyColumn to order by must be provided.");
@@ -182,7 +185,7 @@ class SqlGenerator {
SelectBuilder.SelectWhere builder = selectBuilder( //
keyColumn == null //
? Collections.emptyList() //
: Collections.singleton(keyColumn) //
: Collections.singleton(keyColumn.toSql(identifierProcessing)) //
);
Table table = getTable();
@@ -191,7 +194,9 @@ class SqlGenerator {
SelectBuilder.SelectWhereAndOr withWhereClause = builder.where(condition);
Select select = ordered //
? withWhereClause.orderBy(table.column(keyColumn).as(keyColumn)).build() //
? withWhereClause
.orderBy(table.column(keyColumn.toSql(identifierProcessing)).as(keyColumn.toSql(identifierProcessing)))
.build() //
: withWhereClause.build();
return render(select);
@@ -200,9 +205,10 @@ class SqlGenerator {
private Condition buildConditionForBackReference(Identifier parentIdentifier, Table table) {
Condition condition = null;
for (String backReferenceColumn : parentIdentifier.toMap().keySet()) {
for (SqlIdentifier backReferenceColumn : parentIdentifier.toMap().keySet()) {
Condition newCondition = table.column(backReferenceColumn).isEqualTo(getBindMarker(backReferenceColumn));
Condition newCondition = table.column(backReferenceColumn.toSql(identifierProcessing))
.isEqualTo(getBindMarker(backReferenceColumn));
condition = condition == null ? newCondition : condition.and(newCondition);
}
@@ -214,7 +220,7 @@ class SqlGenerator {
/**
* Create a {@code SELECT COUNT(id) FROM … WHERE :id = …} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getExists() {
return existsSql.get();
@@ -223,7 +229,7 @@ class SqlGenerator {
/**
* Create a {@code SELECT … FROM … WHERE :id = …} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getFindOne() {
return findOneSql.get();
@@ -232,16 +238,16 @@ class SqlGenerator {
/**
* Create a {@code INSERT INTO … (…) VALUES(…)} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getInsert(Set<String> additionalColumns) {
String getInsert(Set<SqlIdentifier> additionalColumns) {
return createInsertSql(additionalColumns);
}
/**
* Create a {@code UPDATE … SET …} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getUpdate() {
return updateSql.get();
@@ -250,7 +256,7 @@ class SqlGenerator {
/**
* Create a {@code UPDATE … SET … WHERE ID = :id and VERSION_COLUMN = :___oldOptimisticLockingVersion } statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getUpdateWithVersion() {
return updateWithVersionSql.get();
@@ -259,7 +265,7 @@ class SqlGenerator {
/**
* Create a {@code SELECT COUNT(*) FROM …} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getCount() {
return countSql.get();
@@ -268,7 +274,7 @@ class SqlGenerator {
/**
* Create a {@code DELETE FROM … WHERE :id = …} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getDeleteById() {
return deleteByIdSql.get();
@@ -277,7 +283,7 @@ class SqlGenerator {
/**
* Create a {@code DELETE FROM … WHERE :id = … and :___oldOptimisticLockingVersion = ...} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getDeleteByIdAndVersion() {
return deleteByIdAndVersionSql.get();
@@ -286,7 +292,7 @@ class SqlGenerator {
/**
* Create a {@code DELETE FROM … WHERE :ids in (…)} statement.
*
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String getDeleteByList() {
return deleteByListSql.get();
@@ -296,7 +302,7 @@ class SqlGenerator {
* Create a {@code DELETE} query and optionally filter by {@link PersistentPropertyPath}.
*
* @param path can be {@literal null}.
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String createDeleteAllSql(@Nullable PersistentPropertyPath<RelationalPersistentProperty> path) {
@@ -315,16 +321,16 @@ class SqlGenerator {
* Create a {@code DELETE} query and filter by {@link PersistentPropertyPath}.
*
* @param path must not be {@literal null}.
* @return
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
String createDeleteByPath(PersistentPropertyPath<RelationalPersistentProperty> path) {
return createDeleteByPathAndCriteria(new PersistentPropertyPathExtension(mappingContext, path),
filterColumn -> filterColumn.isEqualTo(getBindMarker("rootId")));
filterColumn -> filterColumn.isEqualTo(getBindMarker(ROOT_ID_PARAMETER)));
}
private String createFindOneSql() {
Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker("id"))) //
Select select = selectBuilder().where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
.build();
return render(select);
@@ -379,8 +385,8 @@ class SqlGenerator {
/**
* Create a {@link Column} for {@link PersistentPropertyPathExtension}.
*
* @param path
* @return
* @param path the path to the column in question.
* @return the statement as a {@link String}. Guaranteed to be not {@literal null}.
*/
@Nullable
Column getColumn(PersistentPropertyPathExtension path) {
@@ -425,14 +431,14 @@ class SqlGenerator {
return new Join( //
currentTable, //
currentTable.column(path.getReverseColumnName()), //
parentTable.column(idDefiningParentPath.getIdColumnName()) //
currentTable.column(path.getReverseColumnName().toSql(identifierProcessing)), //
parentTable.column(idDefiningParentPath.getIdColumnName().toSql(identifierProcessing)) //
);
}
private String createFindAllInListSql() {
Select select = selectBuilder().where(getIdColumn().in(getBindMarker("ids"))).build();
Select select = selectBuilder().where(getIdColumn().in(getBindMarker(IDS_SQL_PARAMETER))).build();
return render(select);
}
@@ -444,7 +450,7 @@ class SqlGenerator {
Select select = StatementBuilder //
.select(Functions.count(getIdColumn())) //
.from(table) //
.where(getIdColumn().isEqualTo(getBindMarker("id"))) //
.where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
.build();
return render(select);
@@ -462,21 +468,22 @@ class SqlGenerator {
return render(select);
}
private String createInsertSql(Set<String> additionalColumns) {
private String createInsertSql(Set<SqlIdentifier> additionalColumns) {
Table table = getTable();
Set<String> columnNamesForInsert = new LinkedHashSet<>(columns.getInsertableColumns());
Set<SqlIdentifier> columnNamesForInsert = new TreeSet<>(Comparator.comparing(id -> id.toSql(identifierProcessing)));
columnNamesForInsert.addAll(columns.getInsertableColumns());
columnNamesForInsert.addAll(additionalColumns);
InsertBuilder.InsertIntoColumnsAndValuesWithBuild insert = Insert.builder().into(table);
for (String cn : columnNamesForInsert) {
insert = insert.column(table.column(cn));
for (SqlIdentifier cn : columnNamesForInsert) {
insert = insert.column(table.column(cn.toSql(identifierProcessing)));
}
InsertBuilder.InsertValuesWithBuild insertWithValues = null;
for (String cn : columnNamesForInsert) {
for (SqlIdentifier cn : columnNamesForInsert) {
insertWithValues = (insertWithValues == null ? insert : insertWithValues).values(getBindMarker(cn));
}
@@ -490,7 +497,8 @@ class SqlGenerator {
private String createUpdateWithVersionSql() {
Update update = createBaseUpdate() //
.and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) //
.and(getVersionColumn()
.isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.toColumnName(identifierProcessing)))) //
.build();
return render(update);
@@ -503,7 +511,7 @@ class SqlGenerator {
List<AssignValue> assignments = columns.getUpdateableColumns() //
.stream() //
.map(columnName -> Assignments.value( //
table.column(columnName), //
table.column(columnName.toSql(identifierProcessing)), //
getBindMarker(columnName))) //
.collect(Collectors.toList());
@@ -520,7 +528,8 @@ class SqlGenerator {
private String createDeleteByIdAndVersionSql() {
Delete delete = createBaseDeleteById(getTable()) //
.and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) //
.and(getVersionColumn()
.isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER.toColumnName(identifierProcessing)))) //
.build();
return render(delete);
@@ -533,13 +542,13 @@ class SqlGenerator {
private String createDeleteByPathAndCriteria(PersistentPropertyPathExtension path,
Function<Column, Condition> rootCondition) {
Table table = SQL.table(path.getTableName());
Table table = SQL.table(path.getTableName().toSql(identifierProcessing));
DeleteBuilder.DeleteWhere builder = Delete.builder() //
.from(table);
Delete delete;
Column filterColumn = table.column(path.getReverseColumnName());
Column filterColumn = table.column(path.getReverseColumnName().toSql(identifierProcessing));
if (path.getLength() == 1) {
@@ -561,7 +570,7 @@ class SqlGenerator {
Delete delete = Delete.builder() //
.from(table) //
.where(getIdColumn().in(getBindMarker("ids"))) //
.where(getIdColumn().in(getBindMarker(IDS_SQL_PARAMETER))) //
.build();
return render(delete);
@@ -609,17 +618,18 @@ class SqlGenerator {
* Value object encapsulating column name caches.
*
* @author Mark Paluch
* @author Jens Schauder
*/
static class Columns {
private final MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext;
private final List<String> columnNames = new ArrayList<>();
private final List<String> idColumnNames = new ArrayList<>();
private final List<String> nonIdColumnNames = new ArrayList<>();
private final Set<String> readOnlyColumnNames = new HashSet<>();
private final Set<String> insertableColumns;
private final Set<String> updateableColumns;
private final List<SqlIdentifier> columnNames = new ArrayList<>();
private final List<SqlIdentifier> idColumnNames = new ArrayList<>();
private final List<SqlIdentifier> nonIdColumnNames = new ArrayList<>();
private final Set<SqlIdentifier> readOnlyColumnNames = new HashSet<>();
private final Set<SqlIdentifier> insertableColumns;
private final Set<SqlIdentifier> updateableColumns;
Columns(RelationalPersistentEntity<?> entity,
MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext) {
@@ -628,12 +638,12 @@ class SqlGenerator {
populateColumnNameCache(entity, "");
Set<String> insertable = new LinkedHashSet<>(nonIdColumnNames);
Set<SqlIdentifier> insertable = new LinkedHashSet<>(nonIdColumnNames);
insertable.removeAll(readOnlyColumnNames);
this.insertableColumns = Collections.unmodifiableSet(insertable);
Set<String> updateable = new LinkedHashSet<>(columnNames);
Set<SqlIdentifier> updateable = new LinkedHashSet<>(columnNames);
updateable.removeAll(idColumnNames);
updateable.removeAll(readOnlyColumnNames);
@@ -656,7 +666,7 @@ class SqlGenerator {
private void initSimpleColumnName(RelationalPersistentProperty property, String prefix) {
String columnName = prefix + property.getColumnName();
SqlIdentifier columnName = property.getColumnName().prefix(prefix);
columnNames.add(columnName);
@@ -684,14 +694,14 @@ class SqlGenerator {
/**
* @return Column names that can be used for {@code INSERT}.
*/
Set<String> getInsertableColumns() {
Set<SqlIdentifier> getInsertableColumns() {
return insertableColumns;
}
/**
* @return Column names that can be used for {@code UPDATE}.
*/
Set<String> getUpdateableColumns() {
Set<SqlIdentifier> getUpdateableColumns() {
return updateableColumns;
}
}

View File

@@ -19,7 +19,9 @@ import lombok.RequiredArgsConstructor;
import java.util.Map;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
@@ -33,8 +35,17 @@ public class SqlGeneratorSource {
private final Map<Class<?>, 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()));
}
}

View File

@@ -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<SqlIdentifier> identifiers = new HashSet<>();
private final Map<String, Object> 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<SqlIdentifier> getIdentifiers() {
return Collections.unmodifiableSet(identifiers);
}
void addValue(SqlIdentifier name, Object value) {
addValue(name, value, Integer.MIN_VALUE);
}
void addValue(SqlIdentifier identifier, Object value, int sqlType) {
identifiers.add(identifier);
String name = identifier.toColumnName(identifierProcessing);
namesToValues.put(name, value);
registerSqlType(name, sqlType);
}
void addAll(SqlIdentifierParameterSource others) {
for (SqlIdentifier identifier : others.getIdentifiers()) {
String name = identifier.toColumnName(identifierProcessing);
addValue(identifier, others.getValue(name), others.getSqlType(name));
}
}
}

View File

@@ -15,11 +15,11 @@
*/
package org.springframework.data.jdbc.mybatis;
import static java.util.Arrays.asList;
import static java.util.Arrays.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSession;
@@ -34,9 +34,12 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.util.Assert;
@@ -61,6 +64,7 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
private static final String VERSION_SQL_PARAMETER_NAME_OLD = "___oldOptimisticLockingVersion";
private final SqlSession sqlSession;
private final IdentifierProcessing identifierProcessing;
private NamespaceStrategy namespaceStrategy = NamespaceStrategy.DEFAULT_INSTANCE;
/**
@@ -68,8 +72,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
* uses a {@link DefaultDataAccessStrategy}
*/
public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingContext context,
JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession) {
return createCombinedAccessStrategy(context, converter, operations, sqlSession, NamespaceStrategy.DEFAULT_INSTANCE);
JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession, Dialect dialect) {
return createCombinedAccessStrategy(context, converter, operations, sqlSession, NamespaceStrategy.DEFAULT_INSTANCE,
dialect);
}
/**
@@ -78,19 +83,20 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
*/
public static DataAccessStrategy createCombinedAccessStrategy(RelationalMappingContext context,
JdbcConverter converter, NamedParameterJdbcOperations operations, SqlSession sqlSession,
NamespaceStrategy namespaceStrategy) {
NamespaceStrategy namespaceStrategy, Dialect dialect) {
// the DefaultDataAccessStrategy needs a reference to the returned DataAccessStrategy. This creates a dependency
// cycle. In order to create it, we need something that allows to defer closing the cycle until all the elements are
// created. That is the purpose of the DelegatingAccessStrategy.
DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy();
MyBatisDataAccessStrategy myBatisDataAccessStrategy = new MyBatisDataAccessStrategy(sqlSession, context, converter);
MyBatisDataAccessStrategy myBatisDataAccessStrategy = new MyBatisDataAccessStrategy(sqlSession,
dialect.getIdentifierProcessing());
myBatisDataAccessStrategy.setNamespaceStrategy(namespaceStrategy);
CascadingDataAccessStrategy cascadingDataAccessStrategy = new CascadingDataAccessStrategy(
asList(myBatisDataAccessStrategy, delegatingDataAccessStrategy));
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context);
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(context, dialect);
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy( //
sqlGeneratorSource, //
context, //
@@ -109,13 +115,17 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
* Use a {@link SqlSessionTemplate} for {@link SqlSession} or a similar implementation tying the session to the proper
* transaction. Note that the resulting {@link DataAccessStrategy} only handles MyBatis. It does not include the
* functionality of the {@link DefaultDataAccessStrategy} which one normally still wants. Use
* {@link #createCombinedAccessStrategy(RelationalMappingContext, JdbcConverter, NamedParameterJdbcOperations, SqlSession, NamespaceStrategy)}
* {@link #createCombinedAccessStrategy(RelationalMappingContext, JdbcConverter, NamedParameterJdbcOperations, SqlSession, NamespaceStrategy, Dialect)}
* to create such a {@link DataAccessStrategy}.
*
* @param sqlSession Must be non {@literal null}.
* @param identifierProcessing the {@link IdentifierProcessing} applied to {@link SqlIdentifier} instances in order to
* turn them into {@link String}
*/
public MyBatisDataAccessStrategy(SqlSession sqlSession, RelationalMappingContext context, JdbcConverter converter) {
public MyBatisDataAccessStrategy(SqlSession sqlSession, IdentifierProcessing identifierProcessing) {
this.sqlSession = sqlSession;
this.identifierProcessing = identifierProcessing;
}
/**
@@ -135,9 +145,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
public <T> Object insert(T instance, Class<T> domainType, Map<SqlIdentifier, Object> additionalParameters) {
MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, additionalParameters);
MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType,
convertToParameterMap(additionalParameters));
sqlSession().insert(namespace(domainType) + ".insert", myBatisContext);
return myBatisContext.getId();
@@ -150,7 +161,8 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, identifier.toMap());
MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType,
convertToParameterMap(identifier.toMap()));
sqlSession().insert(namespace(domainType) + ".insert", myBatisContext);
return myBatisContext.getId();
@@ -174,9 +186,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public <S> boolean updateWithVersion(S instance, Class<S> domainType, Number previousVersion) {
return sqlSession().update(namespace(domainType) + ".updateWithVersion",
new MyBatisContext(null, instance, domainType, Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion))) != 0;
String statement = namespace(domainType) + ".updateWithVersion";
MyBatisContext parameter = new MyBatisContext(null, instance, domainType,
Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion));
return sqlSession().update(statement, parameter) != 0;
}
/*
@@ -186,8 +199,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public void delete(Object id, Class<?> domainType) {
sqlSession().delete(namespace(domainType) + ".delete",
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
String statement = namespace(domainType) + ".delete";
MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
sqlSession().delete(statement, parameter);
}
/*
@@ -197,8 +211,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public <T> void deleteWithVersion(Object id, Class<T> domainType, Number previousVersion) {
sqlSession().delete(namespace(domainType) + ".deleteWithVersion",
new MyBatisContext(id, null, domainType, Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion)));
String statement = namespace(domainType) + ".deleteWithVersion";
MyBatisContext parameter = new MyBatisContext(id, null, domainType,
Collections.singletonMap(VERSION_SQL_PARAMETER_NAME_OLD, previousVersion));
sqlSession().delete(statement, parameter);
}
/*
@@ -208,10 +224,12 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public void delete(Object rootId, PersistentPropertyPath<RelationalPersistentProperty> propertyPath) {
sqlSession().delete(
namespace(propertyPath.getBaseProperty().getOwner().getType()) + ".delete-" + toDashPath(propertyPath),
new MyBatisContext(rootId, null, propertyPath.getRequiredLeafProperty().getTypeInformation().getType(),
Collections.emptyMap()));
Class<?> ownerType = propertyPath.getBaseProperty().getOwner().getType();
String statement = namespace(ownerType) + ".delete-" + toDashPath(propertyPath);
Class<?> leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType();
MyBatisContext parameter = new MyBatisContext(rootId, null, leafType, Collections.emptyMap());
sqlSession().delete(statement, parameter);
}
/*
@@ -221,10 +239,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public <T> void deleteAll(Class<T> domainType) {
sqlSession().delete( //
namespace(domainType) + ".deleteAll", //
new MyBatisContext(null, null, domainType, Collections.emptyMap()) //
);
String statement = namespace(domainType) + ".deleteAll";
MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap());
sqlSession().delete(statement, parameter);
}
/*
@@ -237,10 +254,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
Class<?> baseType = propertyPath.getBaseProperty().getOwner().getType();
Class<?> leafType = propertyPath.getRequiredLeafProperty().getTypeInformation().getType();
sqlSession().delete( //
namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath), //
new MyBatisContext(null, null, leafType, Collections.emptyMap()) //
);
String statement = namespace(baseType) + ".deleteAll-" + toDashPath(propertyPath);
MyBatisContext parameter = new MyBatisContext(null, null, leafType, Collections.emptyMap());
sqlSession().delete(statement, parameter);
}
/*
@@ -249,8 +265,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
*/
@Override
public <T> T findById(Object id, Class<T> domainType) {
return sqlSession().selectOne(namespace(domainType) + ".findById",
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
String statement = namespace(domainType) + ".findById";
MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
return sqlSession().selectOne(statement, parameter);
}
/*
@@ -259,8 +277,10 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
*/
@Override
public <T> Iterable<T> findAll(Class<T> domainType) {
return sqlSession().selectList(namespace(domainType) + ".findAll",
new MyBatisContext(null, null, domainType, Collections.emptyMap()));
String statement = namespace(domainType) + ".findAll";
MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap());
return sqlSession().selectList(statement, parameter);
}
/*
@@ -299,9 +319,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public <T> Iterable<T> findAllByProperty(Object rootId, RelationalPersistentProperty property) {
return sqlSession().selectList(
namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName(),
new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap()));
String statement = namespace(property.getOwner().getType()) + ".findAllByProperty-" + property.getName();
MyBatisContext parameter = new MyBatisContext(rootId, null, property.getType(), Collections.emptyMap());
return sqlSession().selectList(statement, parameter);
}
/*
@@ -311,8 +331,9 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
@Override
public <T> boolean existsById(Object id, Class<T> domainType) {
return sqlSession().selectOne(namespace(domainType) + ".existsById",
new MyBatisContext(id, null, domainType, Collections.emptyMap()));
String statement = namespace(domainType) + ".existsById";
MyBatisContext parameter = new MyBatisContext(id, null, domainType, Collections.emptyMap());
return sqlSession().selectOne(statement, parameter);
}
/*
@@ -321,8 +342,16 @@ public class MyBatisDataAccessStrategy implements DataAccessStrategy {
*/
@Override
public long count(Class<?> domainType) {
return sqlSession().selectOne(namespace(domainType) + ".count",
new MyBatisContext(null, null, domainType, Collections.emptyMap()));
String statement = namespace(domainType) + ".count";
MyBatisContext parameter = new MyBatisContext(null, null, domainType, Collections.emptyMap());
return sqlSession().selectOne(statement, parameter);
}
private Map<String, Object> convertToParameterMap(Map<SqlIdentifier, Object> additionalParameters) {
return additionalParameters.entrySet().stream() //
.collect(Collectors.toMap(e -> e.getKey().toSql(identifierProcessing), Map.Entry::getValue));
}
private String namespace(Class<?> domainType) {

View File

@@ -34,6 +34,8 @@ import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -71,8 +73,8 @@ public class AbstractJdbcConfiguration {
}
/**
* Creates a {@link RelationalConverter} using the configured {@link #jdbcMappingContext(Optional)}. Will get
* {@link #jdbcCustomConversions()} applied.
* Creates a {@link RelationalConverter} using the configured
* {@link #jdbcMappingContext(Optional, JdbcCustomConversions)}. Will get {@link #jdbcCustomConversions()} applied.
*
* @see #jdbcMappingContext(Optional, JdbcCustomConversions)
* @see #jdbcCustomConversions()
@@ -125,7 +127,12 @@ public class AbstractJdbcConfiguration {
*/
@Bean
public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter,
RelationalMappingContext context) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, jdbcConverter, operations);
RelationalMappingContext context, Dialect dialect) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, jdbcConverter, operations);
}
@Bean
Dialect dialect() {
return HsqlDbDialect.INSTANCE;
}
}

View File

@@ -34,6 +34,8 @@ import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -102,12 +104,15 @@ public class JdbcConfiguration {
* @param context
* @param converter
* @param operations
* @param dialect
* @return
*/
@Bean
public JdbcAggregateOperations jdbcAggregateOperations(ApplicationEventPublisher publisher,
RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations) {
return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy(context, converter, operations));
RelationalMappingContext context, JdbcConverter converter, NamedParameterJdbcOperations operations,
Dialect dialect) {
return new JdbcAggregateTemplate(publisher, context, converter,
dataAccessStrategy(context, converter, operations, dialect));
}
/**
@@ -117,11 +122,17 @@ public class JdbcConfiguration {
* @param context
* @param converter
* @param operations
* @param dialect
* @return
*/
@Bean
public DataAccessStrategy dataAccessStrategy(RelationalMappingContext context, JdbcConverter converter,
NamedParameterJdbcOperations operations) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, converter, operations);
NamedParameterJdbcOperations operations, Dialect dialect) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, converter, operations);
}
@Bean
public Dialect dialect() {
return HsqlDbDialect.INSTANCE;
}
}

View File

@@ -22,6 +22,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -43,8 +44,8 @@ public class MyBatisJdbcConfiguration extends AbstractJdbcConfiguration {
@Bean
@Override
public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, JdbcConverter jdbcConverter,
RelationalMappingContext context) {
RelationalMappingContext context, Dialect dialect) {
return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session);
return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, jdbcConverter, operations, session, dialect);
}
}

View File

@@ -28,6 +28,7 @@ import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
import org.springframework.data.jdbc.repository.RowMapperMap;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
@@ -56,6 +57,7 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
private NamedParameterJdbcOperations operations;
private EntityCallbacks entityCallbacks;
private Dialect dialect;
/**
* Creates a new {@link JdbcRepositoryFactoryBean} for the given repository interface.
@@ -99,6 +101,11 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
this.mappingContext = mappingContext;
}
@Autowired
protected void setDialect(Dialect dialect) {
this.dialect = dialect;
}
/**
* @param dataAccessStrategy can be {@literal null}.
*/
@@ -167,7 +174,9 @@ public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extend
this.dataAccessStrategy = this.beanFactory.getBeanProvider(DataAccessStrategy.class) //
.getIfAvailable(() -> {
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext);
Assert.state(this.dialect != null, "Dialect is required and must not be null!");
SqlGeneratorSource sqlGeneratorSource = new SqlGeneratorSource(this.mappingContext, this.dialect);
return new DefaultDataAccessStrategy(sqlGeneratorSource, this.mappingContext, this.converter,
this.operations);
});

View File

@@ -15,14 +15,11 @@
*/
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.data.jdbc.core.PropertyPathTestingUtils.toPath;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.jdbc.core.PropertyPathTestingUtils.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import java.util.List;
@@ -52,9 +49,8 @@ import org.springframework.data.relational.domain.Identifier;
*/
public class DefaultJdbcInterpreterUnitTests {
public static final SimpleSqlIdentifier BACK_REFERENCE = quoted("container");
static final long CONTAINER_ID = 23L;
static final String BACK_REFERENCE = "container";
RelationalMappingContext context = new JdbcMappingContext();
JdbcConverter converter = new BasicJdbcConverter(context, (Identifier, path) -> null);
DataAccessStrategy dataAccessStrategy = mock(DataAccessStrategy.class);
@@ -156,9 +152,10 @@ public class DefaultJdbcInterpreterUnitTests {
assertThat(argumentCaptor.getValue().getParts()) //
.extracting("name", "value", "targetType") //
.containsOnly(tuple("root_with_list", CONTAINER_ID, Long.class), // the top level id
tuple("root_with_list_key", 3, Integer.class), // midlevel key
tuple("with_list_key", 6, Integer.class) // lowlevel key
.containsOnly(tuple(quoted("root_with_list"), CONTAINER_ID, Long.class), // the top
// level id
tuple(quoted("root_with_list_key"), 3, Integer.class), // midlevel key
tuple(quoted("with_list_key"), 6, Integer.class) // lowlevel key
);
}

View File

@@ -89,6 +89,109 @@ public class JdbcAggregateTemplateIntegrationTests {
@Autowired NamedParameterJdbcOperations jdbcTemplate;
LegoSet legoSet = createLegoSet();
/**
* creates an instance of {@link NoIdListChain4} with the following properties:
* <ul>
* <li>Each element has two children with indices 0 and 1.</li>
* <li>the xxxValue of each element is a {@literal v} followed by the indices used to navigate to the given instance.
* </li>
* </ul>
*/
private static NoIdListChain4 createNoIdTree() {
NoIdListChain4 chain4 = new NoIdListChain4();
chain4.fourValue = "v";
IntStream.of(0, 1).forEach(i -> {
NoIdListChain3 c3 = new NoIdListChain3();
c3.threeValue = chain4.fourValue + i;
chain4.chain3.add(c3);
IntStream.of(0, 1).forEach(j -> {
NoIdListChain2 c2 = new NoIdListChain2();
c2.twoValue = c3.threeValue + j;
c3.chain2.add(c2);
IntStream.of(0, 1).forEach(k -> {
NoIdListChain1 c1 = new NoIdListChain1();
c1.oneValue = c2.twoValue + k;
c2.chain1.add(c1);
IntStream.of(0, 1).forEach(m -> {
NoIdListChain0 c0 = new NoIdListChain0();
c0.zeroValue = c1.oneValue + m;
c1.chain0.add(c0);
});
});
});
});
return chain4;
}
private static NoIdMapChain4 createNoIdMapTree() {
NoIdMapChain4 chain4 = new NoIdMapChain4();
chain4.fourValue = "v";
IntStream.of(0, 1).forEach(i -> {
NoIdMapChain3 c3 = new NoIdMapChain3();
c3.threeValue = chain4.fourValue + i;
chain4.chain3.put(asString(i), c3);
IntStream.of(0, 1).forEach(j -> {
NoIdMapChain2 c2 = new NoIdMapChain2();
c2.twoValue = c3.threeValue + j;
c3.chain2.put(asString(j), c2);
IntStream.of(0, 1).forEach(k -> {
NoIdMapChain1 c1 = new NoIdMapChain1();
c1.oneValue = c2.twoValue + k;
c2.chain1.put(asString(k), c1);
IntStream.of(0, 1).forEach(it -> {
NoIdMapChain0 c0 = new NoIdMapChain0();
c0.zeroValue = c1.oneValue + it;
c1.chain0.put(asString(it), c0);
});
});
});
});
return chain4;
}
private static String asString(int i) {
return "_" + i;
}
private static void assumeNot(String dbProfileName) {
Assume.assumeTrue("true"
.equalsIgnoreCase(ProfileValueUtils.retrieveProfileValueSource(JdbcAggregateTemplateIntegrationTests.class)
.get("current.database.is.not." + dbProfileName)));
}
private static LegoSet createLegoSet() {
LegoSet entity = new LegoSet();
entity.setName("Star Destroyer");
Manual manual = new Manual();
manual.setContent("Accelerates to 99% of light speed. Destroys almost everything. See https://what-if.xkcd.com/1/");
entity.setManual(manual);
return entity;
}
@Test // DATAJDBC-112
public void saveAndLoadAnEntityWithReferencedEntityById() {
@@ -550,50 +653,6 @@ public class JdbcAggregateTemplateIntegrationTests {
});
}
/**
* creates an instance of {@link NoIdListChain4} with the following properties:
* <ul>
* <li>Each element has two children with indices 0 and 1.</li>
* <li>the xxxValue of each element is a {@literal v} followed by the indices used to navigate to the given instance.
* </li>
* </ul>
*/
private static NoIdListChain4 createNoIdTree() {
NoIdListChain4 chain4 = new NoIdListChain4();
chain4.fourValue = "v";
IntStream.of(0, 1).forEach(i -> {
NoIdListChain3 c3 = new NoIdListChain3();
c3.threeValue = chain4.fourValue + i;
chain4.chain3.add(c3);
IntStream.of(0, 1).forEach(j -> {
NoIdListChain2 c2 = new NoIdListChain2();
c2.twoValue = c3.threeValue + j;
c3.chain2.add(c2);
IntStream.of(0, 1).forEach(k -> {
NoIdListChain1 c1 = new NoIdListChain1();
c1.oneValue = c2.twoValue + k;
c2.chain1.add(c1);
IntStream.of(0, 1).forEach(m -> {
NoIdListChain0 c0 = new NoIdListChain0();
c0.zeroValue = c1.oneValue + m;
c1.chain0.add(c0);
});
});
});
});
return chain4;
}
@Test // DATAJDBC-223
public void saveAndLoadLongChainOfMapsWithoutIds() {
@@ -758,57 +817,10 @@ public class JdbcAggregateTemplateIntegrationTests {
.withFailMessage("saving an aggregate with a future version should raise an exception");
}
private static NoIdMapChain4 createNoIdMapTree() {
NoIdMapChain4 chain4 = new NoIdMapChain4();
chain4.fourValue = "v";
IntStream.of(0, 1).forEach(i -> {
NoIdMapChain3 c3 = new NoIdMapChain3();
c3.threeValue = chain4.fourValue + i;
chain4.chain3.put(asString(i), c3);
IntStream.of(0, 1).forEach(j -> {
NoIdMapChain2 c2 = new NoIdMapChain2();
c2.twoValue = c3.threeValue + j;
c3.chain2.put(asString(j), c2);
IntStream.of(0, 1).forEach(k -> {
NoIdMapChain1 c1 = new NoIdMapChain1();
c1.oneValue = c2.twoValue + k;
c2.chain1.put(asString(k), c1);
IntStream.of(0, 1).forEach(it -> {
NoIdMapChain0 c0 = new NoIdMapChain0();
c0.zeroValue = c1.oneValue + it;
c1.chain0.put(asString(it), c0);
});
});
});
});
return chain4;
}
private static String asString(int i) {
return "_" + i;
}
private Long count(String tableName) {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM " + tableName, emptyMap(), Long.class);
}
private static void assumeNot(String dbProfileName) {
Assume.assumeTrue("true"
.equalsIgnoreCase(ProfileValueUtils.retrieveProfileValueSource(JdbcAggregateTemplateIntegrationTests.class)
.get("current.database.is.not." + dbProfileName)));
}
private static class ArrayOwner {
@Id Long id;
@@ -836,18 +848,6 @@ public class JdbcAggregateTemplateIntegrationTests {
Set<String> digits = new HashSet<>();
}
private static LegoSet createLegoSet() {
LegoSet entity = new LegoSet();
entity.setName("Star Destroyer");
Manual manual = new Manual();
manual.setContent("Accelerates to 99% of light speed. Destroys almost everything. See https://what-if.xkcd.com/1/");
entity.setManual(manual);
return entity;
}
@Data
static class LegoSet {
@@ -1060,13 +1060,13 @@ public class JdbcAggregateTemplateIntegrationTests {
@Version private long version;
@Override
void setVersion(Number newVersion) {
this.version = (long) newVersion;
Number getVersion() {
return this.version;
}
@Override
Number getVersion() {
return this.version;
void setVersion(Number newVersion) {
this.version = (long) newVersion;
}
}
@@ -1088,13 +1088,13 @@ public class JdbcAggregateTemplateIntegrationTests {
@Version private int version;
@Override
void setVersion(Number newVersion) {
this.version = (int) newVersion;
Number getVersion() {
return this.version;
}
@Override
Number getVersion() {
return this.version;
void setVersion(Number newVersion) {
this.version = (int) newVersion;
}
}
@@ -1116,13 +1116,13 @@ public class JdbcAggregateTemplateIntegrationTests {
@Version private short version;
@Override
void setVersion(Number newVersion) {
this.version = (short) newVersion;
Number getVersion() {
return this.version;
}
@Override
Number getVersion() {
return this.version;
void setVersion(Number newVersion) {
this.version = (short) newVersion;
}
}

View File

@@ -15,9 +15,11 @@
*/
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.SoftAssertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
@@ -28,8 +30,6 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathEx
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import static org.assertj.core.api.SoftAssertions.*;
/**
* @author Jens Schauder
*/
@@ -99,13 +99,13 @@ public class PersistentPropertyPathExtensionUnitTests {
assertSoftly(softly -> {
softly.assertThat(extPath(entity).getTableName()).isEqualTo("dummy_entity");
softly.assertThat(extPath("second").getTableName()).isEqualTo("second");
softly.assertThat(extPath("second.third2").getTableName()).isEqualTo("second");
softly.assertThat(extPath("second.third2.value").getTableName()).isEqualTo("second");
softly.assertThat(extPath("secondList.third2").getTableName()).isEqualTo("second");
softly.assertThat(extPath("secondList.third2.value").getTableName()).isEqualTo("second");
softly.assertThat(extPath("secondList").getTableName()).isEqualTo("second");
softly.assertThat(extPath(entity).getTableName()).isEqualTo(quoted("dummy_entity"));
softly.assertThat(extPath("second").getTableName()).isEqualTo(quoted("second"));
softly.assertThat(extPath("second.third2").getTableName()).isEqualTo(quoted("second"));
softly.assertThat(extPath("second.third2.value").getTableName()).isEqualTo(quoted("second"));
softly.assertThat(extPath("secondList.third2").getTableName()).isEqualTo(quoted("second"));
softly.assertThat(extPath("secondList.third2.value").getTableName()).isEqualTo(quoted("second"));
softly.assertThat(extPath("secondList").getTableName()).isEqualTo(quoted("second"));
});
}
@@ -115,17 +115,17 @@ public class PersistentPropertyPathExtensionUnitTests {
assertSoftly(softly -> {
softly.assertThat(extPath(entity).getTableAlias()).isEqualTo(null);
softly.assertThat(extPath("second").getTableAlias()).isEqualTo("second");
softly.assertThat(extPath("second.third2").getTableAlias()).isEqualTo("second");
softly.assertThat(extPath("second.third2.value").getTableAlias()).isEqualTo("second");
softly.assertThat(extPath("second.third").getTableAlias()).isEqualTo("second_third");
softly.assertThat(extPath("second.third.value").getTableAlias()).isEqualTo("second_third");
softly.assertThat(extPath("secondList.third2").getTableAlias()).isEqualTo("secondList");
softly.assertThat(extPath("secondList.third2.value").getTableAlias()).isEqualTo("secondList");
softly.assertThat(extPath("secondList.third").getTableAlias()).isEqualTo("secondList_third");
softly.assertThat(extPath("secondList.third.value").getTableAlias()).isEqualTo("secondList_third");
softly.assertThat(extPath("secondList").getTableAlias()).isEqualTo("secondList");
softly.assertThat(extPath("second2.third").getTableAlias()).isEqualTo("secthird");
softly.assertThat(extPath("second").getTableAlias()).isEqualTo(quoted("second"));
softly.assertThat(extPath("second.third2").getTableAlias()).isEqualTo(quoted("second"));
softly.assertThat(extPath("second.third2.value").getTableAlias()).isEqualTo(quoted("second"));
softly.assertThat(extPath("second.third").getTableAlias()).isEqualTo(quoted("second_third"));
softly.assertThat(extPath("second.third.value").getTableAlias()).isEqualTo(quoted("second_third"));
softly.assertThat(extPath("secondList.third2").getTableAlias()).isEqualTo(quoted("secondList"));
softly.assertThat(extPath("secondList.third2.value").getTableAlias()).isEqualTo(quoted("secondList"));
softly.assertThat(extPath("secondList.third").getTableAlias()).isEqualTo(quoted("secondList_third"));
softly.assertThat(extPath("secondList.third.value").getTableAlias()).isEqualTo(quoted("secondList_third"));
softly.assertThat(extPath("secondList").getTableAlias()).isEqualTo(quoted("secondList"));
softly.assertThat(extPath("second2.third").getTableAlias()).isEqualTo(quoted("secthird"));
});
}
@@ -134,12 +134,12 @@ public class PersistentPropertyPathExtensionUnitTests {
assertSoftly(softly -> {
softly.assertThat(extPath("second.third2.value").getColumnName()).isEqualTo("thrdvalue");
softly.assertThat(extPath("second.third.value").getColumnName()).isEqualTo("value");
softly.assertThat(extPath("secondList.third2.value").getColumnName()).isEqualTo("thrdvalue");
softly.assertThat(extPath("secondList.third.value").getColumnName()).isEqualTo("value");
softly.assertThat(extPath("second2.third2.value").getColumnName()).isEqualTo("secthrdvalue");
softly.assertThat(extPath("second2.third.value").getColumnName()).isEqualTo("value");
softly.assertThat(extPath("second.third2.value").getColumnName()).isEqualTo(quoted("thrdvalue"));
softly.assertThat(extPath("second.third.value").getColumnName()).isEqualTo(quoted("value"));
softly.assertThat(extPath("secondList.third2.value").getColumnName()).isEqualTo(quoted("thrdvalue"));
softly.assertThat(extPath("secondList.third.value").getColumnName()).isEqualTo(quoted("value"));
softly.assertThat(extPath("second2.third2.value").getColumnName()).isEqualTo(quoted("secthrdvalue"));
softly.assertThat(extPath("second2.third.value").getColumnName()).isEqualTo(quoted("value"));
});
}
@@ -164,15 +164,15 @@ public class PersistentPropertyPathExtensionUnitTests {
assertSoftly(softly -> {
softly.assertThat(extPath("second.third2").getReverseColumnName()).isEqualTo("dummy_entity");
softly.assertThat(extPath("second.third").getReverseColumnName()).isEqualTo("dummy_entity");
softly.assertThat(extPath("secondList.third2").getReverseColumnName()).isEqualTo("dummy_entity");
softly.assertThat(extPath("secondList.third").getReverseColumnName()).isEqualTo("dummy_entity");
softly.assertThat(extPath("second2.third2").getReverseColumnName()).isEqualTo("dummy_entity");
softly.assertThat(extPath("second2.third").getReverseColumnName()).isEqualTo("dummy_entity");
softly.assertThat(extPath("withId.second.third2.value").getReverseColumnName()).isEqualTo("with_id");
softly.assertThat(extPath("withId.second.third").getReverseColumnName()).isEqualTo("with_id");
softly.assertThat(extPath("withId.second2.third").getReverseColumnName()).isEqualTo("with_id");
softly.assertThat(extPath("second.third2").getReverseColumnName()).isEqualTo(quoted("dummy_entity"));
softly.assertThat(extPath("second.third").getReverseColumnName()).isEqualTo(quoted("dummy_entity"));
softly.assertThat(extPath("secondList.third2").getReverseColumnName()).isEqualTo(quoted("dummy_entity"));
softly.assertThat(extPath("secondList.third").getReverseColumnName()).isEqualTo(quoted("dummy_entity"));
softly.assertThat(extPath("second2.third2").getReverseColumnName()).isEqualTo(quoted("dummy_entity"));
softly.assertThat(extPath("second2.third").getReverseColumnName()).isEqualTo(quoted("dummy_entity"));
softly.assertThat(extPath("withId.second.third2.value").getReverseColumnName()).isEqualTo(quoted("with_id"));
softly.assertThat(extPath("withId.second.third").getReverseColumnName()).isEqualTo(quoted("with_id"));
softly.assertThat(extPath("withId.second2.third").getReverseColumnName()).isEqualTo(quoted("with_id"));
});
}
@@ -200,12 +200,10 @@ public class PersistentPropertyPathExtensionUnitTests {
});
}
@NotNull
private PersistentPropertyPathExtension extPath(RelationalPersistentEntity<?> entity) {
return new PersistentPropertyPathExtension(context, entity);
}
@NotNull
private PersistentPropertyPathExtension extPath(String path) {
return new PersistentPropertyPathExtension(context, createSimplePath(path));
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.jdbc.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
@@ -33,7 +34,10 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
@@ -54,7 +58,7 @@ public class DefaultDataAccessStrategyUnitTests {
JdbcOperations jdbcOperations = mock(JdbcOperations.class);
RelationalMappingContext context = new JdbcMappingContext();
HashMap<String, Object> additionalParameters = new HashMap<>();
HashMap<SqlIdentifier, Object> additionalParameters = new HashMap<>();
ArgumentCaptor<SqlParameterSource> paramSourceCaptor = ArgumentCaptor.forClass(SqlParameterSource.class);
JdbcConverter converter;
@@ -62,12 +66,14 @@ public class DefaultDataAccessStrategyUnitTests {
@Before
public void before() {
DelegatingDataAccessStrategy relationResolver = new DelegatingDataAccessStrategy();
Dialect dialect = HsqlDbDialect.INSTANCE;
converter = new BasicJdbcConverter(context, relationResolver, new JdbcCustomConversions(),
new DefaultJdbcTypeFactory(jdbcOperations));
accessStrategy = new DefaultDataAccessStrategy( //
new SqlGeneratorSource(context), //
new SqlGeneratorSource(context, dialect), //
context, //
converter, //
namedJdbcOperations);
@@ -78,12 +84,12 @@ public class DefaultDataAccessStrategyUnitTests {
@Test // DATAJDBC-146
public void additionalParameterForIdDoesNotLeadToDuplicateParameters() {
additionalParameters.put("id", ID_FROM_ADDITIONAL_VALUES);
additionalParameters.put(SqlIdentifier.quoted("ID"), ID_FROM_ADDITIONAL_VALUES);
accessStrategy.insert(new DummyEntity(ORIGINAL_ID), DummyEntity.class, additionalParameters);
verify(namedJdbcOperations).update(eq("INSERT INTO dummy_entity (id) VALUES (:id)"), paramSourceCaptor.capture(),
any(KeyHolder.class));
verify(namedJdbcOperations).update(eq("INSERT INTO \"DUMMY_ENTITY\" (\"ID\") VALUES (:ID)"),
paramSourceCaptor.capture(), any(KeyHolder.class));
}
@Test // DATAJDBC-146
@@ -91,16 +97,16 @@ public class DefaultDataAccessStrategyUnitTests {
ArgumentCaptor<String> sqlCaptor = ArgumentCaptor.forClass(String.class);
additionalParameters.put("reference", ID_FROM_ADDITIONAL_VALUES);
additionalParameters.put(unquoted("reference"), ID_FROM_ADDITIONAL_VALUES);
accessStrategy.insert(new DummyEntity(ORIGINAL_ID), DummyEntity.class, additionalParameters);
verify(namedJdbcOperations).update(sqlCaptor.capture(), paramSourceCaptor.capture(), any(KeyHolder.class));
assertThat(sqlCaptor.getValue()) //
.containsSequence("INSERT INTO dummy_entity (", "id", ") VALUES (", ":id", ")") //
.containsSequence("INSERT INTO dummy_entity (", "reference", ") VALUES (", ":reference", ")");
assertThat(paramSourceCaptor.getValue().getValue("id")).isEqualTo(ORIGINAL_ID);
.containsSequence("INSERT INTO \"DUMMY_ENTITY\" (", "\"ID\"", ") VALUES (", ":ID", ")") //
.containsSequence("INSERT INTO \"DUMMY_ENTITY\" (", "reference", ") VALUES (", ":reference", ")");
assertThat(paramSourceCaptor.getValue().getValue("ID")).isEqualTo(ORIGINAL_ID);
}
@Test // DATAJDBC-235
@@ -113,7 +119,7 @@ public class DefaultDataAccessStrategyUnitTests {
new DefaultJdbcTypeFactory(jdbcOperations));
DefaultDataAccessStrategy accessStrategy = new DefaultDataAccessStrategy( //
new SqlGeneratorSource(context), //
new SqlGeneratorSource(context, HsqlDbDialect.INSTANCE), //
context, //
converter, //
namedJdbcOperations);
@@ -128,8 +134,8 @@ public class DefaultDataAccessStrategyUnitTests {
verify(namedJdbcOperations).update(sqlCaptor.capture(), paramSourceCaptor.capture(), any(KeyHolder.class));
assertThat(paramSourceCaptor.getValue().getValue("id")).isEqualTo(ORIGINAL_ID);
assertThat(paramSourceCaptor.getValue().getValue("flag")).isEqualTo("T");
assertThat(paramSourceCaptor.getValue().getValue("ID")).isEqualTo(ORIGINAL_ID);
assertThat(paramSourceCaptor.getValue().getValue("FLAG")).isEqualTo("T");
}
@RequiredArgsConstructor

View File

@@ -60,6 +60,7 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier;
import org.springframework.data.repository.query.Param;
import org.springframework.util.Assert;
@@ -80,15 +81,15 @@ public class EntityRowMapperUnitTests {
public static final long ID_FOR_ENTITY_NOT_REFERENCING_MAP = 23L;
public static final NamingStrategy X_APPENDING_NAMINGSTRATEGY = new NamingStrategy() {
@Override
public String getColumnName(RelationalPersistentProperty property) {
return NamingStrategy.super.getColumnName(property) + "x";
public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) {
return NamingStrategy.super.getColumnName(property).suffix("x");
}
};
@Test // DATAJDBC-113
public void simpleEntitiesGetProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
rs.next();
@@ -103,7 +104,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-181
public void namingStrategyGetsHonored() throws SQLException {
ResultSet rs = mockResultSet(asList("idx", "namex"), //
ResultSet rs = mockResultSet(asList("IDX", "NAMEX"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
rs.next();
@@ -118,7 +119,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-181
public void namingStrategyGetsHonoredForConstructor() throws SQLException {
ResultSet rs = mockResultSet(asList("idx", "namex"), //
ResultSet rs = mockResultSet(asList("IDX", "NAMEX"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
rs.next();
@@ -133,7 +134,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-427
public void simpleWithReferenceGetProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name", "trivial_id"), //
ResultSet rs = mockResultSet(asList("ID", "NAME", "TRIVIAL_ID"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 100L);
rs.next();
@@ -148,7 +149,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-113
public void simpleOneToOneGetsProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name", "child_id", "child_name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME", "CHILD_ID", "CHILD_NAME"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L, "beta");
rs.next();
@@ -163,7 +164,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-286
public void immutableOneToOneGetsProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name", "child_id", "child_name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME", "CHILD_ID", "CHILD_NAME"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L, "beta");
rs.next();
@@ -178,7 +179,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-427
public void immutableWithReferenceGetsProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name", "trivial_id"), //
ResultSet rs = mockResultSet(asList("ID", "NAME", "TRIVIAL_ID"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 100L);
rs.next();
@@ -194,7 +195,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-111
public void simpleEmbeddedGetsProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name", "prefix_id", "prefix_name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME", "PREFIX_ID", "PREFIX_NAME"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24L, "beta");
rs.next();
@@ -209,7 +210,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-113
public void collectionReferenceGetsLoadedWithAdditionalSelect() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha");
rs.next();
@@ -224,7 +225,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-131
public void mapReferenceGetsLoadedWithAdditionalSelect() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME"), //
ID_FOR_ENTITY_REFERENCING_MAP, "alpha");
rs.next();
@@ -239,7 +240,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-130
public void listReferenceGetsLoadedWithAdditionalSelect() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME"), //
ID_FOR_ENTITY_REFERENCING_LIST, "alpha");
rs.next();
@@ -254,7 +255,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-252
public void doesNotTryToSetPropertiesThatAreSetViaConstructor() throws SQLException {
ResultSet rs = mockResultSet(singletonList("value"), //
ResultSet rs = mockResultSet(singletonList("VALUE"), //
"value-from-resultSet");
rs.next();
@@ -267,7 +268,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-252
public void handlesMixedProperties() throws SQLException {
ResultSet rs = mockResultSet(asList("one", "two", "three"), //
ResultSet rs = mockResultSet(asList("ONE", "TWO", "THREE"), //
"111", "222", "333");
rs.next();
@@ -281,7 +282,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-273
public void handlesNonSimplePropertyInConstructor() throws SQLException {
ResultSet rs = mockResultSet(singletonList("id"), //
ResultSet rs = mockResultSet(singletonList("ID"), //
ID_FOR_ENTITY_REFERENCING_LIST);
rs.next();
@@ -297,22 +298,22 @@ public class EntityRowMapperUnitTests {
Fixture<NoIdChain4> fixture = this.<NoIdChain4> buildFixture() //
// Id of the aggregate root and backreference to it from
// the various aggregate members.
.value(4L).inColumns("four", //
"chain3_no_id_chain4", //
"chain3_chain2_no_id_chain4", //
"chain3_chain2_chain1_no_id_chain4", //
"chain3_chain2_chain1_chain0_no_id_chain4") //
.value(4L).inColumns("FOUR", //
"CHAIN3_NO_ID_CHAIN4", //
"CHAIN3_CHAIN2_NO_ID_CHAIN4", //
"CHAIN3_CHAIN2_CHAIN1_NO_ID_CHAIN4", //
"CHAIN3_CHAIN2_CHAIN1_CHAIN0_NO_ID_CHAIN4") //
.endUpIn(e -> e.four)
// values for the different entities
.value("four_value").inColumns("four_value").endUpIn(e -> e.fourValue) //
.value("four_value").inColumns("FOUR_VALUE").endUpIn(e -> e.fourValue) //
.value("three_value").inColumns("chain3_three_value").endUpIn(e -> e.chain3.threeValue) //
.value("three_value").inColumns("CHAIN3_THREE_VALUE").endUpIn(e -> e.chain3.threeValue) //
.value("two_value").inColumns("chain3_chain2_two_value").endUpIn(e -> e.chain3.chain2.twoValue) //
.value("two_value").inColumns("CHAIN3_CHAIN2_TWO_VALUE").endUpIn(e -> e.chain3.chain2.twoValue) //
.value("one_value").inColumns("chain3_chain2_chain1_one_value").endUpIn(e -> e.chain3.chain2.chain1.oneValue) //
.value("one_value").inColumns("CHAIN3_CHAIN2_CHAIN1_ONE_VALUE").endUpIn(e -> e.chain3.chain2.chain1.oneValue) //
.value("zero_value").inColumns("chain3_chain2_chain1_chain0_zero_value")
.value("zero_value").inColumns("CHAIN3_CHAIN2_CHAIN1_CHAIN0_ZERO_VALUE")
.endUpIn(e -> e.chain3.chain2.chain1.chain0.zeroValue) //
.build();
// @formatter:on
@@ -329,7 +330,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-370
public void simpleNullableImmutableEmbeddedGetsProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "value"), //
ResultSet rs = mockResultSet(asList("ID", "VALUE"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "ru'Ha'");
rs.next();
@@ -345,7 +346,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-374
public void simpleEmptyImmutableEmbeddedGetsProperlyExtracted() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "value"), //
ResultSet rs = mockResultSet(asList("ID", "VALUE"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, null);
rs.next();
@@ -361,7 +362,7 @@ public class EntityRowMapperUnitTests {
@SneakyThrows
public void simplePrimitiveImmutableEmbeddedGetsProperlyExtracted() {
ResultSet rs = mockResultSet(asList("id", "value"), //
ResultSet rs = mockResultSet(asList("ID", "VALUE"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, 24);
rs.next();
@@ -377,7 +378,7 @@ public class EntityRowMapperUnitTests {
@Test // DATAJDBC-370
public void simpleImmutableEmbeddedShouldBeNullIfAllOfTheEmbeddableAreNull() throws SQLException {
ResultSet rs = mockResultSet(asList("id", "value"), //
ResultSet rs = mockResultSet(asList("ID", "VALUE"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, null);
rs.next();
@@ -394,7 +395,7 @@ public class EntityRowMapperUnitTests {
@SneakyThrows
public void embeddedShouldBeNullWhenFieldsAreNull() {
ResultSet rs = mockResultSet(asList("id", "name", "prefix_id", "prefix_name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME", "PREFIX_ID", "PREFIX_NAME"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", null, null);
rs.next();
@@ -410,7 +411,7 @@ public class EntityRowMapperUnitTests {
@SneakyThrows
public void embeddedShouldNotBeNullWhenAtLeastOneFieldIsNotNull() {
ResultSet rs = mockResultSet(asList("id", "name", "prefix_id", "prefix_name"), //
ResultSet rs = mockResultSet(asList("ID", "NAME", "PREFIX_ID", "PREFIX_NAME"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "alpha", 24, null);
rs.next();
@@ -426,7 +427,7 @@ public class EntityRowMapperUnitTests {
@SneakyThrows
public void primitiveEmbeddedShouldBeNullWhenNoValuePresent() {
ResultSet rs = mockResultSet(asList("id", "value"), //
ResultSet rs = mockResultSet(asList("ID", "VALUE"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, null);
rs.next();
@@ -443,7 +444,7 @@ public class EntityRowMapperUnitTests {
@SneakyThrows
public void deepNestedEmbeddable() {
ResultSet rs = mockResultSet(asList("id", "level0", "level1_value", "level1_level2_value"), //
ResultSet rs = mockResultSet(asList("ID", "LEVEL0", "LEVEL1_VALUE", "LEVEL1_LEVEL2_VALUE"), //
ID_FOR_ENTITY_NOT_REFERENCING_MAP, "0", "1", "2");
rs.next();

View File

@@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.jdbc.core.PropertyPathTestingUtils.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import java.util.List;
import java.util.Map;
@@ -46,7 +47,7 @@ public class JdbcIdentifierBuilderUnitTests {
assertThat(identifier.getParts()) //
.extracting("name", "value", "targetType") //
.containsExactly( //
tuple("dummy_entity", "eins", UUID.class) //
tuple(quoted("dummy_entity"), "eins", UUID.class) //
);
}
@@ -63,8 +64,8 @@ public class JdbcIdentifierBuilderUnitTests {
assertThat(identifier.getParts()) //
.extracting("name", "value", "targetType") //
.containsExactlyInAnyOrder( //
tuple("dummy_entity", "parent-eins", UUID.class), //
tuple("dummy_entity_key", "map-key-eins", String.class) //
tuple(quoted("dummy_entity"), "parent-eins", UUID.class), //
tuple(quoted("dummy_entity_key"), "map-key-eins", String.class) //
);
}
@@ -81,8 +82,8 @@ public class JdbcIdentifierBuilderUnitTests {
assertThat(identifier.getParts()) //
.extracting("name", "value", "targetType") //
.containsExactlyInAnyOrder( //
tuple("dummy_entity", "parent-eins", UUID.class), //
tuple("dummy_entity_key", "list-index-eins", Integer.class) //
tuple(quoted("dummy_entity"), "parent-eins", UUID.class), //
tuple(quoted("dummy_entity_key"), "list-index-eins", Integer.class) //
);
}
@@ -96,7 +97,7 @@ public class JdbcIdentifierBuilderUnitTests {
assertThat(identifier.getParts()) //
.extracting("name", "value", "targetType") //
.containsExactly( //
tuple("dummy_entity", "parent-eins", UUID.class) //
tuple(quoted("dummy_entity"), "parent-eins", UUID.class) //
);
}
@@ -110,7 +111,7 @@ public class JdbcIdentifierBuilderUnitTests {
assertThat(identifier.getParts()) //
.extracting("name", "value", "targetType") //
.containsExactly( //
tuple("dummy_entity", "parent-eins", UUID.class) //
tuple(quoted("dummy_entity"), "parent-eins", UUID.class) //
);
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.jdbc.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -25,7 +26,6 @@ import java.util.function.Consumer;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.convert.SqlGenerator;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils;
import org.springframework.data.mapping.PersistentPropertyPath;
@@ -33,6 +33,10 @@ import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.domain.IdentifierProcessing.Quoting;
import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Unit tests to verify a contextual {@link NamingStrategy} implementation that customizes using a user-centric
@@ -52,8 +56,8 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests {
private final NamingStrategy contextualNamingStrategy = new NamingStrategy() {
@Override
public String getSchema() {
return userHandler.get();
public SqlIdentifier getSchema() {
return unquoted(userHandler.get());
}
};
@@ -87,8 +91,11 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests {
String sql = sqlGenerator.createDeleteByPath(getPath("ref"));
assertThat(sql).isEqualTo(
"DELETE FROM " + user + ".referenced_entity WHERE " + user + ".referenced_entity.dummy_entity = :rootId");
assertThat(sql).isEqualTo( //
"DELETE FROM " //
+ user + ".referenced_entity WHERE " //
+ user + ".referenced_entity.dummy_entity = :rootId" //
);
});
}
@@ -213,7 +220,8 @@ public class SqlGeneratorContextBasedNamingStrategyUnitTests {
RelationalMappingContext context = new JdbcMappingContext(namingStrategy);
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
return new SqlGenerator(context, persistentEntity);
return new SqlGenerator(context, persistentEntity,
new DefaultIdentifierProcessing(new Quoting(""), LetterCasing.AS_IS));
}
@SuppressWarnings("unused")

View File

@@ -32,6 +32,9 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathEx
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.sql.Aliased;
import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.domain.IdentifierProcessing.Quoting;
/**
* Unit tests for the {@link SqlGenerator} in a context of the {@link Embedded} annotation.
@@ -50,81 +53,121 @@ public class SqlGeneratorEmbeddedUnitTests {
SqlGenerator createSqlGenerator(Class<?> type) {
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(type);
return new SqlGenerator(context, persistentEntity);
return new SqlGenerator(context, persistentEntity,
new DefaultIdentifierProcessing(new Quoting(""), LetterCasing.AS_IS));
}
@Test // DATAJDBC-111
public void findOne() {
final String sql = sqlGenerator.getFindOne();
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql).startsWith("SELECT").contains("dummy_entity.id1 AS id1")
.contains("dummy_entity.test AS test").contains("dummy_entity.attr1 AS attr1")
.contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1")
.contains("dummy_entity.prefix2_attr2 AS prefix2_attr2").contains("dummy_entity.prefix_test AS prefix_test")
.contains("dummy_entity.prefix_attr1 AS prefix_attr1").contains("dummy_entity.prefix_attr2 AS prefix_attr2")
.contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1")
.contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2").contains("WHERE dummy_entity.id1 = :id")
.doesNotContain("JOIN").doesNotContain("embeddable");
softAssertions.assertAll();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(sql).startsWith("SELECT") //
.contains("dummy_entity.id1 AS id1") //
.contains("dummy_entity.test AS test") //
.contains("dummy_entity.attr1 AS attr1") //
.contains("dummy_entity.attr2 AS attr2") //
.contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") //
.contains("dummy_entity.prefix2_attr2 AS prefix2_attr2") //
.contains("dummy_entity.prefix_test AS prefix_test") //
.contains("dummy_entity.prefix_attr1 AS prefix_attr1") //
.contains("dummy_entity.prefix_attr2 AS prefix_attr2") //
.contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") //
.contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2") //
.contains("WHERE dummy_entity.id1 = :id") //
.doesNotContain("JOIN").doesNotContain("embeddable"); //
});
}
@Test // DATAJDBC-111
public void findAll() {
final String sql = sqlGenerator.getFindAll();
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql).startsWith("SELECT").contains("dummy_entity.id1 AS id1")
.contains("dummy_entity.test AS test").contains("dummy_entity.attr1 AS attr1")
.contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1")
.contains("dummy_entity.prefix2_attr2 AS prefix2_attr2").contains("dummy_entity.prefix_test AS prefix_test")
.contains("dummy_entity.prefix_attr1 AS prefix_attr1").contains("dummy_entity.prefix_attr2 AS prefix_attr2")
.contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1")
.contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2").doesNotContain("JOIN")
.doesNotContain("embeddable");
softAssertions.assertAll();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(sql).startsWith("SELECT") //
.contains("dummy_entity.id1 AS id1") //
.contains("dummy_entity.test AS test") //
.contains("dummy_entity.attr1 AS attr1") //
.contains("dummy_entity.attr2 AS attr2") //
.contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") //
.contains("dummy_entity.prefix2_attr2 AS prefix2_attr2") //
.contains("dummy_entity.prefix_test AS prefix_test") //
.contains("dummy_entity.prefix_attr1 AS prefix_attr1") //
.contains("dummy_entity.prefix_attr2 AS prefix_attr2") //
.contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") //
.contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2") //
.doesNotContain("JOIN") //
.doesNotContain("embeddable");
});
}
@Test // DATAJDBC-111
public void findAllInList() {
final String sql = sqlGenerator.getFindAllInList();
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql).startsWith("SELECT").contains("dummy_entity.id1 AS id1")
.contains("dummy_entity.test AS test").contains("dummy_entity.attr1 AS attr1")
.contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1")
.contains("dummy_entity.prefix2_attr2 AS prefix2_attr2").contains("dummy_entity.prefix_test AS prefix_test")
.contains("dummy_entity.prefix_attr1 AS prefix_attr1").contains("dummy_entity.prefix_attr2 AS prefix_attr2")
.contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1")
.contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2")
.contains("WHERE dummy_entity.id1 IN (:ids)").doesNotContain("JOIN").doesNotContain("embeddable");
softAssertions.assertAll();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(sql).startsWith("SELECT") //
.contains("dummy_entity.id1 AS id1") //
.contains("dummy_entity.test AS test") //
.contains("dummy_entity.attr1 AS attr1") //
.contains("dummy_entity.attr2 AS attr2").contains("dummy_entity.prefix2_attr1 AS prefix2_attr1") //
.contains("dummy_entity.prefix2_attr2 AS prefix2_attr2") //
.contains("dummy_entity.prefix_test AS prefix_test") //
.contains("dummy_entity.prefix_attr1 AS prefix_attr1") //
.contains("dummy_entity.prefix_attr2 AS prefix_attr2") //
.contains("dummy_entity.prefix_prefix2_attr1 AS prefix_prefix2_attr1") //
.contains("dummy_entity.prefix_prefix2_attr2 AS prefix_prefix2_attr2") //
.contains("WHERE dummy_entity.id1 IN (:ids)") //
.doesNotContain("JOIN") //
.doesNotContain("embeddable");
});
}
@Test // DATAJDBC-111
public void insert() {
final String sql = sqlGenerator.getInsert(emptySet());
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql).startsWith("INSERT INTO").contains("dummy_entity").contains(":test")
.contains(":attr1").contains(":attr2").contains(":prefix2_attr1").contains(":prefix2_attr2")
.contains(":prefix_test").contains(":prefix_attr1").contains(":prefix_attr2").contains(":prefix_prefix2_attr1")
.contains(":prefix_prefix2_attr2");
softAssertions.assertAll();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(sql) //
.startsWith("INSERT INTO") //
.contains("dummy_entity") //
.contains(":test") //
.contains(":attr1") //
.contains(":attr2") //
.contains(":prefix2_attr1") //
.contains(":prefix2_attr2") //
.contains(":prefix_test") //
.contains(":prefix_attr1") //
.contains(":prefix_attr2") //
.contains(":prefix_prefix2_attr1") //
.contains(":prefix_prefix2_attr2");
});
}
@Test // DATAJDBC-111
public void update() {
final String sql = sqlGenerator.getUpdate();
SoftAssertions softAssertions = new SoftAssertions();
softAssertions.assertThat(sql).startsWith("UPDATE").contains("dummy_entity").contains("test = :test")
.contains("attr1 = :attr1").contains("attr2 = :attr2").contains("prefix2_attr1 = :prefix2_attr1")
.contains("prefix2_attr2 = :prefix2_attr2").contains("prefix_test = :prefix_test")
.contains("prefix_attr1 = :prefix_attr1").contains("prefix_attr2 = :prefix_attr2")
.contains("prefix_prefix2_attr1 = :prefix_prefix2_attr1")
.contains("prefix_prefix2_attr2 = :prefix_prefix2_attr2");
softAssertions.assertAll();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(sql) //
.startsWith("UPDATE") //
.contains("dummy_entity") //
.contains("test = :test") //
.contains("attr1 = :attr1") //
.contains("attr2 = :attr2") //
.contains("prefix2_attr1 = :prefix2_attr1") //
.contains("prefix2_attr2 = :prefix2_attr2") //
.contains("prefix_test = :prefix_test") //
.contains("prefix_attr1 = :prefix_attr1") //
.contains("prefix_attr2 = :prefix_attr2") //
.contains("prefix_prefix2_attr1 = :prefix_prefix2_attr1") //
.contains("prefix_prefix2_attr2 = :prefix_prefix2_attr2");
});
}
@Test // DATAJDBC-340

View File

@@ -16,6 +16,7 @@
package org.springframework.data.jdbc.core.convert;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
@@ -23,10 +24,13 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.PersistentPropertyPathTestUtils;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier;
/**
* Unit tests the {@link SqlGenerator} with a fixed {@link NamingStrategy} implementation containing a hard wired
@@ -39,31 +43,31 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
final NamingStrategy fixedCustomTablePrefixStrategy = new NamingStrategy() {
@Override
public String getSchema() {
return "FixedCustomSchema";
public SqlIdentifier getSchema() {
return unquoted("FixedCustomSchema");
}
@Override
public String getTableName(Class<?> type) {
return "FixedCustomTablePrefix_" + type.getSimpleName();
public SqlIdentifier getTableName(Class<?> type) {
return unquoted("FixedCustomTablePrefix_" + type.getSimpleName());
}
@Override
public String getColumnName(RelationalPersistentProperty property) {
return "FixedCustomPropertyPrefix_" + property.getName();
public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) {
return unquoted("FixedCustomPropertyPrefix_" + property.getName());
}
};
final NamingStrategy upperCaseLowerCaseStrategy = new NamingStrategy() {
@Override
public String getTableName(Class<?> type) {
return type.getSimpleName().toUpperCase();
public SqlIdentifier getTableName(Class<?> type) {
return unquoted(type.getSimpleName().toUpperCase());
}
@Override
public String getColumnName(RelationalPersistentProperty property) {
return property.getName().toLowerCase();
public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) {
return unquoted(property.getName().toLowerCase());
}
};
@@ -83,8 +87,8 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
"FixedCustomSchema.FixedCustomTablePrefix_DummyEntity.FixedCustomPropertyPrefix_id AS FixedCustomPropertyPrefix_id,") //
.contains(
"FixedCustomSchema.FixedCustomTablePrefix_DummyEntity.FixedCustomPropertyPrefix_name AS FixedCustomPropertyPrefix_name,") //
.contains("ref.FixedCustomPropertyPrefix_l1id AS ref_FixedCustomPropertyPrefix_l1id") //
.contains("ref.FixedCustomPropertyPrefix_content AS ref_FixedCustomPropertyPrefix_content") //
.contains("\"REF\".FixedCustomPropertyPrefix_l1id AS ref_FixedCustomPropertyPrefix_l1id") //
.contains("\"REF\".FixedCustomPropertyPrefix_content AS ref_FixedCustomPropertyPrefix_content") //
.contains("FROM FixedCustomSchema.FixedCustomTablePrefix_DummyEntity");
softAssertions.assertAll();
}
@@ -101,8 +105,8 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
.startsWith("SELECT") //
.contains("DUMMYENTITY.id AS id,") //
.contains("DUMMYENTITY.name AS name,") //
.contains("ref.l1id AS ref_l1id") //
.contains("ref.content AS ref_content") //
.contains("\"REF\".l1id AS ref_l1id") //
.contains("\"REF\".content AS ref_content") //
.contains("FROM DUMMYENTITY");
softAssertions.assertAll();
}
@@ -115,7 +119,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
String sql = sqlGenerator.createDeleteByPath(getPath("ref"));
assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity = :rootId");
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" = :rootId");
}
@Test // DATAJDBC-107
@@ -126,10 +130,10 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
String sql = sqlGenerator.createDeleteByPath(getPath("ref.further"));
assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.referenced_entity IN "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.\"REFERENCED_ENTITY\" IN "
+ "(SELECT FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.FixedCustomPropertyPrefix_l1id "
+ "FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity = :rootId)");
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" = :rootId)");
}
@Test // DATAJDBC-107
@@ -150,7 +154,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
String sql = sqlGenerator.createDeleteAllSql(getPath("ref"));
assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity IS NOT NULL");
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" IS NOT NULL");
}
@Test // DATAJDBC-107
@@ -161,10 +165,10 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
String sql = sqlGenerator.createDeleteAllSql(getPath("ref.further"));
assertThat(sql).isEqualTo("DELETE FROM FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.referenced_entity IN "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_SecondLevelReferencedEntity.\"REFERENCED_ENTITY\" IN "
+ "(SELECT FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.FixedCustomPropertyPrefix_l1id "
+ "FROM FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity "
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.dummy_entity IS NOT NULL)");
+ "WHERE FixedCustomSchema.FixedCustomTablePrefix_ReferencedEntity.\"DUMMY_ENTITY\" IS NOT NULL)");
}
@Test // DATAJDBC-113
@@ -189,7 +193,7 @@ public class SqlGeneratorFixedNamingStrategyUnitTests {
RelationalMappingContext context = new JdbcMappingContext(namingStrategy);
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class);
return new SqlGenerator(context, persistentEntity);
return new SqlGenerator(context, persistentEntity, IdentifierProcessing.ANSI);
}
@SuppressWarnings("unused")

View File

@@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core.convert;
import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import java.util.Map;
import java.util.Set;
@@ -41,6 +42,11 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentProp
import org.springframework.data.relational.core.sql.Aliased;
import org.springframework.data.relational.core.sql.Table;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.domain.IdentifierProcessing.Quoting;
import org.springframework.data.relational.domain.SqlIdentifier.*;
/**
* Unit tests for the {@link SqlGenerator}.
@@ -54,7 +60,7 @@ import org.springframework.data.relational.domain.Identifier;
*/
public class SqlGeneratorUnitTests {
static final Identifier BACKREF = Identifier.of("backref", "some-value", String.class);
static final Identifier BACKREF = Identifier.of(unquoted("backref"), "some-value", String.class);
SqlGenerator sqlGenerator;
NamingStrategy namingStrategy = new PrefixingNamingStrategy();
@@ -67,9 +73,14 @@ public class SqlGeneratorUnitTests {
SqlGenerator createSqlGenerator(Class<?> type) {
return createSqlGenerator(type, new DefaultIdentifierProcessing(new Quoting(""), LetterCasing.AS_IS));
}
SqlGenerator createSqlGenerator(Class<?> type, IdentifierProcessing identifierProcessing) {
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(type);
return new SqlGenerator(context, persistentEntity);
return new SqlGenerator(context, persistentEntity, identifierProcessing);
}
@Test // DATAJDBC-112
@@ -174,8 +185,8 @@ public class SqlGeneratorUnitTests {
public void findAllByPropertyWithMultipartIdentifier() {
// this would get called when ListParent is the element type of a Set
Identifier parentIdentifier = Identifier.of("backref", "some-value", String.class) //
.withPart("backref_key", "key-value", Object.class);
Identifier parentIdentifier = Identifier.of(unquoted("backref"), "some-value", String.class) //
.withPart(unquoted("backref_key"), "key-value", Object.class);
String sql = sqlGenerator.getFindAllByProperty(parentIdentifier, null, false);
assertThat(sql).contains("SELECT", //
@@ -197,7 +208,7 @@ public class SqlGeneratorUnitTests {
public void findAllByPropertyWithKey() {
// this would get called when ListParent is th element type of a Map
String sql = sqlGenerator.getFindAllByProperty(BACKREF, "key-column", false);
String sql = sqlGenerator.getFindAllByProperty(BACKREF, unquoted("key-column"), false);
assertThat(sql).isEqualTo("SELECT dummy_entity.id1 AS id1, dummy_entity.x_name AS x_name, " //
+ "dummy_entity.x_other AS x_other, " //
@@ -219,7 +230,7 @@ public class SqlGeneratorUnitTests {
public void findAllByPropertyWithKeyOrdered() {
// this would get called when ListParent is th element type of a Map
String sql = sqlGenerator.getFindAllByProperty(BACKREF, "key-column", true);
String sql = sqlGenerator.getFindAllByProperty(BACKREF, unquoted("key-column"), true);
assertThat(sql).isEqualTo("SELECT dummy_entity.id1 AS id1, dummy_entity.x_name AS x_name, " //
+ "dummy_entity.x_other AS x_other, " //
@@ -235,16 +246,16 @@ public class SqlGeneratorUnitTests {
@Test // DATAJDBC-219
public void updateWithVersion() {
SqlGenerator sqlGenerator = createSqlGenerator(VersionedEntity.class);
SqlGenerator sqlGenerator = createSqlGenerator(VersionedEntity.class, IdentifierProcessing.ANSI);
assertThat(sqlGenerator.getUpdateWithVersion()).containsSequence( //
"UPDATE", //
"versioned_entity", //
"\"VERSIONED_ENTITY\"", //
"SET", //
"WHERE", //
"id1 = :id", //
"\"ID1\" = :ID1", //
"AND", //
"version = :___oldOptimisticLockingVersion");
"\"X_VERSION\" = :___oldOptimisticLockingVersion");
}
@Test // DATAJDBC-264
@@ -260,66 +271,69 @@ public class SqlGeneratorUnitTests {
@Test // DATAJDBC-334
public void getInsertForQuotedColumnName() {
SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class);
SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class, IdentifierProcessing.ANSI);
String insert = sqlGenerator.getInsert(emptySet());
assertThat(insert)
.isEqualTo("INSERT INTO entity_with_quoted_column_name " + "(\"test_@123\") " + "VALUES (:test_123)");
assertThat(insert).isEqualTo("INSERT INTO \"ENTITY_WITH_QUOTED_COLUMN_NAME\" " //
+ "(\"TEST\"\"_@123\") " + "VALUES (:TEST_123)");
}
@Test // DATAJDBC-266
public void joinForOneToOneWithoutIdIncludesTheBackReferenceOfTheOuterJoin() {
SqlGenerator sqlGenerator = createSqlGenerator(ParentOfNoIdChild.class);
SqlGenerator sqlGenerator = createSqlGenerator(ParentOfNoIdChild.class, IdentifierProcessing.ANSI);
String findAll = sqlGenerator.getFindAll();
assertThat(findAll).containsSequence("SELECT", "child.parent_of_no_id_child AS child_parent_of_no_id_child",
"FROM");
assertThat(findAll).containsSequence("SELECT",
"\"CHILD\".\"PARENT_OF_NO_ID_CHILD\" AS \"CHILD_PARENT_OF_NO_ID_CHILD\"", "FROM");
}
@Test // DATAJDBC-262
public void update() {
SqlGenerator sqlGenerator = createSqlGenerator(DummyEntity.class, IdentifierProcessing.ANSI);
assertThat(sqlGenerator.getUpdate()).containsSequence( //
"UPDATE", //
"dummy_entity", //
"\"DUMMY_ENTITY\"", //
"SET", //
"WHERE", //
"id1 = :id");
"\"ID1\" = :ID");
}
@Test // DATAJDBC-324
public void readOnlyPropertyExcludedFromQuery_when_generateUpdateSql() {
final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class);
final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class, IdentifierProcessing.ANSI);
assertThat(sqlGenerator.getUpdate()).isEqualToIgnoringCase( //
"UPDATE entity_with_read_only_property " //
+ "SET x_name = :x_name " //
+ "WHERE entity_with_read_only_property.x_id = :x_id" //
"UPDATE \"ENTITY_WITH_READ_ONLY_PROPERTY\" " //
+ "SET \"X_NAME\" = :X_NAME " //
+ "WHERE \"ENTITY_WITH_READ_ONLY_PROPERTY\".\"X_ID\" = :X_ID" //
);
}
@Test // DATAJDBC-334
public void getUpdateForQuotedColumnName() {
SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class);
SqlGenerator sqlGenerator = createSqlGenerator(EntityWithQuotedColumnName.class, IdentifierProcessing.ANSI);
String update = sqlGenerator.getUpdate();
assertThat(update).isEqualTo("UPDATE entity_with_quoted_column_name " + "SET \"test_@123\" = :test_123 "
+ "WHERE entity_with_quoted_column_name.\"test_@id\" = :test_id");
assertThat(update).isEqualTo("UPDATE \"ENTITY_WITH_QUOTED_COLUMN_NAME\" " //
+ "SET \"TEST\"\"_@123\" = :TEST_123 " //
+ "WHERE \"ENTITY_WITH_QUOTED_COLUMN_NAME\".\"TEST\"\"_@ID\" = :TEST_ID");
}
@Test // DATAJDBC-324
public void readOnlyPropertyExcludedFromQuery_when_generateInsertSql() {
final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class);
final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class, IdentifierProcessing.ANSI);
assertThat(sqlGenerator.getInsert(emptySet())).isEqualToIgnoringCase( //
"INSERT INTO entity_with_read_only_property (x_name) " //
"INSERT INTO \"ENTITY_WITH_READ_ONLY_PROPERTY\" (\"X_NAME\") " //
+ "VALUES (:x_name)" //
);
}
@@ -340,7 +354,7 @@ public class SqlGeneratorUnitTests {
final SqlGenerator sqlGenerator = createSqlGenerator(EntityWithReadOnlyProperty.class);
assertThat(sqlGenerator.getFindAllByProperty(BACKREF, "key-column", true)).isEqualToIgnoringCase( //
assertThat(sqlGenerator.getFindAllByProperty(BACKREF, unquoted("key-column"), true)).isEqualToIgnoringCase( //
"SELECT " //
+ "entity_with_read_only_property.x_id AS x_id, " //
+ "entity_with_read_only_property.x_name AS x_name, " //
@@ -434,14 +448,15 @@ public class SqlGeneratorUnitTests {
@Test // DATAJDBC-340
public void joinForSimpleReference() {
SqlGenerator.Join join = generateJoin("ref", DummyEntity.class);
SoftAssertions.assertSoftly(softly -> {
SqlGenerator.Join join = generateJoin("ref", DummyEntity.class);
softly.assertThat(join.getJoinTable().getName()).isEqualTo("referenced_entity");
softly.assertThat(join.getJoinTable().getName()).isEqualTo("\"REFERENCED_ENTITY\"");
softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable());
softly.assertThat(join.getJoinColumn().getName()).isEqualTo("dummy_entity");
softly.assertThat(join.getParentId().getName()).isEqualTo("id1");
softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("dummy_entity");
softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"DUMMY_ENTITY\"");
softly.assertThat(join.getParentId().getName()).isEqualTo("\"ID1\"");
softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"DUMMY_ENTITY\"");
});
}
@@ -465,37 +480,39 @@ public class SqlGeneratorUnitTests {
@Test // DATAJDBC-340
public void joinForSecondLevelReference() {
SqlGenerator.Join join = generateJoin("ref.further", DummyEntity.class);
SoftAssertions.assertSoftly(softly -> {
SqlGenerator.Join join = generateJoin("ref.further", DummyEntity.class);
softly.assertThat(join.getJoinTable().getName()).isEqualTo("second_level_referenced_entity");
softly.assertThat(join.getJoinTable().getName()).isEqualTo("\"SECOND_LEVEL_REFERENCED_ENTITY\"");
softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable());
softly.assertThat(join.getJoinColumn().getName()).isEqualTo("referenced_entity");
softly.assertThat(join.getParentId().getName()).isEqualTo("x_l1id");
softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("referenced_entity");
softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"REFERENCED_ENTITY\"");
softly.assertThat(join.getParentId().getName()).isEqualTo("\"X_L1ID\"");
softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"REFERENCED_ENTITY\"");
});
}
@Test // DATAJDBC-340
public void joinForOneToOneWithoutId() {
SqlGenerator.Join join = generateJoin("child", ParentOfNoIdChild.class);
Table joinTable = join.getJoinTable();
SoftAssertions.assertSoftly(softly -> {
SqlGenerator.Join join = generateJoin("child", ParentOfNoIdChild.class);
Table joinTable = join.getJoinTable();
softly.assertThat(joinTable.getName()).isEqualTo("no_id_child");
softly.assertThat(joinTable.getName()).isEqualTo("\"NO_ID_CHILD\"");
softly.assertThat(joinTable).isInstanceOf(Aliased.class);
softly.assertThat(((Aliased) joinTable).getAlias()).isEqualTo("child");
softly.assertThat(((Aliased) joinTable).getAlias()).isEqualTo("\"CHILD\"");
softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(joinTable);
softly.assertThat(join.getJoinColumn().getName()).isEqualTo("parent_of_no_id_child");
softly.assertThat(join.getParentId().getName()).isEqualTo("x_id");
softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("parent_of_no_id_child");
softly.assertThat(join.getJoinColumn().getName()).isEqualTo("\"PARENT_OF_NO_ID_CHILD\"");
softly.assertThat(join.getParentId().getName()).isEqualTo("\"X_ID\"");
softly.assertThat(join.getParentId().getTable().getName()).isEqualTo("\"PARENT_OF_NO_ID_CHILD\"");
});
}
private SqlGenerator.Join generateJoin(String path, Class<?> type) {
return createSqlGenerator(type)
return createSqlGenerator(type, IdentifierProcessing.ANSI)
.getJoin(new PersistentPropertyPathExtension(context, PropertyPathTestingUtils.toPath(path, type, context)));
}
@@ -504,7 +521,7 @@ public class SqlGeneratorUnitTests {
assertThat(generatedColumn("id", DummyEntity.class)) //
.extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias)
.containsExactly("id1", "dummy_entity", null, "id1");
.containsExactly("\"ID1\"", "\"DUMMY_ENTITY\"", null, "\"ID1\"");
}
@Test // DATAJDBC-340
@@ -512,7 +529,7 @@ public class SqlGeneratorUnitTests {
assertThat(generatedColumn("ref.l1id", DummyEntity.class)) //
.extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) //
.containsExactly("x_l1id", "referenced_entity", "ref", "ref_x_l1id");
.containsExactly("\"X_L1ID\"", "\"REFERENCED_ENTITY\"", "\"REF\"", "\"REF_X_L1ID\"");
}
@Test // DATAJDBC-340
@@ -526,7 +543,8 @@ public class SqlGeneratorUnitTests {
assertThat(generatedColumn("child", ParentOfNoIdChild.class)) //
.extracting(c -> c.getName(), c -> c.getTable().getName(), c -> getAlias(c.getTable()), this::getAlias) //
.containsExactly("parent_of_no_id_child", "no_id_child", "child", "child_parent_of_no_id_child");
.containsExactly("\"PARENT_OF_NO_ID_CHILD\"", "\"NO_ID_CHILD\"", "\"CHILD\"",
"\"CHILD_PARENT_OF_NO_ID_CHILD\"");
}
private String getAlias(Object maybeAliased) {
@@ -539,7 +557,7 @@ public class SqlGeneratorUnitTests {
private org.springframework.data.relational.core.sql.Column generatedColumn(String path, Class<?> type) {
return createSqlGenerator(type)
return createSqlGenerator(type, IdentifierProcessing.ANSI)
.getColumn(new PersistentPropertyPathExtension(context, PropertyPathTestingUtils.toPath(path, type, context)));
}
@@ -599,8 +617,8 @@ public class SqlGeneratorUnitTests {
private static class PrefixingNamingStrategy implements NamingStrategy {
@Override
public String getColumnName(RelationalPersistentProperty property) {
return "x_" + NamingStrategy.super.getColumnName(property);
public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) {
return NamingStrategy.super.getColumnName(property).prefix("x_");
}
}
@@ -621,8 +639,10 @@ public class SqlGeneratorUnitTests {
static class EntityWithQuotedColumnName {
@Id @Column("\"test_@id\"") Long id;
@Column("\"test_@123\"") String name;
// these column names behave like single double quote in the name since the get quoted and then doubling the double
// quote escapes it.
@Id @Column("test\"\"_@id") Long id;
@Column("test\"\"_@123") String name;
}
@SuppressWarnings("unused")

View File

@@ -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);
});
}
}

View File

@@ -15,7 +15,8 @@
*/
package org.springframework.data.jdbc.core.mapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import lombok.Data;
@@ -27,7 +28,6 @@ import java.util.UUID;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.relational.core.mapping.BasicRelationalPersistentProperty;
@@ -89,9 +89,9 @@ public class BasicJdbcPersistentPropertyUnitTests {
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(DummyEntity.class);
assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo("dummy_name");
assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo(quoted("dummy_name"));
assertThat(entity.getRequiredPersistentProperty("localDateTime").getColumnName())
.isEqualTo("dummy_last_updated_at");
.isEqualTo(quoted("dummy_last_updated_at"));
}
@Test // DATAJDBC-218
@@ -101,8 +101,8 @@ public class BasicJdbcPersistentPropertyUnitTests {
.getRequiredPersistentEntity(DummyEntity.class) //
.getRequiredPersistentProperty("someList");
assertThat(listProperty.getReverseColumnName()).isEqualTo("dummy_column_name");
assertThat(listProperty.getKeyColumn()).isEqualTo("dummy_key_column_name");
assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("dummy_column_name"));
assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("dummy_key_column_name"));
}
@Test // DATAJDBC-221
@@ -125,8 +125,8 @@ public class BasicJdbcPersistentPropertyUnitTests {
.getRequiredPersistentEntity(WithCollections.class) //
.getRequiredPersistentProperty("someList");
assertThat(listProperty.getKeyColumn()).isEqualTo("some_key");
assertThat(listProperty.getReverseColumnName()).isEqualTo("some_value");
assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("some_key"));
assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("some_value"));
}
@Test // DATAJDBC-331
@@ -136,8 +136,8 @@ public class BasicJdbcPersistentPropertyUnitTests {
.getRequiredPersistentEntity(WithCollections.class) //
.getRequiredPersistentProperty("overrideList");
assertThat(listProperty.getKeyColumn()).isEqualTo("override_key");
assertThat(listProperty.getReverseColumnName()).isEqualTo("override_id");
assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("override_key"));
assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("override_id"));
}
private void checkTargetType(SoftAssertions softly, RelationalPersistentEntity<?> persistentEntity,
@@ -148,6 +148,11 @@ public class BasicJdbcPersistentPropertyUnitTests {
softly.assertThat(property.getColumnType()).describedAs(propertyName).isEqualTo(expected);
}
@SuppressWarnings("unused")
private enum SomeEnum {
ALPHA
}
@Data
@SuppressWarnings("unused")
private static class DummyEntity {
@@ -187,9 +192,4 @@ public class BasicJdbcPersistentPropertyUnitTests {
@Column(value = "some_value", keyColumn = "some_key") @MappedCollection(idColumn = "override_id",
keyColumn = "override_key") List<Integer> overrideList;
}
@SuppressWarnings("unused")
private enum SomeEnum {
ALPHA
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.jdbc.mapping.model;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import lombok.Data;
@@ -27,6 +28,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Unit tests for the default {@link NamingStrategy}.
@@ -44,42 +46,42 @@ public class NamingStrategyUnitTests {
@Test // DATAJDBC-184
public void getTableName() {
assertThat(target.getTableName(persistentEntity.getType())).isEqualTo("dummy_entity");
assertThat(target.getTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity"));
}
@Test // DATAJDBC-184
public void getColumnName() {
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("id"))) //
.isEqualTo("id");
.isEqualTo(quoted("id"));
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("createdAt"))) //
.isEqualTo("created_at");
.isEqualTo(quoted("created_at"));
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("dummySubEntities"))) //
.isEqualTo("dummy_sub_entities");
.isEqualTo(quoted("dummy_sub_entities"));
}
@Test // DATAJDBC-184
public void getReverseColumnName() {
assertThat(target.getReverseColumnName(persistentEntity.getPersistentProperty("dummySubEntities")))
.isEqualTo("dummy_entity");
.isEqualTo(quoted("dummy_entity"));
}
@Test // DATAJDBC-184
public void getKeyColumn() {
assertThat(target.getKeyColumn(persistentEntity.getPersistentProperty("dummySubEntities"))) //
.isEqualTo("dummy_entity_key");
.isEqualTo(quoted("dummy_entity_key"));
}
@Test // DATAJDBC-184
public void getSchema() {
assertThat(target.getSchema()).isEmpty();
assertThat(target.getSchema()).isEqualTo(SqlIdentifier.EMPTY);
}
@Test // DATAJDBC-184
public void getQualifiedTableName() {
assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo("dummy_entity");
assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity"));
}
@Data

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.jdbc.mybatis;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.*;
import junit.framework.AssertionFailedError;
@@ -39,6 +39,7 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
@@ -79,6 +80,8 @@ public class MyBatisCustomizingNamespaceHsqlIntegrationTests {
assertThat(reloaded.name).isEqualTo("name " + saved.id);
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
@org.springframework.context.annotation.Configuration
@Import(TestConfiguration.class)
@EnableJdbcRepositories(considerNestedRepositories = true)
@@ -116,8 +119,9 @@ public class MyBatisCustomizingNamespaceHsqlIntegrationTests {
RelationalMappingContext context = new JdbcMappingContext();
JdbcConverter converter = new BasicJdbcConverter(context, (Identifier, path) -> null);
MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession, context, converter);
MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession,
HsqlDbDialect.INSTANCE.getIdentifierProcessing());
strategy.setNamespaceStrategy(new NamespaceStrategy() {
@Override
@@ -129,6 +133,4 @@ public class MyBatisCustomizingNamespaceHsqlIntegrationTests {
return strategy;
}
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
}

View File

@@ -15,18 +15,12 @@
*/
package org.springframework.data.jdbc.mybatis;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import java.util.Collections;
@@ -44,6 +38,7 @@ import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.relational.domain.IdentifierProcessing;
/**
* Unit tests for the {@link MyBatisDataAccessStrategy}, mainly ensuring that the correct statements get's looked up.
@@ -60,7 +55,7 @@ public class MyBatisDataAccessStrategyUnitTests {
SqlSession session = mock(SqlSession.class);
ArgumentCaptor<MyBatisContext> captor = ArgumentCaptor.forClass(MyBatisContext.class);
MyBatisDataAccessStrategy accessStrategy = new MyBatisDataAccessStrategy(session, context, converter);
MyBatisDataAccessStrategy accessStrategy = new MyBatisDataAccessStrategy(session, IdentifierProcessing.ANSI);
PersistentPropertyPath<RelationalPersistentProperty> path = PropertyPathTestingUtils.toPath("one.two",
DummyEntity.class, context);
@@ -74,7 +69,7 @@ public class MyBatisDataAccessStrategyUnitTests {
@Test // DATAJDBC-123
public void insert() {
accessStrategy.insert("x", String.class, Collections.singletonMap("key", "value"));
accessStrategy.insert("x", String.class, Collections.singletonMap(unquoted("key"), "value"));
verify(session).insert(eq("java.lang.StringMapper.insert"), captor.capture());

View File

@@ -35,6 +35,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@@ -57,6 +58,36 @@ import org.springframework.transaction.annotation.Transactional;
@Transactional
public class MyBatisHsqlIntegrationTests {
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
@Autowired SqlSessionFactory sqlSessionFactory;
@Autowired DummyEntityRepository repository;
@Test // DATAJDBC-123
public void mybatisSelfTest() {
SqlSession session = sqlSessionFactory.openSession();
session.selectList("org.springframework.data.jdbc.mybatis.DummyEntityMapper.findById");
}
@Test // DATAJDBC-123
public void myBatisGetsUsedForInsertAndSelect() {
DummyEntity entity = new DummyEntity(null, "some name");
DummyEntity saved = repository.save(entity);
assertThat(saved.id).isNotNull();
DummyEntity reloaded = repository.findById(saved.id).orElseThrow(AssertionFailedError::new);
assertThat(reloaded).isNotNull().extracting(e -> e.id, e -> e.name);
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
}
@org.springframework.context.annotation.Configuration
@Import(TestConfiguration.class)
@EnableJdbcRepositories(considerNestedRepositories = true)
@@ -94,38 +125,7 @@ public class MyBatisHsqlIntegrationTests {
SqlSession sqlSession, EmbeddedDatabase db) {
return MyBatisDataAccessStrategy.createCombinedAccessStrategy(context, converter,
new NamedParameterJdbcTemplate(db), sqlSession);
new NamedParameterJdbcTemplate(db), sqlSession, HsqlDbDialect.INSTANCE);
}
}
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
@Autowired SqlSessionFactory sqlSessionFactory;
@Autowired DummyEntityRepository repository;
@Test // DATAJDBC-123
public void mybatisSelfTest() {
SqlSession session = sqlSessionFactory.openSession();
session.selectList("org.springframework.data.jdbc.mybatis.DummyEntityMapper.findById");
}
@Test // DATAJDBC-123
public void myBatisGetsUsedForInsertAndSelect() {
DummyEntity entity = new DummyEntity(null, "some name");
DummyEntity saved = repository.save(entity);
assertThat(saved.id).isNotNull();
DummyEntity reloaded = repository.findById(saved.id).orElseThrow(AssertionFailedError::new);
assertThat(reloaded).isNotNull().extracting(e -> e.id, e -> e.name);
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.jdbc.repository;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import lombok.Data;
import lombok.Value;
@@ -27,7 +28,6 @@ import java.util.concurrent.atomic.AtomicLong;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
@@ -39,6 +39,7 @@ import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
@@ -166,8 +167,8 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
return new NamingStrategy() {
@Override
public String getTableName(Class<?> type) {
return type.getSimpleName().toUpperCase();
public SqlIdentifier getTableName(Class<?> type) {
return unquoted(type.getSimpleName().toUpperCase());
}
};
}

View File

@@ -45,6 +45,7 @@ import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.event.AfterDeleteEvent;
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
@@ -82,7 +83,7 @@ public class SimpleJdbcRepositoryEventsUnitTests {
DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy();
JdbcConverter converter = new BasicJdbcConverter(context, delegatingDataAccessStrategy, new JdbcCustomConversions(),
new DefaultJdbcTypeFactory(operations.getJdbcOperations()));
SqlGeneratorSource generatorSource = new SqlGeneratorSource(context);
SqlGeneratorSource generatorSource = new SqlGeneratorSource(context, HsqlDbDialect.INSTANCE);
this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations));
delegatingDataAccessStrategy.setDelegate(dataAccessStrategy);

View File

@@ -16,6 +16,7 @@
package org.springframework.data.jdbc.repository.config;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import lombok.Data;
@@ -44,6 +45,7 @@ import org.springframework.data.domain.AuditorAware;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ActiveProfiles;
@@ -271,8 +273,8 @@ public class EnableJdbcAuditingHsqlIntegrationTests {
return new NamingStrategy() {
public String getTableName(@NotNull Class<?> type) {
return "DummyEntity";
public SqlIdentifier getTableName(@NotNull Class<?> type) {
return unquoted("DummyEntity");
}
};
}

View File

@@ -40,9 +40,9 @@ import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositoriesIntegrationTests.TestConfiguration;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@@ -69,7 +69,6 @@ public class EnableJdbcRepositoriesIntegrationTests {
"dataAccessStrategy");
public static final RowMapper DUMMY_ENTITY_ROW_MAPPER = mock(RowMapper.class);
public static final RowMapper STRING_ROW_MAPPER = mock(RowMapper.class);
public static final ResultSetExtractor<Integer> INTEGER_RESULT_SET_EXTRACTOR = mock(ResultSetExtractor.class);
@Autowired JdbcRepositoryFactoryBean factoryBean;
@Autowired DummyRepository repository;
@@ -153,8 +152,8 @@ public class EnableJdbcRepositoriesIntegrationTests {
@Bean("qualifierDataAccessStrategy")
DataAccessStrategy defaultDataAccessStrategy(
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template,
RelationalMappingContext context, JdbcConverter converter) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context), context, converter, template);
RelationalMappingContext context, JdbcConverter converter, Dialect dialect) {
return new DefaultDataAccessStrategy(new SqlGeneratorSource(context, dialect), context, converter, template);
}
}
}

View File

@@ -37,6 +37,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.DefaultDataAccessStrategy;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -60,6 +61,8 @@ public class JdbcRepositoryFactoryBeanUnitTests {
@Mock DataAccessStrategy dataAccessStrategy;
@Mock ApplicationEventPublisher publisher;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) ListableBeanFactory beanFactory;
@Mock
Dialect dialect;
RelationalMappingContext mappingContext;
@@ -86,6 +89,7 @@ public class JdbcRepositoryFactoryBeanUnitTests {
factoryBean.setConverter(new BasicJdbcConverter(mappingContext, dataAccessStrategy));
factoryBean.setApplicationEventPublisher(publisher);
factoryBean.setBeanFactory(beanFactory);
factoryBean.setDialect(dialect);
factoryBean.afterPropertiesSet();
assertThat(factoryBean.getObject()).isNotNull();
@@ -113,6 +117,7 @@ public class JdbcRepositoryFactoryBeanUnitTests {
factoryBean.setConverter(new BasicJdbcConverter(mappingContext, dataAccessStrategy));
factoryBean.setApplicationEventPublisher(publisher);
factoryBean.setBeanFactory(beanFactory);
factoryBean.setDialect(dialect);
factoryBean.afterPropertiesSet();
assertThat(factoryBean.getObject()).isNotNull();

View File

@@ -21,6 +21,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@@ -47,4 +49,9 @@ class HsqlDataSourceConfiguration {
.addScript(TestUtils.createScriptName(context, "hsql")) //
.build();
}
@Bean
Dialect dialect() {
return HsqlDbDialect.INSTANCE;
}
}

View File

@@ -22,8 +22,11 @@ import javax.script.ScriptException;
import javax.sql.DataSource;
import org.mariadb.jdbc.MariaDbDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.MariaDbDialect;
import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.jdbc.ext.ScriptUtils;
@@ -48,7 +51,9 @@ class MariaDBDataSourceConfiguration extends DataSourceConfiguration {
*/
@Override
protected DataSource createDataSource() {
try {
MariaDbDataSource dataSource = new MariaDbDataSource();
dataSource.setUrl(MARIADB_CONTAINER.getJdbcUrl());
dataSource.setUser(MARIADB_CONTAINER.getUsername());
@@ -59,6 +64,11 @@ class MariaDBDataSourceConfiguration extends DataSourceConfiguration {
}
}
@Bean
Dialect dialect() {
return MariaDbDialect.INSTANCE;
}
@PostConstruct
public void initDatabase() throws SQLException, ScriptException {
ScriptUtils.executeSqlScript(createDataSource().getConnection(), null, "DROP DATABASE test;CREATE DATABASE test;");

View File

@@ -21,8 +21,11 @@ import javax.annotation.PostConstruct;
import javax.script.ScriptException;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.MySqlDialect;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.jdbc.ext.ScriptUtils;
@@ -62,6 +65,11 @@ class MySqlDataSourceConfiguration extends DataSourceConfiguration {
return dataSource;
}
@Bean
Dialect dialect() {
return MySqlDialect.INSTANCE;
}
@PostConstruct
public void initDatabase() throws SQLException, ScriptException {
ScriptUtils.executeSqlScript(createDataSource().getConnection(), null, "DROP DATABASE test;CREATE DATABASE test;");

View File

@@ -18,8 +18,11 @@ package org.springframework.data.jdbc.testing;
import javax.sql.DataSource;
import org.postgresql.ds.PGSimpleDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.PostgresDialect;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.testcontainers.containers.PostgreSQLContainer;
@@ -55,6 +58,11 @@ public class PostgresDataSourceConfiguration extends DataSourceConfiguration {
return dataSource;
}
@Bean
Dialect dialect() {
return PostgresDialect.INSTANCE;
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.testing.DataSourceFactoryBean#customizePopulator(org.springframework.jdbc.datasource.init.ResourceDatabasePopulator)

View File

@@ -38,6 +38,7 @@ import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.repository.core.NamedQueries;
@@ -86,10 +87,10 @@ public class TestConfiguration {
@Bean
DataAccessStrategy defaultDataAccessStrategy(
@Qualifier("namedParameterJdbcTemplate") NamedParameterJdbcOperations template, RelationalMappingContext context,
JdbcConverter converter) {
JdbcConverter converter, Dialect dialect) {
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy(new SqlGeneratorSource(context),
context, converter, template);
DefaultDataAccessStrategy defaultDataAccessStrategy = new DefaultDataAccessStrategy(
new SqlGeneratorSource(context, dialect), context, converter, template);
return defaultDataAccessStrategy;
}

View File

@@ -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'
);

View File

@@ -11,302 +11,309 @@ DROP TABLE CHAIN3;
DROP TABLE CHAIN2;
DROP TABLE CHAIN1;
DROP TABLE CHAIN0;
DROP TABLE WITH_READ_ONLY;
CREATE TABLE LEGO_SET
(
id1 SERIAL PRIMARY KEY,
NAME VARCHAR(30)
id1 SERIAL PRIMARY KEY,
NAME VARCHAR(30)
);
CREATE TABLE MANUAL
(
id2 SERIAL PRIMARY KEY,
LEGO_SET BIGINT,
ALTERNATIVE BIGINT,
CONTENT VARCHAR(2000)
id2 SERIAL PRIMARY KEY,
LEGO_SET BIGINT,
ALTERNATIVE BIGINT,
CONTENT VARCHAR(2000)
);
ALTER TABLE MANUAL
ADD FOREIGN KEY (LEGO_SET)
REFERENCES LEGO_SET (id1);
ADD FOREIGN KEY (LEGO_SET)
REFERENCES LEGO_SET (id1);
CREATE TABLE ONE_TO_ONE_PARENT
(
id3 SERIAL PRIMARY KEY,
content VARCHAR(30)
id3 SERIAL PRIMARY KEY,
content VARCHAR(30)
);
CREATE TABLE Child_No_Id
(
ONE_TO_ONE_PARENT INTEGER PRIMARY KEY,
content VARCHAR(30)
ONE_TO_ONE_PARENT INTEGER PRIMARY KEY,
content VARCHAR(30)
);
CREATE TABLE LIST_PARENT
(
id4 SERIAL PRIMARY KEY,
NAME VARCHAR(100)
id4 SERIAL PRIMARY KEY,
NAME VARCHAR(100)
);
CREATE TABLE element_no_id
(
content VARCHAR(100),
LIST_PARENT_key BIGINT,
LIST_PARENT INTEGER
content VARCHAR(100),
LIST_PARENT_key BIGINT,
LIST_PARENT INTEGER
);
CREATE TABLE ARRAY_OWNER
(
ID SERIAL PRIMARY KEY,
DIGITS VARCHAR(20)[10],
MULTIDIMENSIONAL VARCHAR(20)[10][10]
ID SERIAL PRIMARY KEY,
DIGITS VARCHAR(20)[10],
MULTIDIMENSIONAL VARCHAR(20)[10][10]
);
CREATE TABLE BYTE_ARRAY_OWNER
(
ID SERIAL PRIMARY KEY,
BINARY_DATA BYTEA NOT NULL
ID SERIAL PRIMARY KEY,
BINARY_DATA BYTEA NOT NULL
);
CREATE TABLE CHAIN4
(
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
);
CREATE TABLE CHAIN3
(
THREE SERIAL PRIMARY KEY,
THREE_VALUE VARCHAR(20),
CHAIN4 BIGINT,
FOREIGN KEY (CHAIN4) REFERENCES CHAIN4 (FOUR)
THREE SERIAL PRIMARY KEY,
THREE_VALUE VARCHAR(20),
CHAIN4 BIGINT,
FOREIGN KEY (CHAIN4) REFERENCES CHAIN4 (FOUR)
);
CREATE TABLE CHAIN2
(
TWO SERIAL PRIMARY KEY,
TWO_VALUE VARCHAR(20),
CHAIN3 BIGINT,
FOREIGN KEY (CHAIN3) REFERENCES CHAIN3 (THREE)
TWO SERIAL PRIMARY KEY,
TWO_VALUE VARCHAR(20),
CHAIN3 BIGINT,
FOREIGN KEY (CHAIN3) REFERENCES CHAIN3 (THREE)
);
CREATE TABLE CHAIN1
(
ONE SERIAL PRIMARY KEY,
ONE_VALUE VARCHAR(20),
CHAIN2 BIGINT,
FOREIGN KEY (CHAIN2) REFERENCES CHAIN2 (TWO)
ONE SERIAL PRIMARY KEY,
ONE_VALUE VARCHAR(20),
CHAIN2 BIGINT,
FOREIGN KEY (CHAIN2) REFERENCES CHAIN2 (TWO)
);
CREATE TABLE CHAIN0
(
ZERO SERIAL PRIMARY KEY,
ZERO_VALUE VARCHAR(20),
CHAIN1 BIGINT,
FOREIGN KEY (CHAIN1) REFERENCES CHAIN1 (ONE)
ZERO SERIAL PRIMARY KEY,
ZERO_VALUE VARCHAR(20),
CHAIN1 BIGINT,
FOREIGN KEY (CHAIN1) REFERENCES CHAIN1 (ONE)
);
CREATE TABLE NO_ID_CHAIN4
(
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
);
CREATE TABLE NO_ID_CHAIN3
(
THREE_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
THREE_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
);
CREATE TABLE NO_ID_CHAIN2
(
TWO_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
TWO_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
);
CREATE TABLE NO_ID_CHAIN1
(
ONE_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
ONE_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
);
CREATE TABLE NO_ID_CHAIN0
(
ZERO_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
ZERO_VALUE VARCHAR(20),
NO_ID_CHAIN4 BIGINT,
FOREIGN KEY (NO_ID_CHAIN4) REFERENCES NO_ID_CHAIN4 (FOUR)
);
CREATE TABLE NO_ID_LIST_CHAIN4
(
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
);
CREATE TABLE NO_ID_LIST_CHAIN3
(
THREE_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY),
FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR)
THREE_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY),
FOREIGN KEY (NO_ID_LIST_CHAIN4) REFERENCES NO_ID_LIST_CHAIN4 (FOUR)
);
CREATE TABLE NO_ID_LIST_CHAIN2
(
TWO_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
NO_ID_LIST_CHAIN3_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY),
FOREIGN KEY (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY
) REFERENCES NO_ID_LIST_CHAIN3 (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY
)
TWO_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
NO_ID_LIST_CHAIN3_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY),
FOREIGN KEY (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY
) REFERENCES NO_ID_LIST_CHAIN3 (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY
)
);
CREATE TABLE NO_ID_LIST_CHAIN1
(
ONE_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
NO_ID_LIST_CHAIN3_KEY BIGINT,
NO_ID_LIST_CHAIN2_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY),
FOREIGN KEY (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY
) REFERENCES NO_ID_LIST_CHAIN2 (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY
)
ONE_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
NO_ID_LIST_CHAIN3_KEY BIGINT,
NO_ID_LIST_CHAIN2_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY),
FOREIGN KEY (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY
) REFERENCES NO_ID_LIST_CHAIN2 (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY
)
);
CREATE TABLE NO_ID_LIST_CHAIN0
(
ZERO_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
NO_ID_LIST_CHAIN3_KEY BIGINT,
NO_ID_LIST_CHAIN2_KEY BIGINT,
NO_ID_LIST_CHAIN1_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY,
NO_ID_LIST_CHAIN1_KEY),
FOREIGN KEY (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY
) REFERENCES NO_ID_LIST_CHAIN1 (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY
)
ZERO_VALUE VARCHAR(20),
NO_ID_LIST_CHAIN4 BIGINT,
NO_ID_LIST_CHAIN4_KEY BIGINT,
NO_ID_LIST_CHAIN3_KEY BIGINT,
NO_ID_LIST_CHAIN2_KEY BIGINT,
NO_ID_LIST_CHAIN1_KEY BIGINT,
PRIMARY KEY (NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY,
NO_ID_LIST_CHAIN1_KEY),
FOREIGN KEY (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY
) REFERENCES NO_ID_LIST_CHAIN1 (
NO_ID_LIST_CHAIN4,
NO_ID_LIST_CHAIN4_KEY,
NO_ID_LIST_CHAIN3_KEY,
NO_ID_LIST_CHAIN2_KEY
)
);
CREATE TABLE NO_ID_MAP_CHAIN4
(
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
FOUR SERIAL PRIMARY KEY,
FOUR_VALUE VARCHAR(20)
);
CREATE TABLE NO_ID_MAP_CHAIN3
(
THREE_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY),
FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR)
THREE_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY),
FOREIGN KEY (NO_ID_MAP_CHAIN4) REFERENCES NO_ID_MAP_CHAIN4 (FOUR)
);
CREATE TABLE NO_ID_MAP_CHAIN2
(
TWO_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
NO_ID_MAP_CHAIN3_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY),
FOREIGN KEY (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY
) REFERENCES NO_ID_MAP_CHAIN3 (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY
)
TWO_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
NO_ID_MAP_CHAIN3_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY),
FOREIGN KEY (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY
) REFERENCES NO_ID_MAP_CHAIN3 (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY
)
);
CREATE TABLE NO_ID_MAP_CHAIN1
(
ONE_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
NO_ID_MAP_CHAIN3_KEY VARCHAR(20),
NO_ID_MAP_CHAIN2_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY),
FOREIGN KEY (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY
) REFERENCES NO_ID_MAP_CHAIN2 (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY
)
ONE_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
NO_ID_MAP_CHAIN3_KEY VARCHAR(20),
NO_ID_MAP_CHAIN2_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY),
FOREIGN KEY (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY
) REFERENCES NO_ID_MAP_CHAIN2 (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY
)
);
CREATE TABLE NO_ID_MAP_CHAIN0
(
ZERO_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
NO_ID_MAP_CHAIN3_KEY VARCHAR(20),
NO_ID_MAP_CHAIN2_KEY VARCHAR(20),
NO_ID_MAP_CHAIN1_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY,
NO_ID_MAP_CHAIN1_KEY),
FOREIGN KEY (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY
) REFERENCES NO_ID_MAP_CHAIN1 (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY
)
ZERO_VALUE VARCHAR(20),
NO_ID_MAP_CHAIN4 BIGINT,
NO_ID_MAP_CHAIN4_KEY VARCHAR(20),
NO_ID_MAP_CHAIN3_KEY VARCHAR(20),
NO_ID_MAP_CHAIN2_KEY VARCHAR(20),
NO_ID_MAP_CHAIN1_KEY VARCHAR(20),
PRIMARY KEY (NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY,
NO_ID_MAP_CHAIN1_KEY),
FOREIGN KEY (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY
) REFERENCES NO_ID_MAP_CHAIN1 (
NO_ID_MAP_CHAIN4,
NO_ID_MAP_CHAIN4_KEY,
NO_ID_MAP_CHAIN3_KEY,
NO_ID_MAP_CHAIN2_KEY
)
);
CREATE TABLE VERSIONED_AGGREGATE
(
ID SERIAL PRIMARY KEY,
VERSION BIGINT
ID SERIAL PRIMARY KEY,
VERSION BIGINT
);
CREATE TABLE WITH_READ_ONLY
(
ID SERIAL PRIMARY KEY,
NAME VARCHAR(200),
READ_ONLY VARCHAR(200) DEFAULT 'from-db'
);

View File

@@ -16,6 +16,8 @@
package org.springframework.data.relational.core.dialect;
import org.springframework.data.relational.core.sql.render.SelectRenderContext;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Represents a dialect that is implemented by a particular database. Please note that not all features are supported by
@@ -50,4 +52,14 @@ public interface Dialect {
* @return the {@link SelectRenderContext}.
*/
SelectRenderContext getSelectContext();
/**
* Returns the {@link IdentifierProcessing} used for processing {@link SqlIdentifier} when converting them to SQL snippets or parameter names.
*
* @return the {@link IdentifierProcessing}. Guaranteed to be not {@literal null}.
* @since 2.0
*/
default IdentifierProcessing getIdentifierProcessing() {
return IdentifierProcessing.ANSI;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.relational.core.dialect;
/**
* A {@link Dialect} for HsqlDb.
*
* @author Jens Schauder
*/
public class HsqlDbDialect extends AbstractDialect {
public static final HsqlDbDialect INSTANCE = new HsqlDbDialect();
protected HsqlDbDialect() { }
@Override
public LimitClause limit() {
return LIMIT_CLAUSE;
}
private static final LimitClause LIMIT_CLAUSE = new LimitClause() {
@Override
public String getLimit(long limit) {
return "LIMIT " + limit;
}
@Override
public String getOffset(long offset) {
return "OFFSET " + offset;
}
@Override
public String getLimitOffset(long limit, long offset) {
return getOffset(offset) + " " + getLimit(limit);
}
@Override
public Position getClausePosition() {
return Position.AFTER_ORDER_BY;
}
};
}

View File

@@ -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() { }
}

View File

@@ -15,8 +15,13 @@
*/
package org.springframework.data.relational.core.dialect;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.domain.IdentifierProcessing.Quoting;
/**
* An SQL dialect for MySQL.
* A SQL dialect for MySQL.
*
* @author Mark Paluch
* @author Jens Schauder
@@ -29,6 +34,8 @@ public class MySqlDialect extends AbstractDialect {
*/
public static final MySqlDialect INSTANCE = new MySqlDialect();
protected MySqlDialect() { }
private static final LimitClause LIMIT_CLAUSE = new LimitClause() {
/*
@@ -80,4 +87,9 @@ public class MySqlDialect extends AbstractDialect {
public LimitClause limit() {
return LIMIT_CLAUSE;
}
@Override
public IdentifierProcessing getIdentifierProcessing() {
return new DefaultIdentifierProcessing(new Quoting("`"), LetterCasing.LOWER_CASE);
}
}

View File

@@ -17,6 +17,10 @@ package org.springframework.data.relational.core.dialect;
import lombok.RequiredArgsConstructor;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.domain.IdentifierProcessing.Quoting;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -33,6 +37,8 @@ public class PostgresDialect extends AbstractDialect {
*/
public static final PostgresDialect INSTANCE = new PostgresDialect();
protected PostgresDialect() {}
private static final LimitClause LIMIT_CLAUSE = new LimitClause() {
/*
@@ -116,4 +122,9 @@ public class PostgresDialect extends AbstractDialect {
return ClassUtils.resolvePrimitiveIfNecessary(userType);
}
}
@Override
public IdentifierProcessing getIdentifierProcessing() {
return new DefaultIdentifierProcessing(Quoting.ANSI, LetterCasing.LOWER_CASE);
}
}

View File

@@ -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() {
/*

View File

@@ -30,6 +30,8 @@ import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.relational.core.mapping.Embedded.OnEmpty;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.Optionals;
import org.springframework.lang.Nullable;
@@ -58,9 +60,9 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
}
private final RelationalMappingContext context;
private final Lazy<String> columnName;
private final Lazy<Optional<String>> collectionIdColumnName;
private final Lazy<String> collectionKeyColumnName;
private final Lazy<SimpleSqlIdentifier> columnName;
private final Lazy<Optional<SqlIdentifier>> collectionIdColumnName;
private final Lazy<SqlIdentifier> collectionKeyColumnName;
private final Lazy<Boolean> isEmbedded;
private final Lazy<String> embeddedPrefix;
private final Lazy<Class<?>> columnType = Lazy.of(this::doGetColumnType);
@@ -91,17 +93,24 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
this.columnName = Lazy.of(() -> Optional.ofNullable(findAnnotation(Column.class)) //
.map(Column::value) //
.filter(StringUtils::hasText) //
.map(name -> SqlIdentifier.quoted(name).withAdjustableLetterCasing()) //
.orElseGet(() -> context.getNamingStrategy().getColumnName(this)));
this.collectionIdColumnName = Lazy.of(() -> Optionals
.toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)).map(MappedCollection::idColumn),
Optional.ofNullable(findAnnotation(Column.class)).map(Column::value)) //
.filter(StringUtils::hasText).findFirst());
.toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)) //
.map(MappedCollection::idColumn), //
Optional.ofNullable(findAnnotation(Column.class)) //
.map(Column::value)) //
.filter(StringUtils::hasText) //
.findFirst() //
.map(name -> SqlIdentifier.quoted(name).withAdjustableLetterCasing())); //
this.collectionKeyColumnName = Lazy.of(() -> Optionals
.toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)).map(MappedCollection::keyColumn),
this.collectionKeyColumnName = Lazy.of(() -> Optionals //
.toStream(Optional.ofNullable(findAnnotation(MappedCollection.class)).map(MappedCollection::keyColumn), //
Optional.ofNullable(findAnnotation(Column.class)).map(Column::keyColumn)) //
.filter(StringUtils::hasText).findFirst().orElseGet(() -> context.getNamingStrategy().getKeyColumn(this)));
.filter(StringUtils::hasText).findFirst() //
.map(name -> (SqlIdentifier) SqlIdentifier.quoted(name).withAdjustableLetterCasing()) //
.orElseGet(() -> context.getNamingStrategy().getKeyColumn(this)));
}
/*
@@ -128,7 +137,7 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
* @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentProperty#getColumnName()
*/
@Override
public String getColumnName() {
public SimpleSqlIdentifier getColumnName() {
return columnName.get();
}
@@ -178,18 +187,18 @@ public class BasicRelationalPersistentProperty extends AnnotationBasedPersistent
}
@Override
public String getReverseColumnName() {
public SqlIdentifier getReverseColumnName() {
return collectionIdColumnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(this));
}
@Override
public String getReverseColumnName(PersistentPropertyPathExtension path) {
public SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) {
return collectionIdColumnName.get().orElseGet(() -> context.getNamingStrategy().getReverseColumnName(path));
}
@Override
public String getKeyColumn() {
public SqlIdentifier getKeyColumn() {
return isQualified() ? collectionKeyColumnName.get() : null;
}

View File

@@ -18,6 +18,8 @@ package org.springframework.data.relational.core.mapping;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier;
import org.springframework.data.util.Lazy;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
@@ -32,19 +34,19 @@ class CachingNamingStrategy implements NamingStrategy {
private final NamingStrategy delegate;
private final Map<RelationalPersistentProperty, String> columnNames = new ConcurrentHashMap<>();
private final Map<RelationalPersistentProperty, String> keyColumns = new ConcurrentHashMap<>();
private final Map<Class<?>, String> qualifiedTableNames = new ConcurrentReferenceHashMap<>();
private final Map<Class<?>, String> tableNames = new ConcurrentReferenceHashMap<>();
private final Map<RelationalPersistentProperty, SimpleSqlIdentifier> columnNames = new ConcurrentHashMap<>();
private final Map<RelationalPersistentProperty, SqlIdentifier> keyColumns = new ConcurrentHashMap<>();
private final Map<Class<?>, SqlIdentifier> qualifiedTableNames = new ConcurrentReferenceHashMap<>();
private final Map<Class<?>, SqlIdentifier> tableNames = new ConcurrentReferenceHashMap<>();
private final Lazy<String> schema;
private final Lazy<SqlIdentifier> schema;
/**
* Creates a new {@link CachingNamingStrategy} with the given delegate {@link NamingStrategy}.
*
* @param delegate must not be {@literal null}.
*/
public CachingNamingStrategy(NamingStrategy delegate) {
CachingNamingStrategy(NamingStrategy delegate) {
Assert.notNull(delegate, "Delegate must not be null!");
@@ -57,7 +59,7 @@ class CachingNamingStrategy implements NamingStrategy {
* @see org.springframework.data.relational.core.mapping.NamingStrategy#getKeyColumn(org.springframework.data.relational.core.mapping.RelationalPersistentProperty)
*/
@Override
public String getKeyColumn(RelationalPersistentProperty property) {
public SqlIdentifier getKeyColumn(RelationalPersistentProperty property) {
return keyColumns.computeIfAbsent(property, delegate::getKeyColumn);
}
@@ -66,7 +68,7 @@ class CachingNamingStrategy implements NamingStrategy {
* @see org.springframework.data.relational.core.mapping.NamingStrategy#getQualifiedTableName(java.lang.Class)
*/
@Override
public String getQualifiedTableName(Class<?> type) {
public SqlIdentifier getQualifiedTableName(Class<?> type) {
return qualifiedTableNames.computeIfAbsent(type, delegate::getQualifiedTableName);
}
@@ -75,7 +77,7 @@ class CachingNamingStrategy implements NamingStrategy {
* @see org.springframework.data.relational.core.mapping.NamingStrategy#getTableName(java.lang.Class)
*/
@Override
public String getTableName(Class<?> type) {
public SqlIdentifier getTableName(Class<?> type) {
return tableNames.computeIfAbsent(type, delegate::getTableName);
}
@@ -84,7 +86,7 @@ class CachingNamingStrategy implements NamingStrategy {
* @see org.springframework.data.relational.core.mapping.NamingStrategy#getReverseColumnName(org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension)
*/
@Override
public String getReverseColumnName(PersistentPropertyPathExtension path) {
public SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) {
return delegate.getReverseColumnName(path);
}
@@ -93,7 +95,7 @@ class CachingNamingStrategy implements NamingStrategy {
* @see org.springframework.data.relational.core.mapping.NamingStrategy#getReverseColumnName(org.springframework.data.relational.core.mapping.RelationalPersistentProperty)
*/
@Override
public String getReverseColumnName(RelationalPersistentProperty property) {
public SqlIdentifier getReverseColumnName(RelationalPersistentProperty property) {
return delegate.getReverseColumnName(property);
}
@@ -102,7 +104,7 @@ class CachingNamingStrategy implements NamingStrategy {
* @see org.springframework.data.relational.core.mapping.NamingStrategy#getSchema()
*/
@Override
public String getSchema() {
public SqlIdentifier getSchema() {
return schema.get();
}
@@ -111,7 +113,7 @@ class CachingNamingStrategy implements NamingStrategy {
* @see org.springframework.data.relational.core.mapping.NamingStrategy#getColumnName(org.springframework.data.relational.core.mapping.RelationalPersistentProperty)
*/
@Override
public String getColumnName(RelationalPersistentProperty property) {
public SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) {
return columnNames.computeIfAbsent(property, delegate::getColumnName);
}
}

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.data.relational.core.mapping;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.relational.domain.SqlIdentifier.*;
import org.springframework.data.util.ParsingUtils;
import org.springframework.util.Assert;
@@ -45,34 +49,34 @@ public interface NamingStrategy {
*
* @return Empty String representing no schema
*/
default String getSchema() {
return "";
default SqlIdentifier getSchema() {
return SqlIdentifier.EMPTY;
}
/**
* The name of the table to be used for persisting entities having the type passed as an argument. The default
* implementation takes the {@code type.getSimpleName()} and separates camel case parts with '_'.
*/
default String getTableName(Class<?> type) {
default SqlIdentifier getTableName(Class<?> type) {
Assert.notNull(type, "Type must not be null.");
return ParsingUtils.reconcatenateCamelCase(type.getSimpleName(), "_");
return quoted(ParsingUtils.reconcatenateCamelCase(type.getSimpleName(), "_")).withAdjustableLetterCasing();
}
/**
* Defaults to return the given {@link RelationalPersistentProperty}'s name with the parts of a camel case name
* separated by '_';
*/
default String getColumnName(RelationalPersistentProperty property) {
default SimpleSqlIdentifier getColumnName(RelationalPersistentProperty property) {
Assert.notNull(property, "Property must not be null.");
return ParsingUtils.reconcatenateCamelCase(property.getName(), "_");
return quoted(ParsingUtils.reconcatenateCamelCase(property.getName(), "_")).withAdjustableLetterCasing();
}
default String getQualifiedTableName(Class<?> type) {
return this.getSchema() + (this.getSchema().equals("") ? "" : ".") + this.getTableName(type);
default SqlIdentifier getQualifiedTableName(Class<?> type) {
return this.getSchema().concat(this.getTableName(type));
}
/**
@@ -81,14 +85,14 @@ public interface NamingStrategy {
* @param property The property who's column name in the owner table is required
* @return a column name. Must not be {@code null}.
*/
default String getReverseColumnName(RelationalPersistentProperty property) {
default SqlIdentifier getReverseColumnName(RelationalPersistentProperty property) {
Assert.notNull(property, "Property must not be null.");
return property.getOwner().getTableName();
}
default String getReverseColumnName(PersistentPropertyPathExtension path) {
default SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) {
return getTableName(path.getIdDefiningParentPath().getLeafEntity().getType());
}
@@ -99,10 +103,10 @@ public interface NamingStrategy {
*
* @return name of the key column. Must not be {@code null}.
*/
default String getKeyColumn(RelationalPersistentProperty property) {
default SqlIdentifier getKeyColumn(RelationalPersistentProperty property) {
Assert.notNull(property, "Property must not be null.");
return getReverseColumnName(property) + "_key";
return getReverseColumnName(property).suffix("_key");
}
}

View File

@@ -20,6 +20,8 @@ import lombok.EqualsAndHashCode;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier;
import org.springframework.data.util.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -38,7 +40,7 @@ public class PersistentPropertyPathExtension {
private final @Nullable PersistentPropertyPath<RelationalPersistentProperty> path;
private final MappingContext<RelationalPersistentEntity<?>, RelationalPersistentProperty> context;
private final Lazy<String> columnAlias = Lazy.of(() -> prefixWithTableAlias(getColumnName()));
private final Lazy<SqlIdentifier> columnAlias = Lazy.of(() -> prefixWithTableAlias(getColumnName()));
/**
* Creates the empty path referencing the root itself.
@@ -156,10 +158,9 @@ public class PersistentPropertyPathExtension {
*
* @throws IllegalStateException when called on an empty path.
*/
public String getReverseColumnName() {
public SqlIdentifier getReverseColumnName() {
Assert.state(path != null, "Empty paths don't have a reverse column name");
return path.getRequiredLeafProperty().getReverseColumnName(this);
}
@@ -168,7 +169,7 @@ public class PersistentPropertyPathExtension {
*
* @throws IllegalStateException when called on an empty path.
*/
public String getReverseColumnNameAlias() {
public SqlIdentifier getReverseColumnNameAlias() {
return prefixWithTableAlias(getReverseColumnName());
}
@@ -178,7 +179,7 @@ public class PersistentPropertyPathExtension {
*
* @throws IllegalStateException when called on an empty path.
*/
public String getColumnName() {
public SqlIdentifier getColumnName() {
Assert.state(path != null, "Path is null");
@@ -190,7 +191,7 @@ public class PersistentPropertyPathExtension {
*
* @throws IllegalStateException when called on an empty path.
*/
public String getColumnAlias() {
public SqlIdentifier getColumnAlias() {
return columnAlias.get();
}
@@ -228,7 +229,7 @@ public class PersistentPropertyPathExtension {
*
* @return the name of the table. Guaranteed to be not {@literal null}.
*/
public String getTableName() {
public SqlIdentifier getTableName() {
return getTableOwningAncestor().getRequiredLeafEntity().getTableName();
}
@@ -238,7 +239,7 @@ public class PersistentPropertyPathExtension {
* @return a table alias, {@literal null} if the table owning path is the empty path.
*/
@Nullable
public String getTableAlias() {
public SqlIdentifier getTableAlias() {
PersistentPropertyPathExtension tableOwner = getTableOwningAncestor();
@@ -249,7 +250,7 @@ public class PersistentPropertyPathExtension {
/**
* The column name of the id column of the ancestor path that represents an actual table.
*/
public String getIdColumnName() {
public SqlIdentifier getIdColumnName() {
return getTableOwningAncestor().getRequiredLeafEntity().getIdColumn();
}
@@ -257,7 +258,7 @@ public class PersistentPropertyPathExtension {
* If the table owning ancestor has an id the column name of that id property is returned. Otherwise the reverse
* column is returned.
*/
public String getEffectiveIdColumnName() {
public SqlIdentifier getEffectiveIdColumnName() {
PersistentPropertyPathExtension owner = getTableOwningAncestor();
return owner.path == null ? owner.getRequiredLeafEntity().getIdColumn() : owner.getReverseColumnName();
@@ -296,8 +297,8 @@ public class PersistentPropertyPathExtension {
* @return May be {@literal null}.
*/
@Nullable
public String getQualifierColumn() {
return path == null ? "" : path.getRequiredLeafProperty().getKeyColumn();
public SqlIdentifier getQualifierColumn() {
return path == null ? SqlIdentifier.EMPTY : path.getRequiredLeafProperty().getKeyColumn();
}
/**
@@ -384,7 +385,7 @@ public class PersistentPropertyPathExtension {
return isEntity() && !isEmbedded() ? this : getParentPath().getTableOwningAncestor();
}
private String assembleTableAlias() {
private SqlIdentifier assembleTableAlias() {
Assert.state(path != null, "Path is null");
@@ -393,15 +394,15 @@ public class PersistentPropertyPathExtension {
if (path.getLength() == 1) {
Assert.notNull(prefix, "Prefix mus not be null.");
return prefix;
return SqlIdentifier.quoted(prefix).withAdjustableLetterCasing();
}
PersistentPropertyPathExtension parentPath = getParentPath();
return parentPath.isEmbedded() ? parentPath.assembleTableAlias() + prefix
: parentPath.assembleTableAlias() + "_" + prefix;
return parentPath.isEmbedded() ? parentPath.assembleTableAlias().suffix(prefix)
: parentPath.assembleTableAlias().suffix("_" + prefix);
}
private String assembleColumnName(String suffix) {
private SqlIdentifier assembleColumnName(SimpleSqlIdentifier suffix) {
Assert.state(path != null, "Path is null");
@@ -418,17 +419,17 @@ public class PersistentPropertyPathExtension {
String embeddedPrefix = parentLeaf.getEmbeddedPrefix();
return getParentPath().assembleColumnName(embeddedPrefix + suffix);
return getParentPath().assembleColumnName(suffix.prefix(embeddedPrefix));
}
private RelationalPersistentEntity<?> getRequiredLeafEntity() {
return path == null ? entity : context.getRequiredPersistentEntity(path.getRequiredLeafProperty().getActualType());
}
private String prefixWithTableAlias(String columnName) {
private SqlIdentifier prefixWithTableAlias(SqlIdentifier columnName) {
String tableAlias = getTableAlias();
return tableAlias == null ? columnName : tableAlias + "_" + columnName;
SqlIdentifier tableAlias = getTableAlias();
return tableAlias == null ? columnName : columnName.prefix(tableAlias, "_");
}
}

View File

@@ -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<T> extends MutablePersistentEntity<T
*
* @return the table name.
*/
String getTableName();
SqlIdentifier getTableName();
/**
* Returns the column representing the identifier.
*
* @return will never be {@literal null}.
*/
String getIdColumn();
SqlIdentifier getIdColumn();
}

View File

@@ -19,6 +19,7 @@ import java.util.Optional;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.StringUtils;
@@ -34,7 +35,7 @@ class RelationalPersistentEntityImpl<T> extends BasicPersistentEntity<T, Relatio
implements RelationalPersistentEntity<T> {
private final NamingStrategy namingStrategy;
private final Lazy<Optional<String>> tableName;
private final Lazy<Optional<SqlIdentifier>> tableName;
/**
* Creates a new {@link RelationalPersistentEntityImpl} for the given {@link TypeInformation}.
@@ -50,6 +51,7 @@ class RelationalPersistentEntityImpl<T> extends BasicPersistentEntity<T, Relatio
findAnnotation(Table.class)) //
.map(Table::value) //
.filter(StringUtils::hasText) //
.map(name -> SqlIdentifier.quoted(name).withAdjustableLetterCasing()) //
);
}
@@ -58,7 +60,7 @@ class RelationalPersistentEntityImpl<T> extends BasicPersistentEntity<T, Relatio
* @see org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity#getTableName()
*/
@Override
public String getTableName() {
public SqlIdentifier getTableName() {
return tableName.get().orElseGet(() -> namingStrategy.getQualifiedTableName(getType()));
}
@@ -67,7 +69,7 @@ class RelationalPersistentEntityImpl<T> extends BasicPersistentEntity<T, Relatio
* @see org.springframework.data.jdbc.core.mapping.model.JdbcPersistentEntity#getIdColumn()
*/
@Override
public String getIdColumn() {
public SqlIdentifier getIdColumn() {
return getRequiredIdProperty().getColumnName();
}

View File

@@ -16,6 +16,8 @@
package org.springframework.data.relational.core.mapping;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.relational.domain.SqlIdentifier.SimpleSqlIdentifier;
import org.springframework.lang.Nullable;
/**
@@ -34,7 +36,7 @@ public interface RelationalPersistentProperty extends PersistentProperty<Relatio
*
* @return the name of the column backing this property.
*/
String getColumnName();
SimpleSqlIdentifier getColumnName();
/**
* The type to be used to store this property in the database. Multidimensional arrays are unwrapped to reflect a
@@ -61,14 +63,14 @@ public interface RelationalPersistentProperty extends PersistentProperty<Relatio
* @deprecated Use {@link #getReverseColumnName(PersistentPropertyPathExtension)} instead.
*/
@Deprecated
String getReverseColumnName();
SqlIdentifier getReverseColumnName();
default String getReverseColumnName(PersistentPropertyPathExtension path) {
default SqlIdentifier getReverseColumnName(PersistentPropertyPathExtension path) {
return getReverseColumnName();
}
@Nullable
String getKeyColumn();
SqlIdentifier getKeyColumn();
/**
* Returns if this property is a qualified property, i.e. a property referencing multiple elements that can get picked

View File

@@ -69,9 +69,9 @@ public final class Identifier {
* @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 final class Identifier {
* @param map must not be {@literal null}.
* @return the {@link Identifier} from a {@link Map} of name to value tuples.
*/
public static Identifier from(Map<String, Object> map) {
public static Identifier from(Map<SqlIdentifier, Object> map) {
Assert.notNull(map, "Map must not be null!");
@@ -111,9 +111,9 @@ public final class Identifier {
* @return the {@link Identifier} containing all existing keys and the key part for {@code name}, {@code value}, and a
* {@link Class target type}.
*/
public Identifier withPart(String name, Object value, Class<?> targetType) {
public Identifier withPart(SqlIdentifier name, Object value, Class<?> targetType) {
Assert.hasText(name, "Name must not be empty!");
Assert.notNull(name, "Name must not be null!");
Assert.notNull(targetType, "Target type must not be null!");
boolean overwritten = false;
@@ -141,9 +141,9 @@ public final class Identifier {
*
* @return a {@link Map} containing the identifier name to value tuples.
*/
public Map<String, Object> toMap() {
public Map<SqlIdentifier, Object> toMap() {
Map<String, Object> result = new LinkedHashMap<>();
Map<SqlIdentifier, Object> result = new LinkedHashMap<>();
forEach((name, value, type) -> result.put(name, value));
return result;
}
@@ -188,7 +188,7 @@ public final class Identifier {
@AllArgsConstructor(access = AccessLevel.PRIVATE)
static class SingleIdentifierValue {
String name;
SqlIdentifier name;
Object value;
Class<?> targetType;
}
@@ -204,11 +204,11 @@ public final class Identifier {
/**
* Performs this operation on the given arguments.
*
*
* @param name
* @param value
* @param targetType
*/
void accept(String name, Object value, Class<?> targetType);
void accept(SqlIdentifier name, Object value, Class<?> targetType);
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.relational.domain;
/**
* An interface describing the processing steps for the conversion of {@link SqlIdentifier} to SQL snippets or column
* names.
*
* @author Jens Schauder
* @since 2.0
*/
public interface IdentifierProcessing {
/**
* An {@link IdentifierProcessing} that can be used for databases adhering to the SQL standard which uses double
* quotes ({@literal "}) for quoting and makes unquoted literals equivalent to upper case.
*/
IdentifierProcessing ANSI = new DefaultIdentifierProcessing(Quoting.ANSI, LetterCasing.UPPER_CASE);
/**
* Converts a {@link String} representing a bare name of an identifier to a {@link String} with proper quoting
* applied.
*
* @param identifier the name of an identifier. Must not be {@literal null}.
* @return a quoted name of an identifier. Guaranteed to be not {@literal null}.
*/
String quote(String identifier);
/**
* Standardizes the use of upper and lower case letters in an identifier in such a way that semantically the same
* identifier results from the quoted and the unquoted version. If this is not possible use of
* {@link LetterCasing#AS_IS} is recommended.
*
* @param identifier an identifier with arbitrary upper and lower cases. must not be {@literal null}.
* @return an identifier with standardized use of upper and lower case letter. Guaranteed to be not {@literal null}.
*/
String standardizeLetterCase(String identifier);
/**
* An {@link IdentifierProcessing} implementation based on two implementations for the quoting and for the letter case
* standardization.
*/
class DefaultIdentifierProcessing implements IdentifierProcessing {
private final Quoting quoting;
private final LetterCasing letterCasing;
public DefaultIdentifierProcessing(Quoting quoting, LetterCasing letterCasing) {
this.quoting = quoting;
this.letterCasing = letterCasing;
}
@Override
public String quote(String identifier) {
return quoting.apply(identifier);
}
@Override
public String standardizeLetterCase(String identifier) {
return letterCasing.apply(identifier);
}
}
/**
* A conversion from unquoted identifiers to quoted identifiers.
*
* @author Jens Schauder
* @since 2.0
*/
class Quoting {
public static final Quoting ANSI = new Quoting("\"");
private final String prefix;
private final String suffix;
/**
* Constructs a {@literal Quoting} with potential different prefix and suffix used for quoting.
*
* @param prefix a {@literal String} prefixed before the name for quoting it.
* @param suffix a {@literal String} suffixed at the end of the name for quoting it.
*/
public Quoting(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
/**
* Constructs a {@literal Quoting} with the same {@literal String} appended in front and end of an identifier.
*
* @param quoteCharacter the value appended at the beginning and the end of a name in order to quote it.
*/
public Quoting(String quoteCharacter) {
this(quoteCharacter, quoteCharacter);
}
public String apply(String identifier) {
return prefix + identifier + suffix;
}
}
/**
* Encapsulates the three kinds of letter casing supported.
*
* @author Jens Schauder
* @since 2.0
*/
enum LetterCasing {
UPPER_CASE {
@Override
public String apply(String identifier) {
return identifier.toUpperCase();
}
},
LOWER_CASE {
@Override
public String apply(String identifier) {
return identifier.toLowerCase();
}
},
AS_IS {
@Override
public String apply(String identifier) {
return identifier;
}
};
abstract String apply(String identifier);
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.relational.domain;
import java.util.Arrays;
import java.util.Objects;
import java.util.StringJoiner;
import org.springframework.util.Assert;
/**
* Represents a named object that exists in the database like a table name or a column name
*
* @author Jens Schauder
* @since 2.0
*/
public interface SqlIdentifier {
SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator);
SqlIdentifier suffix(String suffix);
SqlIdentifier concat(SqlIdentifier second);
String toSql(IdentifierProcessing processing);
String toColumnName(IdentifierProcessing processing);
static SimpleSqlIdentifier quoted(String name) {
return new SimpleSqlIdentifier(name, true, true);
}
static SimpleSqlIdentifier unquoted(String name) {
return new SimpleSqlIdentifier(name, false, true);
}
SqlIdentifier EMPTY = new SqlIdentifier() {
@Override
public SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator) {
throw new UnsupportedOperationException("We can't prefix an empty DatabaseObjectIdentifier");
}
@Override
public SqlIdentifier suffix(String suffix) {
throw new UnsupportedOperationException("We can't suffix an empty DatabaseObjectIdentifier");
}
@Override
public SqlIdentifier concat(SqlIdentifier second) {
return second;
}
@Override
public String toSql(IdentifierProcessing processing) {
throw new UnsupportedOperationException("An empty SqlIdentifier can't be used in to create SQL snippets");
}
@Override
public String toColumnName(IdentifierProcessing processing) {
throw new UnsupportedOperationException("An empty SqlIdentifier can't be used in to create column names");
}
public String toString() {
return "<NULL-IDENTIFIER>";
}
};
final class SimpleSqlIdentifier implements SqlIdentifier {
private final String name;
private final boolean quoted;
private final boolean fixedLetterCasing;
private SimpleSqlIdentifier(String name, boolean quoted, boolean fixedLetterCasing) {
Assert.hasText(name, "A database object must have at least on name part.");
this.name = name;
this.quoted = quoted;
this.fixedLetterCasing = fixedLetterCasing;
}
public SimpleSqlIdentifier withAdjustableLetterCasing() {
return new SimpleSqlIdentifier(name, quoted, false);
}
public SimpleSqlIdentifier prefix(String prefix) {
return new SimpleSqlIdentifier(prefix + name, quoted, fixedLetterCasing);
}
@Override
public SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator) {
Assert.isInstanceOf(SimpleSqlIdentifier.class, prefix, "Prefixing is only supported for simple SqlIdentifier");
return new SimpleSqlIdentifier(((SimpleSqlIdentifier) prefix).name + separator + name, quoted, fixedLetterCasing);
}
public SimpleSqlIdentifier suffix(String suffix) {
return new SimpleSqlIdentifier(name + suffix, quoted, fixedLetterCasing);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SimpleSqlIdentifier that = (SimpleSqlIdentifier) o;
return quoted == that.quoted && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
int result = Objects.hash(quoted);
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString() {
return "DatabaseObjectIdentifier{" + "name=" + name + ", quoted=" + quoted + '}';
}
@Override
public SqlIdentifier concat(SqlIdentifier second) {
// TODO: this is completely broken and needs fixing.
return new CombinedSqlIdentifier(this, (SimpleSqlIdentifier) second);
}
@Override
public String toSql(IdentifierProcessing processing) {
return quoted ? processing.quote(toColumnName(processing)) : toColumnName(processing);
}
@Override
public String toColumnName(IdentifierProcessing processing) {
return fixedLetterCasing ? name : processing.standardizeLetterCase(name);
}
}
final class CombinedSqlIdentifier implements SqlIdentifier {
private final SimpleSqlIdentifier[] parts;
private CombinedSqlIdentifier(SimpleSqlIdentifier... parts) {
this.parts = parts;
}
@Override
public SqlIdentifier concat(SqlIdentifier second) {
throw new UnsupportedOperationException();
}
@Override
public SimpleSqlIdentifier prefix(SqlIdentifier prefix, String separator) {
throw new UnsupportedOperationException();
}
@Override
public SqlIdentifier suffix(String suffix) {
throw new UnsupportedOperationException();
}
@Override
public String toSql(IdentifierProcessing processing) {
StringJoiner stringJoiner = new StringJoiner(".");
for (SimpleSqlIdentifier namePart : parts) {
stringJoiner.add(namePart.toSql(processing));
}
return stringJoiner.toString();
}
@Override
public String toColumnName(IdentifierProcessing processing) {
throw new UnsupportedOperationException("A CombinedSqlIdentifier can't be used as a column name");
}
@Override
public String toString() {
return "CombinedDatabaseObjectIdentifier{" + "parts=" + Arrays.toString(parts) + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CombinedSqlIdentifier that = (CombinedSqlIdentifier) o;
return Arrays.equals(parts, that.parts);
}
@Override
public int hashCode() {
return Arrays.hashCode(parts);
}
}
}

View File

@@ -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<T, ID> extends EntityInformation<T,
*
* @return
*/
String getTableName();
SqlIdentifier getTableName();
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.relational.repository.query;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.data.repository.core.EntityMetadata;
/**
@@ -30,7 +31,7 @@ public interface RelationalEntityMetadata<T> extends EntityMetadata<T> {
*
* @return
*/
String getTableName();
SqlIdentifier getTableName();
/**
* Returns the {@link RelationalPersistentEntity} that supposed to determine the table to be queried.

View File

@@ -18,6 +18,7 @@ package org.springframework.data.relational.repository.query;
import lombok.Getter;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.domain.SqlIdentifier;
import org.springframework.util.Assert;
/**
@@ -56,7 +57,7 @@ public class SimpleRelationalEntityMetadata<T> implements RelationalEntityMetada
/* (non-Javadoc)
* @see org.springframework.data.relational.repository.query.RelationalEntityMetadata#getTableName()
*/
public String getTableName() {
public SqlIdentifier getTableName() {
return tableEntity.getTableName();
}
}

View File

@@ -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<T, ID> extends PersistentEntityI
implements RelationalEntityInformation<T, ID> {
private final RelationalPersistentEntity<T> entityMetadata;
private final @Nullable String customTableName;
private final @Nullable SqlIdentifier customTableName;
private final Class<ID> fallbackIdType;
/**
@@ -81,14 +82,14 @@ public class MappingRelationalEntityInformation<T, ID> extends PersistentEntityI
super(entity);
this.entityMetadata = entity;
this.customTableName = customTableName;
this.customTableName = customTableName == null ? null : SqlIdentifier.quoted(customTableName);
this.fallbackIdType = idType != null ? idType : (Class<ID>) 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;
}

View File

@@ -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\"");
}
}

View File

@@ -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`");
}
}

View File

@@ -59,4 +59,12 @@ public class MySqlDialectUnitTests {
assertThat(limit.getLimitOffset(20, 10)).isEqualTo("LIMIT 10, 20");
}
@Test // DATAJDBC-386
public void shouldQuoteIdentifiersUsingBackticks() {
String abcQuoted = MySqlDialect.INSTANCE.getIdentifierProcessing().quote("abc");
assertThat(abcQuoted).isEqualTo("`abc`");
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.relational.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import lombok.Data;
@@ -82,9 +83,9 @@ public class BasicRelationalPersistentPropertyUnitTests {
@Test // DATAJDBC-106
public void detectsAnnotatedColumnName() {
assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo("dummy_name");
assertThat(entity.getRequiredPersistentProperty("name").getColumnName()).isEqualTo(quoted("dummy_name"));
assertThat(entity.getRequiredPersistentProperty("localDateTime").getColumnName())
.isEqualTo("dummy_last_updated_at");
.isEqualTo(quoted("dummy_last_updated_at"));
}
@Test // DATAJDBC-218
@@ -92,8 +93,8 @@ public class BasicRelationalPersistentPropertyUnitTests {
RelationalPersistentProperty listProperty = entity.getRequiredPersistentProperty("someList");
assertThat(listProperty.getReverseColumnName()).isEqualTo("dummy_column_name");
assertThat(listProperty.getKeyColumn()).isEqualTo("dummy_key_column_name");
assertThat(listProperty.getReverseColumnName()).isEqualTo(quoted("dummy_column_name"));
assertThat(listProperty.getKeyColumn()).isEqualTo(quoted("dummy_key_column_name"));
}
@Test // DATAJDBC-111
@@ -148,10 +149,10 @@ public class BasicRelationalPersistentPropertyUnitTests {
.assertThat(p.isCollectionLike() && !p.isEntity()).describedAs(s + " contains either simple types or entities")
.isNotEqualTo(p.isCollectionLike() && p.isEntity());
checkEitherOr.accept(listOfString,"listOfString");
checkEitherOr.accept(arrayOfString,"arrayOfString");
checkEitherOr.accept(listOfEntity,"listOfEntity");
checkEitherOr.accept(arrayOfEntity,"arrayOfEntity");
checkEitherOr.accept(listOfString, "listOfString");
checkEitherOr.accept(arrayOfString, "arrayOfString");
checkEitherOr.accept(listOfEntity, "listOfEntity");
checkEitherOr.accept(arrayOfEntity, "arrayOfEntity");
softly.assertThat(arrayOfString.getColumnType()).isEqualTo(String[].class);
softly.assertThat(listOfString.getColumnType()).isEqualTo(String[].class);
@@ -183,7 +184,8 @@ public class BasicRelationalPersistentPropertyUnitTests {
private final List<OtherEntity> listOfEntity;
private final OtherEntity[] arrayOfEntity;
@MappedCollection(idColumn = "dummy_column_name", keyColumn = "dummy_key_column_name") private List<Integer> someList;
@MappedCollection(idColumn = "dummy_column_name",
keyColumn = "dummy_key_column_name") private List<Integer> someList;
// DATACMNS-106
private @Column("dummy_name") String name;

View File

@@ -16,6 +16,7 @@
package org.springframework.data.relational.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import java.time.LocalDateTime;
import java.util.List;
@@ -23,6 +24,7 @@ import java.util.List;
import org.junit.Test;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntityImplUnitTests.DummySubEntity;
import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Unit tests for the {@link NamingStrategy}.
@@ -40,51 +42,53 @@ public class NamingStrategyUnitTests {
@Test
public void getTableName() {
assertThat(target.getTableName(persistentEntity.getType())).isEqualTo("dummy_entity");
assertThat(target.getTableName(DummySubEntity.class)).isEqualTo("dummy_sub_entity");
assertThat(target.getTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity"));
assertThat(target.getTableName(DummySubEntity.class)).isEqualTo(quoted("dummy_sub_entity"));
}
@Test
public void getColumnName() {
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("id"))).isEqualTo("id");
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("createdAt"))).isEqualTo("created_at");
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("id"))).isEqualTo(quoted("id"));
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("createdAt")))
.isEqualTo(quoted("created_at"));
assertThat(target.getColumnName(persistentEntity.getPersistentProperty("dummySubEntities")))
.isEqualTo("dummy_sub_entities");
.isEqualTo(quoted("dummy_sub_entities"));
}
@Test
public void getReverseColumnName() {
assertThat(target.getReverseColumnName(persistentEntity.getPersistentProperty("dummySubEntities")))
.isEqualTo("dummy_entity");
.isEqualTo(quoted("dummy_entity"));
}
@Test
public void getKeyColumn() {
assertThat(target.getKeyColumn(persistentEntity.getPersistentProperty("dummySubEntities")))
.isEqualTo("dummy_entity_key");
.isEqualTo(quoted("dummy_entity_key"));
}
@Test
public void getSchema() {
assertThat(target.getSchema()).isEmpty();
assertThat(target.getSchema()).isEqualTo(SqlIdentifier.EMPTY);
}
@Test
public void getQualifiedTableName() {
assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo("dummy_entity");
assertThat(target.getQualifiedTableName(persistentEntity.getType())).isEqualTo(quoted("dummy_entity"));
NamingStrategy strategy = new NamingStrategy() {
@Override
public String getSchema() {
return "schema";
public SqlIdentifier getSchema() {
return quoted("schema");
}
};
assertThat(strategy.getQualifiedTableName(persistentEntity.getType())).isEqualTo("schema.dummy_entity");
assertThat(strategy.getQualifiedTableName(persistentEntity.getType()))
.isEqualTo(quoted("schema").concat(quoted("dummy_entity")));
}
static class DummyEntity {

View File

@@ -16,6 +16,7 @@
package org.springframework.data.relational.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import org.junit.Test;
import org.springframework.data.annotation.Id;
@@ -36,7 +37,7 @@ public class RelationalPersistentEntityImplUnitTests {
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(DummySubEntity.class);
assertThat(entity.getTableName()).isEqualTo("dummy_sub_entity");
assertThat(entity.getTableName()).isEqualTo(quoted("dummy_sub_entity"));
}
@Test // DATAJDBC-294
@@ -44,7 +45,7 @@ public class RelationalPersistentEntityImplUnitTests {
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(DummySubEntity.class);
assertThat(entity.getIdColumn()).isEqualTo("renamedId");
assertThat(entity.getIdColumn()).isEqualTo(quoted("renamedId"));
}
@Test // DATAJDBC-296
@@ -52,7 +53,7 @@ public class RelationalPersistentEntityImplUnitTests {
RelationalPersistentEntity<?> entity = mappingContext.getPersistentEntity(DummyEntityWithEmptyAnnotation.class);
assertThat(entity.getTableName()).isEqualTo("dummy_entity_with_empty_annotation");
assertThat(entity.getTableName()).isEqualTo(quoted("dummy_entity_with_empty_annotation"));
}
@Table("dummy_sub_entity")

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.relational.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.relational.domain.SqlIdentifier.*;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.data.relational.domain.IdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.domain.IdentifierProcessing.Quoting;
import org.springframework.data.relational.domain.SqlIdentifier;
/**
* Unit tests for SqlIdentifier.
*
* @author Jens Schauder
*/
public class SqlIdentifierUnitTests {
public static final DefaultIdentifierProcessing BRACKETS_LOWER_CASE = new DefaultIdentifierProcessing(
new Quoting("[", "]"), LetterCasing.LOWER_CASE);
@Test // DATAJDBC-386
public void quotedSimpleObjectIdentifier() {
SimpleSqlIdentifier identifier = quoted("someName");
assertThat(identifier.toSql(BRACKETS_LOWER_CASE)).isEqualTo("[someName]");
assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("someName");
}
@Test // DATAJDBC-386
public void unquotedSimpleObjectIdentifier() {
SimpleSqlIdentifier identifier = unquoted("someName");
String sql = identifier.toSql(BRACKETS_LOWER_CASE);
assertThat(sql).isEqualTo("someName");
assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("someName");
}
@Test // DATAJDBC-386
public void quotedSimpleObjectIdentifierWithAdjustableLetterCasing() {
SimpleSqlIdentifier identifier = quoted("someName").withAdjustableLetterCasing();
assertThat(identifier.toSql(BRACKETS_LOWER_CASE)).isEqualTo("[somename]");
assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("somename");
}
@Test // DATAJDBC-386
public void unquotedSimpleObjectIdentifierWithAdjustableLetterCasing() {
SimpleSqlIdentifier identifier = unquoted("someName").withAdjustableLetterCasing();
String sql = identifier.toSql(BRACKETS_LOWER_CASE);
assertThat(sql).isEqualTo("somename");
assertThat(identifier.toColumnName(BRACKETS_LOWER_CASE)).isEqualTo("somename");
}
@Test // DATAJDBC-386
public void quotedMultipartObjectIdentifierWithAdjustableLetterCase() {
SqlIdentifier identifier = quoted("some").withAdjustableLetterCasing()
.concat(quoted("name").withAdjustableLetterCasing());
String sql = identifier.toSql(IdentifierProcessing.ANSI);
assertThat(sql).isEqualTo("\"SOME\".\"NAME\"");
}
@Test // DATAJDBC-386
public void quotedMultipartObjectIdentifier() {
SqlIdentifier identifier = quoted("some").concat(quoted("name"));
String sql = identifier.toSql(IdentifierProcessing.ANSI);
assertThat(sql).isEqualTo("\"some\".\"name\"");
}
@Test // DATAJDBC-386
public void unquotedMultipartObjectIdentifier() {
SqlIdentifier identifier = unquoted("some").concat(unquoted("name"));
String sql = identifier.toSql(IdentifierProcessing.ANSI);
assertThat(sql).isEqualTo("some.name");
}
@Test // DATAJDBC-386
public void equality() {
SqlIdentifier basis = SqlIdentifier.unquoted("simple");
SqlIdentifier equal = SqlIdentifier.unquoted("simple");
SqlIdentifier quoted = quoted("simple");
SqlIdentifier notSimple = SqlIdentifier.unquoted("simple").concat(unquoted("not"));
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(basis).isEqualTo(equal);
softly.assertThat(equal).isEqualTo(basis);
softly.assertThat(basis).isNotEqualTo(quoted);
softly.assertThat(basis).isNotEqualTo(notSimple);
});
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.relational.domain;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.data.relational.domain.IdentifierProcessing.DefaultIdentifierProcessing;
import org.springframework.data.relational.domain.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.domain.IdentifierProcessing.Quoting;
/**
* unit tests for {@link DefaultIdentifierProcessing}.
*
* @author Jens Schauder
*/
public class DefaultIdentifierProcessingUnitTests {
@Test // DATAJDBC-386
public void ansiConformProcessing() {
DefaultIdentifierProcessing processing = new DefaultIdentifierProcessing(Quoting.ANSI, LetterCasing.UPPER_CASE);
assertThat(processing.quote("something")).isEqualTo("\"something\"");
assertThat(processing.standardizeLetterCase("aBc")).isEqualTo("ABC");
}
@Test // DATAJDBC-386
public void twoCharacterAsIs() {
DefaultIdentifierProcessing processing = new DefaultIdentifierProcessing(new Quoting("[", "]"), LetterCasing.AS_IS);
assertThat(processing.quote("something")).isEqualTo("[something]");
assertThat(processing.standardizeLetterCase("aBc")).isEqualTo("aBc");
}
}

View File

@@ -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<String, Object> parameters = new HashMap<>();
parameters.put("one", null);
HashMap<SqlIdentifier, Object> parameters = new HashMap<>();
parameters.put(unquoted("one"), null);
Identifier identifier = Identifier.from(parameters);
assertThat(identifier.getParts()) //
.extracting("name", "value", "targetType") //
.containsExactly( //
tuple("one", null, Object.class) //
tuple(unquoted("one"), null, Object.class) //
);
}
@Test // DATAJDBC-326
public void createsIdentifierFromMap() {
Identifier identifier = Identifier.from(Collections.singletonMap("aName", "aValue"));
Identifier identifier = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue"));
assertThat(identifier.toMap()).hasSize(1).containsEntry("aName", "aValue");
assertThat(identifier.toMap()).hasSize(1).containsEntry(unquoted("aName"), "aValue");
}
@Test // DATAJDBC-326
public void withAddsNewEntries() {
Identifier identifier = Identifier.from(Collections.singletonMap("aName", "aValue")).withPart("foo", "bar",
String.class);
Identifier identifier = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue"))
.withPart(unquoted("foo"), "bar", String.class);
assertThat(identifier.toMap()).hasSize(2).containsEntry("aName", "aValue").containsEntry("foo", "bar");
assertThat(identifier.toMap()) //
.hasSize(2) //
.containsEntry(unquoted("aName"), "aValue") //
.containsEntry(unquoted("foo"), "bar");
}
@Test // DATAJDBC-326
public void withOverridesExistingEntries() {
Identifier identifier = Identifier.from(Collections.singletonMap("aName", "aValue")).withPart("aName", "bar",
String.class);
Identifier identifier = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue"))
.withPart(unquoted("aName"), "bar", String.class);
assertThat(identifier.toMap()).hasSize(1).containsEntry("aName", "bar");
assertThat(identifier.toMap()) //
.hasSize(1) //
.containsEntry(unquoted("aName"), "bar");
}
@Test // DATAJDBC-326
@@ -86,7 +92,8 @@ public class IdentifierUnitTests {
List<String> keys = new ArrayList<>();
Identifier.from(Collections.singletonMap("aName", "aValue")).forEach((name, value, targetType) -> keys.add(name));
Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue"))
.forEach((name, value, targetType) -> keys.add(name.toSql(IdentifierProcessing.ANSI)));
assertThat(keys).containsOnly("aName");
}
@@ -94,9 +101,9 @@ public class IdentifierUnitTests {
@Test // DATAJDBC-326
public void equalsConsidersEquality() {
Identifier one = Identifier.from(Collections.singletonMap("aName", "aValue"));
Identifier two = Identifier.from(Collections.singletonMap("aName", "aValue"));
Identifier three = Identifier.from(Collections.singletonMap("aName", "different"));
Identifier one = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue"));
Identifier two = Identifier.from(Collections.singletonMap(unquoted("aName"), "aValue"));
Identifier three = Identifier.from(Collections.singletonMap(unquoted("aName"), "different"));
assertThat(one).isEqualTo(two);
assertThat(one).isNotEqualTo(three);