Skip to content

DATAJDBC-241 - Support for immutable entities. #84

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 2 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-jdbc</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<version>1.0.0.DATAJDBC-241-SNAPSHOT</version>

<name>Spring Data JDBC</name>
<description>Spring Data module for JDBC repositories.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public CascadingDataAccessStrategy(List<DataAccessStrategy> strategies) {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> T insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
return collect(das -> das.insert(instance, domainType, additionalParameters));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,9 @@ public interface DataAccessStrategy {
* @param additionalParameters name-value pairs of additional parameters. Especially ids of parent entities that need
* to get referenced are contained in this map. Must not be {@code null}.
* @param <T> the type of the instance.
* @return the instance after insert into. The returned instance may be different to the given {@code instance} as
* this method can apply changes to the return object.
* @return the id generated by the database if any.
*/
<T> T insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters);
<T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters);

/**
* 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 @@ -21,13 +21,11 @@
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPath;
Expand Down Expand Up @@ -83,7 +81,7 @@ public DefaultDataAccessStrategy(SqlGeneratorSource sqlGeneratorSource, Relation
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> T insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {

KeyHolder holder = new GeneratedKeyHolder();
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
Expand All @@ -110,16 +108,7 @@ public <T> T insert(T instance, Class<T> domainType, Map<String, Object> additio
holder //
);

instance = setIdFromJdbc(instance, holder, persistentEntity);

// if there is an id property and it was null before the save
// The database should have created an id and provided it.

if (idProperty != null && idValue == null && persistentEntity.isNew(instance)) {
throw new IllegalStateException(String.format(ENTITY_NEW_AFTER_INSERT, persistentEntity));
}

return instance;
return getIdFromHolder(holder, persistentEntity);
}

/*
Expand Down Expand Up @@ -327,41 +316,20 @@ private static <S, ID> boolean isIdPropertyNullOrScalarZero(@Nullable ID idValue
|| (idProperty.getType() == long.class && idValue.equals(0L));
}

private <S> S setIdFromJdbc(S instance, KeyHolder holder, RelationalPersistentEntity<S> persistentEntity) {

try {

PersistentPropertyAccessor<S> accessor = converter.getPropertyAccessor(persistentEntity, instance);

getIdFromHolder(holder, persistentEntity).ifPresent(it -> {

RelationalPersistentProperty idProperty = persistentEntity.getRequiredIdProperty();

accessor.setProperty(idProperty, it);

});

return accessor.getBean();

} catch (NonTransientDataAccessException e) {
throw new UnableToSetId("Unable to set id of " + instance, e);
}
}

private <S> Optional<Object> getIdFromHolder(KeyHolder holder, RelationalPersistentEntity<S> persistentEntity) {
private <S> Object getIdFromHolder(KeyHolder holder, RelationalPersistentEntity<S> persistentEntity) {

try {
// MySQL just returns one value with a special name
return Optional.ofNullable(holder.getKey());
return holder.getKey();
} catch (InvalidDataAccessApiUsageException e) {
// Postgres returns a value for each column
Map<String, Object> keys = holder.getKeys();

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ class DefaultJdbcInterpreter implements Interpreter {
@Override
public <T> void interpret(Insert<T> insert) {

T entity = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), createAdditionalColumnValues(insert));
insert.setResultingEntity(entity);
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), createAdditionalColumnValues(insert));

insert.setGeneratedId(id);
}

/*
Expand All @@ -69,16 +70,16 @@ public <T> void interpret(Insert<T> insert) {
@Override
public <T> void interpret(InsertRoot<T> insert) {

T entity = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Collections.emptyMap());
insert.setResultingEntity(entity);
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Collections.emptyMap());
insert.setGeneratedId(id);
}

/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.Update)
*/
@Override
public <T> void interpret(Update<T> update) {
public <T> void interpret(Update<T> update ) {
accessStrategy.update(update.getEntity(), update.getEntityType());
}

Expand Down Expand Up @@ -169,8 +170,8 @@ private Object getIdFromEntityDependingOn(DbAction.WithEntity<?> dependingOn,

Object entity = dependingOn.getEntity();

if (dependingOn instanceof DbAction.WithResultEntity) {
entity = ((DbAction.WithResultEntity<?>) dependingOn).getResultingEntity();
if (dependingOn instanceof DbAction.WithGeneratedId) {
return ((DbAction.WithGeneratedId<?>) dependingOn).getGeneratedId();
}

return persistentEntity.getIdentifierAccessor(entity).getIdentifier();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,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> T insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
return delegate.insert(instance, domainType, additionalParameters);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.conversion.AggregateChange.Kind;
import org.springframework.data.relational.core.conversion.Interpreter;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.conversion.RelationalEntityDeleteWriter;
import org.springframework.data.relational.core.conversion.RelationalEntityWriter;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
Expand All @@ -46,6 +47,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {

private final ApplicationEventPublisher publisher;
private final RelationalMappingContext context;
private final RelationalConverter converter;
private final Interpreter interpreter;

private final RelationalEntityWriter jdbcEntityWriter;
Expand All @@ -62,14 +64,16 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
* @param dataAccessStrategy must not be {@literal null}.
*/
public JdbcAggregateTemplate(ApplicationEventPublisher publisher, RelationalMappingContext context,
DataAccessStrategy dataAccessStrategy) {
RelationalConverter converter, DataAccessStrategy dataAccessStrategy) {

Assert.notNull(publisher, "ApplicationEventPublisher must not be null!");
Assert.notNull(context, "RelationalMappingContext must not be null!");
Assert.notNull(converter, "RelationalConverter must not be null!");
Assert.notNull(dataAccessStrategy, "DataAccessStrategy must not be null!");

this.publisher = publisher;
this.context = context;
this.converter = converter;
this.accessStrategy = dataAccessStrategy;

this.jdbcEntityWriter = new RelationalEntityWriter(context);
Expand All @@ -86,8 +90,8 @@ public <T> T save(T instance) {

Assert.notNull(instance, "Aggregate instance must not be null!");

RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(instance.getClass());
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(instance);
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
IdentifierAccessor identifierAccessor = persistentEntity.getIdentifierAccessor(instance);

AggregateChange<T> change = createChange(instance);

Expand All @@ -97,9 +101,9 @@ public <T> T save(T instance) {
change //
));

change.executeWith(interpreter);
change.executeWith(interpreter, context, converter);

Object identifier = entity.getIdentifierAccessor(change.getEntity()).getIdentifier();
Object identifier = persistentEntity.getIdentifierAccessor(change.getEntity()).getIdentifier();

Assert.notNull(identifier, "After saving the identifier must not be null");

Expand Down Expand Up @@ -198,7 +202,7 @@ public <S> void deleteById(Object id, Class<S> domainType) {
public void deleteAll(Class<?> domainType) {

AggregateChange<?> change = createDeletingChange(domainType);
change.executeWith(interpreter);
change.executeWith(interpreter, context, converter);
}

private void deleteTree(Object id, @Nullable Object entity, Class<?> domainType) {
Expand All @@ -209,7 +213,7 @@ private void deleteTree(Object id, @Nullable Object entity, Class<?> domainType)
Optional<Object> optionalEntity = Optional.ofNullable(entity);
publisher.publishEvent(new BeforeDeleteEvent(specifiedId, optionalEntity, change));

change.executeWith(interpreter);
change.executeWith(interpreter, context, converter);

publisher.publishEvent(new AfterDeleteEvent(specifiedId, optionalEntity, change));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,13 @@ public void setNamespaceStrategy(NamespaceStrategy namespaceStrategy) {
* @see org.springframework.data.jdbc.core.DataAccessStrategy#insert(java.lang.Object, java.lang.Class, java.util.Map)
*/
@Override
public <T> T insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {

public <T> Object insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {

MyBatisContext myBatisContext = new MyBatisContext(null, instance, domainType, additionalParameters);
sqlSession().insert(namespace(domainType) + ".insert",
new MyBatisContext(null, instance, domainType, additionalParameters));
myBatisContext);

return instance;
return myBatisContext.getId();
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) {
@Override
protected Object getTargetRepository(RepositoryInformation repositoryInformation) {

JdbcAggregateTemplate template = new JdbcAggregateTemplate(publisher, context, accessStrategy);
JdbcAggregateTemplate template = new JdbcAggregateTemplate(publisher, context, converter, accessStrategy);

return new SimpleJdbcRepository<>(template, context.getPersistentEntity(repositoryInformation.getDomainType()));
}
Expand Down
Loading