Skip to content

DATAJDBC-326 - Support conversion of backreferences. #118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-326-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data Relational Parent</name>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-jdbc-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-326-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions spring-data-jdbc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>spring-data-jdbc</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-326-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand All @@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-relational-parent</artifactId>
<version>1.1.0.BUILD-SNAPSHOT</version>
<version>1.1.0.DATAJDBC-326-SNAPSHOT</version>
</parent>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.function.Consumer;
import java.util.function.Function;

import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;

Expand All @@ -47,6 +48,15 @@ public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> ad
return collect(das -> das.insert(instance, domainType, additionalParameters));
}

/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, org.springframework.data.jdbc.core.ParentKeys)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
return collect(das -> das.insert(instance, domainType, identifier));
}

/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#update(java.lang.Object, java.lang.Class)
Expand Down Expand Up @@ -149,7 +159,7 @@ public <T> boolean existsById(Object id, Class<T> domainType) {
private <T> T collect(Function<DataAccessStrategy, T> function) {

// Keep <T> as Eclipse fails to compile if <> is used.
return strategies.stream().collect(new FunctionCollector<T>(function));
return strategies.stream().collect(new FunctionCollector<>(function));
}

private void collectVoid(Consumer<DataAccessStrategy> consumer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.lang.Nullable;

/**
Expand All @@ -39,9 +40,27 @@ public interface DataAccessStrategy {
* to get referenced are contained in this map. Must not be {@code null}.
* @param <T> the type of the instance.
* @return the id generated by the database if any.
* @deprecated since 1.1, use {@link #insert(Object, Class, Identifier)} instead.
*/
@Deprecated
<T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters);

/**
* Inserts a the data of a single entity. Referenced entities don't get handled.
*
* @param instance the instance to be stored. Must not be {@code null}.
* @param domainType the type of the instance. Must not be {@code null}.
* @param identifier information about data that needs to be considered for the insert but which is not part of the
* entity. Namely references back to a parent entity and key/index columns for entities that are stored in a
* {@link Map} or {@link java.util.List}.
* @param <T> the type of the instance.
* @return the id generated by the database if any.
* @since 1.1
*/
default <T> Object insert(T instance, Class<T> domainType, Identifier identifier){
return insert(instance, domainType, identifier.toMap());
}

/**
* Updates the data of a single entity in the database. Referenced entities don't get handled.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,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.util.ClassTypeInformation;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
Expand All @@ -57,10 +58,6 @@
@RequiredArgsConstructor
public class DefaultDataAccessStrategy implements DataAccessStrategy {

private static final String ENTITY_NEW_AFTER_INSERT = "Entity [%s] still 'new' after insert. Please set either"
+ " the id property in a BeforeInsert event handler, or ensure the database creates a value and your "
+ "JDBC driver returns it.";

private final @NonNull SqlGeneratorSource sqlGeneratorSource;
private final @NonNull RelationalMappingContext context;
private final @NonNull RelationalConverter converter;
Expand All @@ -87,10 +84,23 @@ public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, Relation
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
return insert(instance, domainType, Identifier.from(additionalParameters));
}

/*
* (non-Javadoc)
* @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, Identifier identifier) {

KeyHolder holder = new GeneratedKeyHolder();
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
Map<String, Object> parameters = new LinkedHashMap<>(additionalParameters);

Map<String, Object> parameters = new LinkedHashMap<>(identifier.size());
identifier.forEach((name, value, type) -> {
parameters.put(name, converter.writeValue(value, ClassTypeInformation.from(type)));
});

MapSqlParameterSource parameterSource = getPropertyMap(instance, persistentEntity, "");

Expand Down Expand Up @@ -282,7 +292,8 @@ public <T> boolean existsById(Object id, Class<T> domainType) {
return result;
}

private <S, T> MapSqlParameterSource getPropertyMap(final S instance, RelationalPersistentEntity<S> persistentEntity, String prefix) {
private <S, T> MapSqlParameterSource getPropertyMap(S instance, RelationalPersistentEntity<S> persistentEntity,
String prefix) {

MapSqlParameterSource parameters = new MapSqlParameterSource();

Expand All @@ -294,23 +305,26 @@ private <S, T> MapSqlParameterSource getPropertyMap(final S instance, Relational
return;
}

if(property.isEmbedded()){
if (property.isEmbedded()) {

Object value = propertyAccessor.getProperty(property);
final RelationalPersistentEntity<?> embeddedEntity = context.getPersistentEntity(property.getType());
final MapSqlParameterSource additionalParameters = getPropertyMap((T)value, (RelationalPersistentEntity<T>) embeddedEntity, prefix + property.getEmbeddedPrefix());
RelationalPersistentEntity<?> embeddedEntity = context.getPersistentEntity(property.getType());
MapSqlParameterSource additionalParameters = getPropertyMap((T) value,
(RelationalPersistentEntity<T>) embeddedEntity, prefix + property.getEmbeddedPrefix());
parameters.addValues(additionalParameters.getValues());
} else {

Object value = propertyAccessor.getProperty(property);
Object convertedValue = convertForWrite(property, value);

parameters.addValue(prefix + property.getColumnName(), convertedValue, JdbcUtil.sqlTypeFor(property.getColumnType()));
parameters.addValue(prefix + property.getColumnName(), convertedValue,
JdbcUtil.sqlTypeFor(property.getColumnType()));
}
});

return parameters;
}

@Nullable
private Object convertForWrite(RelationalPersistentProperty property, @Nullable Object value) {

Expand All @@ -327,9 +341,8 @@ private Object convertForWrite(RelationalPersistentProperty property, @Nullable

String typeName = JDBCType.valueOf(JdbcUtil.sqlTypeFor(componentType)).getName();

return operations.getJdbcOperations().execute(
(Connection c) -> c.createArrayOf(typeName, (Object[]) convertedValue)
);
return operations.getJdbcOperations()
.execute((Connection c) -> c.createArrayOf(typeName, (Object[]) convertedValue));
}

@SuppressWarnings("unchecked")
Expand All @@ -354,22 +367,22 @@ private static <S, ID> boolean isIdPropertyNullOrScalarZero(@Nullable ID idValue
@Nullable
private <S> Object getIdFromHolder(KeyHolder holder, RelationalPersistentEntity<S> persistentEntity) {

try {
// MySQL just returns one value with a special name
return holder.getKey();
} catch (DataRetrievalFailureException | InvalidDataAccessApiUsageException e) {
// Postgres returns a value for each column
try {
// MySQL just returns one value with a special name
return holder.getKey();
} catch (DataRetrievalFailureException | InvalidDataAccessApiUsageException e) {
// Postgres returns a value for each column
// MS SQL Server returns a value that might be null.

Map<String, Object> keys = holder.getKeys();
Map<String, Object> keys = holder.getKeys();

if (keys == null || persistentEntity.getIdProperty() == null) {
return null;
}
if (keys == null || persistentEntity.getIdProperty() == null) {
return null;
}

return keys.get(persistentEntity.getIdColumn());
}
}
return keys.get(persistentEntity.getIdColumn());
}
}

private EntityRowMapper<?> getEntityRowMapper(Class<?> domainType) {
return new EntityRowMapper<>(getRequiredPersistentEntity(domainType), context, converter, accessStrategy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
import lombok.RequiredArgsConstructor;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.data.jdbc.core.convert.JdbcIdentifierBuilder;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.conversion.DbAction;
import org.springframework.data.relational.core.conversion.DbAction.Delete;
Expand All @@ -36,6 +36,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.lang.Nullable;

/**
Expand All @@ -58,7 +59,7 @@ class DefaultJdbcInterpreter implements Interpreter {
@Override
public <T> void interpret(Insert<T> insert) {

Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), createAdditionalColumnValues(insert));
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), getParentKeys(insert));

insert.setGeneratedId(id);
}
Expand Down Expand Up @@ -101,7 +102,7 @@ public <T> void interpret(Merge<T> merge) {

// temporary implementation
if (!accessStrategy.update(merge.getEntity(), merge.getEntityType())) {
accessStrategy.insert(merge.getEntity(), merge.getEntityType(), createAdditionalColumnValues(merge));
accessStrategy.insert(merge.getEntity(), merge.getEntityType(), getParentKeys(merge));
}
}

Expand Down Expand Up @@ -141,27 +142,22 @@ public <T> void interpret(DeleteAllRoot<T> deleteAllRoot) {
accessStrategy.deleteAll(deleteAllRoot.getEntityType());
}

private <T> Map<String, Object> createAdditionalColumnValues(DbAction.WithDependingOn<T> action) {

Map<String, Object> additionalColumnValues = new HashMap<>();
addDependingOnInformation(action, additionalColumnValues);
additionalColumnValues.putAll(action.getAdditionalValues());

return additionalColumnValues;
}

private <T> void addDependingOnInformation(DbAction.WithDependingOn<T> action,
Map<String, Object> additionalColumnValues) {
private Identifier getParentKeys(DbAction.WithDependingOn<?> action) {

DbAction.WithEntity<?> dependingOn = action.getDependingOn();

RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(dependingOn.getEntityType());

String columnName = getColumnNameForReverseColumn(action);
Object id = getIdFromEntityDependingOn(dependingOn, persistentEntity);
JdbcIdentifierBuilder identifier = JdbcIdentifierBuilder //
.forBackReferences(action.getPropertyPath(), id);

Object identifier = getIdFromEntityDependingOn(dependingOn, persistentEntity);
for (Map.Entry<PersistentPropertyPath<RelationalPersistentProperty>, Object> qualifier : action.getQualifiers()
.entrySet()) {
identifier = identifier.withQualifier(qualifier.getKey(), qualifier.getValue());
}

additionalColumnValues.put(columnName, identifier);
return identifier.build();
}

@Nullable
Expand All @@ -182,9 +178,4 @@ private Object getIdFromEntityDependingOn(DbAction.WithEntity<?> dependingOn,
return persistentEntity.getIdentifierAccessor(entity).getIdentifier();
}

private String getColumnNameForReverseColumn(DbAction.WithPropertyPath<?> action) {

PersistentPropertyPath<RelationalPersistentProperty> path = action.getPropertyPath();
return path.getRequiredLeafProperty().getReverseColumnName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Map;

import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.util.Assert;
Expand All @@ -40,6 +41,15 @@ public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> ad
return delegate.insert(instance, domainType, additionalParameters);
}

/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, org.springframework.data.jdbc.core.ParentKeys)
*/
@Override
public <T> Object insert(T instance, Class<T> domainType, Identifier identifier) {
return delegate.insert(instance, domainType, identifier);
}

/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.DataAccessStrategy#update(java.lang.Object, java.lang.Class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,23 @@
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.data.mapping.PersistentPropertyPath;
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.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.domain.Identifier;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;

import java.sql.Array;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
* {@link RelationalConverter} that uses a {@link MappingContext} to apply basic conversion of relational values to
Expand Down Expand Up @@ -122,4 +128,6 @@ public Object writeValue(@Nullable Object value, TypeInformation<?> type) {

return super.writeValue(value, type);
}


}
Loading