Browse Source

DATAJDBC-393 - Polishing.

Accept AggregateChange in BeforeSave and BeforeDelete callbacks. Add Javadoc. Fix generics usage in RelationalAuditingCallback. Align documentation for consistent entity callback documentation.

Add overloaded JdbcAggregateTemplate constructor to directly configure EntityCallbacks.

Original pull request: #161.
pull/163/head
Mark Paluch 7 years ago
parent
commit
257dd7d5d4
  1. 78
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java
  2. 17
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java
  3. 10
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositories.java
  4. 8
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
  5. 4
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java
  6. 12
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java
  7. 22
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteCallback.java
  8. 16
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadCallback.java
  9. 14
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveCallback.java
  10. 18
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeConvertCallback.java
  11. 20
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteCallback.java
  12. 21
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveCallback.java
  13. 9
      spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingCallback.java
  14. 2
      spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingEventListener.java
  15. 2
      src/main/asciidoc/index.adoc
  16. 50
      src/main/asciidoc/jdbc.adoc

78
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java

@ -20,10 +20,10 @@ import java.util.function.Function; @@ -20,10 +20,10 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.conversion.AggregateChange.Kind;
@ -62,7 +62,38 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -62,7 +62,38 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
private final DataAccessStrategy accessStrategy;
private EntityCallbacks entityCallbacks = NoopEntityCallback.INSTANCE;
private EntityCallbacks entityCallbacks = EntityCallbacks.create();
/**
* Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationContext}, {@link RelationalMappingContext} and
* {@link DataAccessStrategy}.
*
* @param publisher must not be {@literal null}.
* @param context must not be {@literal null}.
* @param dataAccessStrategy must not be {@literal null}.
* @since 1.1
*/
public JdbcAggregateTemplate(ApplicationContext publisher, RelationalMappingContext context,
RelationalConverter converter, DataAccessStrategy dataAccessStrategy) {
Assert.notNull(publisher, "ApplicationContext 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);
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context);
this.interpreter = new DefaultJdbcInterpreter(context, accessStrategy);
setEntityCallbacks(EntityCallbacks.create(publisher));
}
/**
* Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationEventPublisher},
@ -92,6 +123,10 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -92,6 +123,10 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
this.interpreter = new DefaultJdbcInterpreter(context, accessStrategy);
}
/**
* @param entityCallbacks
* @since 1.1
*/
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
Assert.notNull(entityCallbacks, "Callbacks must not be null.");
@ -297,16 +332,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -297,16 +332,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
triggerAfterDelete(entity, id, change);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <T> AggregateChange<T> createChange(T instance) {
// context.getRequiredPersistentEntity(o.getClass()).isNew(o)
AggregateChange<T> aggregateChange = new AggregateChange(Kind.SAVE, instance.getClass(), instance);
jdbcEntityWriter.write(instance, aggregateChange);
return aggregateChange;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private <T> AggregateChange<T> createInsertChange(T instance) {
@ -351,9 +376,11 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -351,9 +376,11 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
private <T> T triggerAfterLoad(Object id, T entity) {
publisher.publishEvent(new AfterLoadEvent(Identifier.of(id), entity));
Specified identifier = Identifier.of(id);
publisher.publishEvent(new AfterLoadEvent(identifier, entity));
return entityCallbacks.callback(AfterLoadCallback.class, entity, Identifier.of(id));
return entityCallbacks.callback(AfterLoadCallback.class, entity, identifier);
}
private <T> T triggerBeforeConvert(T aggregateRoot, @Nullable Object id) {
@ -373,7 +400,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -373,7 +400,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
change //
));
return entityCallbacks.callback(BeforeSaveCallback.class, aggregateRoot, identifier);
return entityCallbacks.callback(BeforeSaveCallback.class, aggregateRoot, identifier, change);
}
private <T> T triggerAfterSave(T aggregateRoot, Object id, AggregateChange<T> change) {
@ -389,7 +416,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -389,7 +416,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
return entityCallbacks.callback(AfterSaveCallback.class, aggregateRoot, identifier);
}
@Nullable
private <T> void triggerAfterDelete(@Nullable T aggregateRoot, Object id, AggregateChange<?> change) {
Specified identifier = Identifier.of(id);
@ -409,25 +435,9 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -409,25 +435,9 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
publisher.publishEvent(new BeforeDeleteEvent(identifier, Optional.ofNullable(aggregateRoot), change));
if (aggregateRoot != null) {
return entityCallbacks.callback(BeforeDeleteCallback.class, aggregateRoot, identifier);
return entityCallbacks.callback(BeforeDeleteCallback.class, aggregateRoot, identifier, change);
}
return aggregateRoot;
}
/**
* An {@link EntityCallbacks} implementation doing nothing.
*/
private enum NoopEntityCallback implements EntityCallbacks {
INSTANCE {
@Override
public void addEntityCallback(EntityCallback<?> callback) {}
@Override
public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Object... args) {
return entity;
}
}
return null;
}
}

17
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java

@ -17,8 +17,7 @@ package org.springframework.data.jdbc.repository.config; @@ -17,8 +17,7 @@ package org.springframework.data.jdbc.repository.config;
import java.util.Optional;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@ -75,7 +74,7 @@ public class AbstractJdbcConfiguration { @@ -75,7 +74,7 @@ public class AbstractJdbcConfiguration {
* Creates a {@link RelationalConverter} using the configured {@link #jdbcMappingContext(Optional)}. Will get
* {@link #jdbcCustomConversions()} applied.
*
* @see #jdbcMappingContext(Optional)
* @see #jdbcMappingContext(Optional, JdbcCustomConversions)
* @see #jdbcCustomConversions()
* @return must not be {@literal null}.
*/
@ -91,7 +90,7 @@ public class AbstractJdbcConfiguration { @@ -91,7 +90,7 @@ public class AbstractJdbcConfiguration {
/**
* Register custom {@link Converter}s in a {@link JdbcCustomConversions} object if required. These
* {@link JdbcCustomConversions} will be registered with the
* {@link #jdbcConverter(RelationalMappingContext, NamedParameterJdbcOperations, ObjectProvider, Optional, JdbcConverter)}.
* {@link #jdbcConverter(RelationalMappingContext, NamedParameterJdbcOperations, RelationResolver, JdbcCustomConversions)}.
* Returns an empty {@link JdbcCustomConversions} instance by default.
*
* @return will never be {@literal null}.
@ -105,16 +104,16 @@ public class AbstractJdbcConfiguration { @@ -105,16 +104,16 @@ public class AbstractJdbcConfiguration {
* Register a {@link JdbcAggregateTemplate} as a bean for easy use in applications that need a lower level of
* abstraction than the normal repository abstraction.
*
* @param publisher for publishing events. Must not be {@literal null}.
* @param context the mapping context to be used. Must not be {@literal null}.
* @param applicationContext for publishing events. Must not be {@literal null}.
* @param mappingContext the mapping context to be used. Must not be {@literal null}.
* @param converter the conversions used when reading and writing from/to the database. Must not be {@literal null}.
* @return a {@link JdbcAggregateTemplate}. Will never be {@literal null}.
*/
@Bean
public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationEventPublisher publisher,
RelationalMappingContext context, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) {
public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationContext applicationContext,
RelationalMappingContext mappingContext, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) {
return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy);
return new JdbcAggregateTemplate(applicationContext, mappingContext, converter, dataAccessStrategy);
}
/**

10
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositories.java

@ -101,15 +101,15 @@ public @interface EnableJdbcRepositories { @@ -101,15 +101,15 @@ public @interface EnableJdbcRepositories {
String repositoryImplementationPostfix() default "Impl";
/**
* Configures the name of the {@link NamedParameterJdbcOperations} bean definition to be used to create repositories
* discovered through this annotation. Defaults to {@code namedParameterJdbcTemplate}.
* Configures the name of the {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations} bean
* definition to be used to create repositories discovered through this annotation. Defaults to
* {@code namedParameterJdbcTemplate}.
*/
String jdbcOperationsRef() default "";
/**
* Configures the name of the {@link DataAccessStrategy} bean definition to be used to create repositories
* discovered through this annotation. Defaults to {@code defaultDataAccessStrategy} if existed.
* Configures the name of the {@link org.springframework.data.jdbc.core.convert.DataAccessStrategy} bean definition to
* be used to create repositories discovered through this annotation. Defaults to {@code defaultDataAccessStrategy}.
*/
String dataAccessStrategyRef() default "";
}

8
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java

@ -119,7 +119,8 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport { @@ -119,7 +119,8 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
JdbcAggregateTemplate template = new JdbcAggregateTemplate(publisher, context, converter, accessStrategy);
SimpleJdbcRepository<?, Object> repository = new SimpleJdbcRepository<>(template, context.getPersistentEntity(repositoryInformation.getDomainType()));
SimpleJdbcRepository<?, Object> repository = new SimpleJdbcRepository<>(template,
context.getPersistentEntity(repositoryInformation.getDomainType()));
if (entityCallbacks != null) {
template.setEntityCallbacks(entityCallbacks);
@ -156,8 +157,11 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport { @@ -156,8 +157,11 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
}
/**
* @param entityCallbacks
* @since 1.1
*/
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
this.entityCallbacks = entityCallbacks;
}
}

4
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java

@ -44,7 +44,6 @@ import org.springframework.data.annotation.Id; @@ -44,7 +44,6 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.testing.DatabaseProfileValueSource;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@ -856,7 +855,6 @@ public class JdbcAggregateTemplateIntegrationTests { @@ -856,7 +855,6 @@ public class JdbcAggregateTemplateIntegrationTests {
Map<String, NoIdMapChain3> chain3 = new HashMap<>();
}
@Configuration
@Import(TestConfiguration.class)
static class Config {
@ -868,7 +866,7 @@ public class JdbcAggregateTemplateIntegrationTests { @@ -868,7 +866,7 @@ public class JdbcAggregateTemplateIntegrationTests {
@Bean
JdbcAggregateOperations operations(ApplicationEventPublisher publisher, RelationalMappingContext context,
DataAccessStrategy dataAccessStrategy, RelationalConverter converter) {
DataAccessStrategy dataAccessStrategy, RelationalConverter converter) {
return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy);
}
}

12
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java

@ -35,6 +35,7 @@ import org.springframework.data.jdbc.core.convert.DataAccessStrategy; @@ -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.core.convert.RelationResolver;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@ -47,7 +48,10 @@ import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback @@ -47,7 +48,10 @@ import org.springframework.data.relational.core.mapping.event.BeforeSaveCallback
import org.springframework.data.relational.core.mapping.event.Identifier;
/**
* Unit tests for {@link JdbcAggregateTemplate}.
*
* @author Christoph Strobl
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class JdbcAggregateTemplateUnitTests {
@ -98,7 +102,8 @@ public class JdbcAggregateTemplateUnitTests { @@ -98,7 +102,8 @@ public class JdbcAggregateTemplateUnitTests {
SampleEntity last = template.save(first);
verify(callbacks).callback(BeforeConvertCallback.class, first, Identifier.ofNullable(null));
verify(callbacks).callback(BeforeSaveCallback.class, second, Identifier.ofNullable(23L));
verify(callbacks).callback(eq(BeforeSaveCallback.class), eq(second), eq(Identifier.ofNullable(23L)),
any(AggregateChange.class));
verify(callbacks).callback(AfterSaveCallback.class, third, Identifier.of(23L));
assertThat(last).isEqualTo(third);
}
@ -109,11 +114,12 @@ public class JdbcAggregateTemplateUnitTests { @@ -109,11 +114,12 @@ public class JdbcAggregateTemplateUnitTests {
SampleEntity first = new SampleEntity(23L, "Alfred");
SampleEntity second = new SampleEntity(23L, "Alfred E.");
when(callbacks.callback(any(Class.class), any(), any())).thenReturn(second);
when(callbacks.callback(any(Class.class), any(), any(), any())).thenReturn(second);
template.delete(first, SampleEntity.class);
verify(callbacks).callback(BeforeDeleteCallback.class, first, Identifier.of(23L));
verify(callbacks).callback(eq(BeforeDeleteCallback.class), eq(first), eq(Identifier.of(23L)),
any(AggregateChange.class));
verify(callbacks).callback(AfterDeleteCallback.class, second, Identifier.of(23L));
}

22
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteCallback.java

@ -16,18 +16,26 @@ @@ -16,18 +16,26 @@
package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.lang.Nullable;
/**
* An {@link EntityCallback} that gets called after an aggregate got deleted. This callback gets only invoked if
* the method deleting the aggregate received an instance of that aggregate as an argument. Methods deleting entities by id or
* without any parameter don't invoke this callback.
*
* @since 1.1
* An {@link EntityCallback} that gets called after an aggregate got deleted. This callback gets only invoked if the
* method deleting the aggregate received an instance of that aggregate as an argument. Methods deleting entities by id
* or without any parameter don't invoke this callback.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
@FunctionalInterface
public interface AfterDeleteCallback<T> extends EntityCallback<T> {
T onAfterDelete(@Nullable T aggregate, Identifier id);
/**
* Entity callback method invoked after an aggregate root was deleted. Can return either the same or a modified
* instance of the aggregate object.
*
* @param aggregate the aggregate that was deleted.
* @param id identifier.
* @return the aggregate that was deleted.
*/
T onAfterDelete(T aggregate, Identifier id);
}

16
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadCallback.java

@ -18,12 +18,22 @@ package org.springframework.data.relational.core.mapping.event; @@ -18,12 +18,22 @@ package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.mapping.callback.EntityCallback;
/**
* An {@link EntityCallback} that gets invoked after an aggregate gets loaded from the database.
*
* @since 1.1
* An {@link EntityCallback} that gets invoked after an aggregate was loaded from the database.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
@FunctionalInterface
public interface AfterLoadCallback<T> extends EntityCallback<T> {
/**
* Entity callback method invoked after an aggregate root was loaded. Can return either the same or a modified
* instance of the domain object.
*
* @param aggregate the loaded aggregate.
* @param id identifier.
* @return the loaded aggregate.
*/
T onAfterLoad(T aggregate, Identifier.Specified id);
}

14
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveCallback.java

@ -19,11 +19,21 @@ import org.springframework.data.mapping.callback.EntityCallback; @@ -19,11 +19,21 @@ import org.springframework.data.mapping.callback.EntityCallback;
/**
* An {@link EntityCallback} that gets invoked after an aggregate was saved.
*
* @since 1.1
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
@FunctionalInterface
public interface AfterSaveCallback<T> extends EntityCallback<T> {
/**
* Entity callback method invoked after an aggregate root was persisted. Can return either the same or a modified
* instance of the aggregate.
*
* @param aggregate the saved aggregate.
* @param id identifier.
* @return the saved aggregate.
*/
T onAfterSave(T aggregate, Identifier.Specified id);
}

18
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeConvertCallback.java

@ -18,13 +18,23 @@ package org.springframework.data.relational.core.mapping.event; @@ -18,13 +18,23 @@ package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.mapping.callback.EntityCallback;
/**
* An {@link EntityCallback} that gets invoked before the aggregate gets converted into a database change. The decision
* if the change will be an insert or update will be made before this callback gets called.
*
* @since 1.1
* An {@link EntityCallback} that gets invoked before the aggregate is converted into a database change. The decision if
* the change will be an insert or update is made before this callback gets called.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
@FunctionalInterface
public interface BeforeConvertCallback<T> extends EntityCallback<T> {
/**
* Entity callback method invoked before an aggregate root is converted to be persisted. Can return either the same or
* a modified instance of the aggregate.
*
* @param aggregate the saved aggregate.
* @param id identifier.
* @return the aggregate to be persisted.
*/
T onBeforeConvert(T aggregate, Identifier id);
}

20
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteCallback.java

@ -16,17 +16,29 @@ @@ -16,17 +16,29 @@
package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.data.relational.core.conversion.AggregateChange;
/**
* An {@link EntityCallback} that gets invoked before an entity gets deleted.This callback gets only invoked if * the
* An {@link EntityCallback} that gets invoked before an entity is deleted. This callback gets only invoked if the
* method deleting the aggregate received an instance of that aggregate as an argument. Methods deleting entities by id
* or * without any parameter don't invoke this callback.
* or without any parameter don't invoke this callback.
*
* @since 1.1
* @author Jens Schauder
* @since 1.1
*/
@FunctionalInterface
public interface BeforeDeleteCallback<T> extends EntityCallback<T> {
T onBeforeDelete(T aggregate, Identifier id);
/**
* Entity callback method invoked before an aggregate root is deleted. Can return either the same or a modified
* instance of the aggregate and can modify {@link AggregateChange} contents. This method is called after converting
* the {@code aggregate} to {@link AggregateChange}. Changes to the aggregate are not taken into account for deleting.
* Only transient fields of the entity should be changed in this callback.
*
* @param aggregate the aggregate.
* @param id identifier.
* @param aggregateChange the associated {@link AggregateChange}.
* @return the aggregate to be deleted.
*/
T onBeforeDelete(T aggregate, Identifier id, AggregateChange<T> aggregateChange);
}

21
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveCallback.java

@ -16,15 +16,30 @@ @@ -16,15 +16,30 @@
package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.mapping.callback.EntityCallback;
import org.springframework.data.relational.core.conversion.AggregateChange;
/**
* An {@link EntityCallback} that gets invoked before changes get applied to the database but after the aggregate got actually converted to a database change.
* An {@link EntityCallback} that gets invoked before changes are applied to the database, after the aggregate was
* converted to a database change.
*
* @since 1.1
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
@FunctionalInterface
public interface BeforeSaveCallback<T> extends EntityCallback<T> {
T onBeforeSave(T aggregate, Identifier id);
/**
* Entity callback method invoked before an aggregate root is saved. Can return either the same or a modified instance
* of the aggregate and can modify {@link AggregateChange} contents. This method is called after converting the
* {@code aggregate} to {@link AggregateChange}. Changes to the aggregate are not taken into account for saving. Only
* transient fields of the entity should be changed in this callback. To change persistent the entity before being
* converted, use the {@link BeforeConvertCallback}.
*
* @param aggregate the aggregate.
* @param id identifier.
* @param aggregateChange the associated {@link AggregateChange}.
* @return the aggregate object to be persisted.
*/
T onBeforeSave(T aggregate, Identifier id, AggregateChange<T> aggregateChange);
}

9
spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingCallback.java

@ -21,7 +21,6 @@ import org.springframework.context.ApplicationListener; @@ -21,7 +21,6 @@ import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.relational.core.mapping.event.BeforeConvertCallback;
import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.relational.core.mapping.event.Identifier;
/**
@ -30,9 +29,11 @@ import org.springframework.data.relational.core.mapping.event.Identifier; @@ -30,9 +29,11 @@ import org.springframework.data.relational.core.mapping.event.Identifier;
* An instance of this class gets registered when you enable auditing for Spring Data JDBC.
*
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
*/
@RequiredArgsConstructor
public class RelationalAuditingCallback implements BeforeConvertCallback, Ordered {
public class RelationalAuditingCallback implements BeforeConvertCallback<Object>, Ordered {
/**
* The order used for this {@link org.springframework.context.event.EventListener}. Ordering ensures that this
@ -54,6 +55,10 @@ public class RelationalAuditingCallback implements BeforeConvertCallback, Ordere @@ -54,6 +55,10 @@ public class RelationalAuditingCallback implements BeforeConvertCallback, Ordere
return AUDITING_ORDER;
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.mapping.event.BeforeConvertCallback#onBeforeConvert(java.lang.Object, org.springframework.data.relational.core.mapping.event.Identifier)
*/
@Override
public Object onBeforeConvert(Object entity, Identifier id) {
return handler.markAudited(entity);

2
spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingEventListener.java

@ -28,7 +28,7 @@ import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent; @@ -28,7 +28,7 @@ import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent;
* @author Kazuki Shimizu
* @author Jens Schauder
* @author Oliver Gierke
* @deprecated Use {@link RelationalAuditingCallback} instead.
* @deprecated since 1.1, use {@link RelationalAuditingCallback} instead.
*/
@Deprecated
@RequiredArgsConstructor

2
src/main/asciidoc/index.adoc

@ -4,7 +4,7 @@ Jens Schauder, Jay Bryant, Mark Paluch, Bastian Wilhelm @@ -4,7 +4,7 @@ Jens Schauder, Jay Bryant, Mark Paluch, Bastian Wilhelm
:revdate: {localdate}
:javadoc-base: https://docs.spring.io/spring-data/jdbc/docs/{revnumber}/api/
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
:spring-data-commons-docs: https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
:spring-framework-docs: https://docs.spring.io/spring-framework/docs/{springVersion}/
(C) 2018-2019 The original authors.

50
src/main/asciidoc/jdbc.adoc

@ -5,7 +5,7 @@ This chapter points out the specialties for repository support for JDBC. This bu @@ -5,7 +5,7 @@ This chapter points out the specialties for repository support for JDBC. This bu
You should have a sound understanding of the basic concepts explained there.
[[jdbc.why]]
=== Why Spring Data JDBC?
== Why Spring Data JDBC?
The main persistence API for relational databases in the Java world is certainly JPA, which has its own Spring Data module.
Why is there another one?
@ -36,7 +36,7 @@ If you do not like that, you should code your own strategy. @@ -36,7 +36,7 @@ If you do not like that, you should code your own strategy.
Spring Data JDBC offers only very limited support for customizing the strategy with annotations.
[[jdbc.domain-driven-design]]
=== Domain Driven Design and Relational Databases.
== Domain Driven Design and Relational Databases.
All Spring Data modules are inspired by the concepts of "`repository`", "`aggregate`", and "`aggregate root`" from Domain Driven Design.
These are possibly even more important for Spring Data JDBC, because they are, to some extent, contrary to normal practice when working with relational databases.
@ -62,7 +62,7 @@ WARNING: In the current implementation, entities referenced from an aggregate ro @@ -62,7 +62,7 @@ WARNING: In the current implementation, entities referenced from an aggregate ro
You can overwrite the repository methods with implementations that match your style of working and designing your database.
[[jdbc.java-config]]
=== Annotation-based Configuration
== Annotation-based Configuration
The Spring Data JDBC repositories support can be activated by an annotation through Java configuration, as the following example shows:
.Spring Data JDBC repositories using Java configuration
@ -563,9 +563,9 @@ Note that the type used for prefixing the statement name is the name of the aggr @@ -563,9 +563,9 @@ Note that the type used for prefixing the statement name is the name of the aggr
|===
[[jdbc.events]]
== Events and Callback
== Lifecycle Events
Spring Data JDBC triggers events that get published to any matching `ApplicationListener` and `EntityCallback` beans in the application context.
Spring Data JDBC triggers events that get published to any matching `ApplicationListener` beans in the application context.
For example, the following listener gets invoked before an aggregate gets saved:
====
@ -586,33 +586,59 @@ public ApplicationListener<BeforeSave> timeStampingSaveTime() { @@ -586,33 +586,59 @@ public ApplicationListener<BeforeSave> timeStampingSaveTime() {
----
====
The following table describes the available events and callbacks:
The following table describes the available events:
.Available events
|===
| Event | `EntityCallback` | When It Is Published
| Event | When It Is Published
| {javadoc-base}org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.html[`BeforeDeleteEvent`]
| {javadoc-base}org/springframework/data/relational/core/mapping/event/BeforeDeleteCallback.html[`BeforeDeleteCallback`]
| Before an aggregate root gets deleted.
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.html[`AfterDeleteEvent`]
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterDeleteCallback.html[`AfterDeleteCallback`]
| After an aggregate root gets deleted.
| {javadoc-base}/org/springframework/data/relational/core/mapping/event/BeforeConvertEvent.html[`BeforeConvertEvent`]
| {javadoc-base}/org/springframework/data/relational/core/mapping/event/BeforeConvertCallback.html[`BeforeConvertCallback`]
| Before an aggregate root gets saved (that is, inserted or updated but after the decision about whether if it gets updated or deleted was made).
| {javadoc-base}/org/springframework/data/relational/core/mapping/event/BeforeSaveEvent.html[`BeforeSaveEvent`]
| {javadoc-base}/org/springframework/data/relational/core/mapping/event/BeforeSaveCallback.html[`BeforeSaveCallback`]
| Before an aggregate root gets saved (that is, inserted or updated but after the decision about whether if it gets updated or deleted was made).
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterSaveEvent.html[`AfterSaveEvent`]
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterSaveCallback.html[`AfterSaveCallback`]
| After an aggregate root gets saved (that is, inserted or updated).
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterLoadEvent.html[`AfterLoadEvent`]
| After an aggregate root gets created from a database `ResultSet` and all its property get set.
|===
WARNING: Lifecycle events depend on an `ApplicationEventMulticaster`, which in case of the `SimpleApplicationEventMulticaster` can be configured with a `TaskExecutor`, and therefore gives no guarantees when an Event is processed.
include::../{spring-data-commons-docs}/entity-callbacks.adoc[leveloffset=+1]
[[jdbc.entity-callbacks]]
=== Store-specific EntityCallbacks
Spring Data JDBC uses the `EntityCallback` API for its auditing support and reacts on the following callbacks:
.Available Callbacks
|===
| `EntityCallback` | When It Is Published
| {javadoc-base}org/springframework/data/relational/core/mapping/event/BeforeDeleteCallback.html[`BeforeDeleteCallback`]
| Before an aggregate root gets deleted.
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterDeleteCallback.html[`AfterDeleteCallback`]
| After an aggregate root gets deleted.
| {javadoc-base}/org/springframework/data/relational/core/mapping/event/BeforeConvertCallback.html[`BeforeConvertCallback`]
| Before an aggregate root gets saved (that is, inserted or updated but after the decision about whether if it gets updated or deleted was made).
| {javadoc-base}/org/springframework/data/relational/core/mapping/event/BeforeSaveCallback.html[`BeforeSaveCallback`]
| Before an aggregate root gets saved (that is, inserted or updated but after the decision about whether if it gets updated or deleted was made).
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterSaveCallback.html[`AfterSaveCallback`]
| After an aggregate root gets saved (that is, inserted or updated).
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterLoadCallback.html[`AfterLoadCallback`]
| After an aggregate root gets created from a database `ResultSet` and all its property get set.
|===

Loading…
Cancel
Save