Browse Source

DATAJDBC-204 - Polishing.

Extracted ApplicationContext and Repository construction into method in order to make the actual test stand out more.
Split a test in two.
JavaDoc.
Code formatting.
Removed access modifiers in tests.
pull/61/merge
Jens Schauder 8 years ago
parent
commit
53eea0202a
  1. 23
      src/main/java/org/springframework/data/jdbc/domain/support/JdbcAuditingEventListener.java
  2. 16
      src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditing.java
  3. 22
      src/main/java/org/springframework/data/jdbc/repository/config/JdbcAuditingRegistrar.java
  4. 260
      src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditingHsqlIntegrationTests.java

23
src/main/java/org/springframework/data/jdbc/domain/support/JdbcAuditingEventListener.java

@ -19,30 +19,22 @@ import org.springframework.beans.factory.ObjectFactory; @@ -19,30 +19,22 @@ import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.data.auditing.AuditingHandler;
import org.springframework.data.jdbc.mapping.event.BeforeSaveEvent;
import org.springframework.data.jdbc.repository.config.EnableJdbcAuditing;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Spring JDBC event listener to capture auditing information on persisting and updating entities.
* <p>
* You can enable this class just a matter of activating auditing using {@link org.springframework.data.jdbc.repository.config.EnableJdbcAuditing} in your Spring config:
*
* <pre>
* &#064;Configuration
* &#064;EnableJdbcRepositories
* &#064;EnableJdbcAuditing
* class JdbcRepositoryConfig {
* }
* </pre>
* An instance of this class gets registered when you apply {@link EnableJdbcAuditing} to your Spring config.
*
* @author Kazuki Shimizu
* @see org.springframework.data.jdbc.repository.config.EnableJdbcAuditing
* @see EnableJdbcAuditing
* @since 1.0
*/
public class JdbcAuditingEventListener implements ApplicationListener<BeforeSaveEvent> {
@Nullable
private AuditingHandler handler;
@Nullable private AuditingHandler handler;
/**
* Configures the {@link AuditingHandler} to be used to set the current auditor on the domain types touched.
@ -50,18 +42,24 @@ public class JdbcAuditingEventListener implements ApplicationListener<BeforeSave @@ -50,18 +42,24 @@ public class JdbcAuditingEventListener implements ApplicationListener<BeforeSave
* @param auditingHandler must not be {@literal null}.
*/
public void setAuditingHandler(ObjectFactory<AuditingHandler> auditingHandler) {
Assert.notNull(auditingHandler, "AuditingHandler must not be null!");
this.handler = auditingHandler.getObject();
}
/**
* {@inheritDoc}
*
* @param event a notification event for indicating before save
*/
@Override
public void onApplicationEvent(BeforeSaveEvent event) {
if (handler != null) {
event.getOptionalEntity().ifPresent(entity -> {
if (event.getId().getOptionalValue().isPresent()) {
handler.markModified(entity);
} else {
@ -70,5 +68,4 @@ public class JdbcAuditingEventListener implements ApplicationListener<BeforeSave @@ -70,5 +68,4 @@ public class JdbcAuditingEventListener implements ApplicationListener<BeforeSave
});
}
}
}

16
src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditing.java

@ -29,22 +29,6 @@ import org.springframework.data.domain.AuditorAware; @@ -29,22 +29,6 @@ import org.springframework.data.domain.AuditorAware;
/**
* Annotation to enable auditing in JDBC via annotation configuration.
*
* If you use the auditing feature, you should be configures beans of Spring Data JDBC
* using {@link org.springframework.data.jdbc.repository.config.EnableJdbcRepositories} in your Spring config:
*
* <pre>
* &#064;Configuration
* &#064;EnableJdbcRepositories
* &#064;EnableJdbcAuditing
* class JdbcRepositoryConfig {
* }
* </pre>
*
* <p>
* Note: This feature cannot use to a entity that implements {@link org.springframework.data.domain.Auditable}
* because the Spring Data JDBC does not support an {@link java.util.Optional} property yet.
* </p>
*
* @see EnableJdbcRepositories
* @author Kazuki Shimizu
* @since 1.0

22
src/main/java/org/springframework/data/jdbc/repository/config/JdbcAuditingRegistrar.java

@ -27,7 +27,8 @@ import org.springframework.data.config.ParsingUtils; @@ -27,7 +27,8 @@ import org.springframework.data.config.ParsingUtils;
import org.springframework.data.jdbc.domain.support.JdbcAuditingEventListener;
/**
* {@link ImportBeanDefinitionRegistrar} to enable {@link EnableJdbcAuditing} annotation.
* {@link ImportBeanDefinitionRegistrar} which registers additional beans in order to enable auditing via the
* {@link EnableJdbcAuditing} annotation.
*
* @see EnableJdbcAuditing
* @author Kazuki Shimizu
@ -37,8 +38,9 @@ class JdbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { @@ -37,8 +38,9 @@ class JdbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
/**
* {@inheritDoc}
*
* @return return the {@link EnableJdbcAuditing}
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
* @see AuditingBeanDefinitionRegistrarSupport#getAnnotation()
*/
@Override
protected Class<? extends Annotation> getAnnotation() {
@ -47,8 +49,9 @@ class JdbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { @@ -47,8 +49,9 @@ class JdbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
/**
* {@inheritDoc}
*
* @return return "{@literal jdbcAuditingHandler}"
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
* @see AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
*/
@Override
protected String getAuditingHandlerBeanName() {
@ -61,22 +64,25 @@ class JdbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { @@ -61,22 +64,25 @@ class JdbcAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
*/
@Override
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
BeanDefinitionBuilder builder = super.getAuditHandlerBeanDefinitionBuilder(configuration);
return builder.addConstructorArgReference("jdbcMappingContext");
}
/**
* Register the bean definition of {@link JdbcAuditingEventListener}.
* {@inheritDoc}
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListenerBeanDefinition(BeanDefinition, BeanDefinitionRegistry)
* Register the bean definition of {@link JdbcAuditingEventListener}. {@inheritDoc}
*
* @see AuditingBeanDefinitionRegistrarSupport#registerAuditListenerBeanDefinition(BeanDefinition,
* BeanDefinitionRegistry)
*/
@Override
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
BeanDefinitionRegistry registry) {
BeanDefinitionRegistry registry) {
Class<?> listenerClass = JdbcAuditingEventListener.class;
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(listenerClass);
builder.addPropertyValue("auditingHandler",
ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), null));
ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), null));
registerInfrastructureBeanWithId(builder.getRawBeanDefinition(), listenerClass.getName(), registry);
}

260
src/test/java/org/springframework/data/jdbc/repository/config/EnableJdbcAuditingHsqlIntegrationTests.java

@ -15,7 +15,17 @@ @@ -15,7 +15,17 @@
*/
package org.springframework.data.jdbc.repository.config;
import static org.assertj.core.api.Assertions.*;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import org.assertj.core.api.SoftAssertions;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -32,144 +42,178 @@ import org.springframework.data.domain.AuditorAware; @@ -32,144 +42,178 @@ import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jdbc.mapping.model.NamingStrategy;
import org.springframework.data.repository.CrudRepository;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests the {@link EnableJdbcAuditing} annotation.
*
* @author Kazuki Shimizu
* @author Jens Schauder
*/
public class EnableJdbcAuditingHsqlIntegrationTests {
SoftAssertions softly = new SoftAssertions();
@Test
public void auditForAnnotatedEntity() throws InterruptedException {
try (ConfigurableApplicationContext context =
new AnnotationConfigApplicationContext(TestConfiguration.class, AuditingConfiguration.class)) {
public void auditForAnnotatedEntity() {
AuditingAnnotatedDummyEntityRepository repository = context.getBean(AuditingAnnotatedDummyEntityRepository.class);
configureRepositoryWith( //
AuditingAnnotatedDummyEntityRepository.class, //
TestConfiguration.class, //
AuditingConfiguration.class) //
.accept(repository -> {
AuditingConfiguration.currentAuditor = "user01";
LocalDateTime now = LocalDateTime.now();
AuditingConfiguration.currentAuditor = "user01";
LocalDateTime now = LocalDateTime.now();
AuditingAnnotatedDummyEntity entity = new AuditingAnnotatedDummyEntity();
entity.setName("Spring Data");
repository.save(entity);
AuditingAnnotatedDummyEntity entity = repository.save(new AuditingAnnotatedDummyEntity());
assertThat(entity.id).isNotNull();
assertThat(entity.getCreatedBy()).isEqualTo("user01");
assertThat(entity.getCreatedDate()).isAfter(now);
assertThat(entity.getLastModifiedBy()).isEqualTo("user01");
assertThat(entity.getLastModifiedDate()).isAfterOrEqualTo(entity.getCreatedDate());
assertThat(entity.getLastModifiedDate()).isAfter(now);
assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
softly.assertThat(entity.id).isNotNull();
softly.assertThat(entity.getCreatedBy()).isEqualTo("user01");
softly.assertThat(entity.getCreatedDate()).isAfter(now);
softly.assertThat(entity.getLastModifiedBy()).isEqualTo("user01");
softly.assertThat(entity.getLastModifiedDate()).isAfterOrEqualTo(entity.getCreatedDate());
softly.assertThat(entity.getLastModifiedDate()).isAfter(now);
softly.assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
LocalDateTime beforeCreatedDate = entity.getCreatedDate();
LocalDateTime beforeLastModifiedDate = entity.getLastModifiedDate();
LocalDateTime beforeCreatedDate = entity.getCreatedDate();
LocalDateTime beforeLastModifiedDate = entity.getLastModifiedDate();
TimeUnit.MILLISECONDS.sleep(100);
AuditingConfiguration.currentAuditor = "user02";
softly.assertAll();
entity.setName("Spring Data JDBC");
repository.save(entity);
sleepMillis(10);
assertThat(entity.getCreatedBy()).isEqualTo("user01");
assertThat(entity.getCreatedDate()).isEqualTo(beforeCreatedDate);
assertThat(entity.getLastModifiedBy()).isEqualTo("user02");
assertThat(entity.getLastModifiedDate()).isAfter(beforeLastModifiedDate);
assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
}
AuditingConfiguration.currentAuditor = "user02";
entity = repository.save(entity);
softly.assertThat(entity.getCreatedBy()).isEqualTo("user01");
softly.assertThat(entity.getCreatedDate()).isEqualTo(beforeCreatedDate);
softly.assertThat(entity.getLastModifiedBy()).isEqualTo("user02");
softly.assertThat(entity.getLastModifiedDate()).isAfter(beforeLastModifiedDate);
softly.assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
});
}
@Test
public void noAnnotatedEntity() {
try (ConfigurableApplicationContext context =
new AnnotationConfigApplicationContext(TestConfiguration.class, AuditingConfiguration.class)) {
DummyEntityRepository repository = context.getBean(DummyEntityRepository.class);
configureRepositoryWith( //
DummyEntityRepository.class, //
TestConfiguration.class, //
AuditingConfiguration.class) //
.accept(repository -> {
DummyEntity entity = new DummyEntity();
entity.setName("Spring Data");
repository.save(entity);
DummyEntity entity = repository.save(new DummyEntity());
assertThat(entity.id).isNotNull();
assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
softly.assertThat(entity.id).isNotNull();
softly.assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
entity.setName("Spring Data JDBC");
repository.save(entity);
softly.assertAll();
assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
}
entity = repository.save(entity);
assertThat(repository.findById(entity.id).get()).isEqualTo(entity);
});
}
@Test
public void customizeEnableJdbcAuditingAttributes() {
// Test for 'auditorAwareRef', 'dateTimeProviderRef' and 'modifyOnCreate'
try (ConfigurableApplicationContext context =
new AnnotationConfigApplicationContext(TestConfiguration.class, CustomizeAuditingConfiguration1.class)) {
AuditingAnnotatedDummyEntityRepository repository = context.getBean(AuditingAnnotatedDummyEntityRepository.class);
LocalDateTime currentDateTime = LocalDate.of(2018, 4, 14).atStartOfDay();
CustomizeAuditingConfiguration1.currentDateTime = currentDateTime;
AuditingAnnotatedDummyEntity entity = new AuditingAnnotatedDummyEntity();
entity.setName("Spring Data JDBC");
repository.save(entity);
assertThat(entity.id).isNotNull();
assertThat(entity.getCreatedBy()).isEqualTo("custom user");
assertThat(entity.getCreatedDate()).isEqualTo(currentDateTime);
assertThat(entity.getLastModifiedBy()).isNull();
assertThat(entity.getLastModifiedDate()).isNull();
}
// Test for 'setDates'
try (ConfigurableApplicationContext context =
new AnnotationConfigApplicationContext(TestConfiguration.class, CustomizeAuditingConfiguration2.class)) {
AuditingAnnotatedDummyEntityRepository repository = context.getBean(AuditingAnnotatedDummyEntityRepository.class);
AuditingAnnotatedDummyEntity entity = new AuditingAnnotatedDummyEntity();
entity.setName("Spring Data JDBC");
repository.save(entity);
assertThat(entity.id).isNotNull();
assertThat(entity.getCreatedBy()).isEqualTo("user");
assertThat(entity.getCreatedDate()).isNull();
assertThat(entity.getLastModifiedBy()).isEqualTo("user");
assertThat(entity.getLastModifiedDate()).isNull();
}
public void customDateTimeProvider() {
configureRepositoryWith( //
AuditingAnnotatedDummyEntityRepository.class, //
TestConfiguration.class, //
CustomizeAuditorAwareAndDateTimeProvider.class) //
.accept(repository -> {
LocalDateTime currentDateTime = LocalDate.of(2018, 4, 14).atStartOfDay();
CustomizeAuditorAwareAndDateTimeProvider.currentDateTime = currentDateTime;
AuditingAnnotatedDummyEntity entity = repository.save(new AuditingAnnotatedDummyEntity());
softly.assertThat(entity.id).isNotNull();
softly.assertThat(entity.getCreatedBy()).isEqualTo("custom user");
softly.assertThat(entity.getCreatedDate()).isEqualTo(currentDateTime);
softly.assertThat(entity.getLastModifiedBy()).isNull();
softly.assertThat(entity.getLastModifiedDate()).isNull();
});
}
@Test
public void customAuditorAware() {
configureRepositoryWith( //
AuditingAnnotatedDummyEntityRepository.class, //
TestConfiguration.class, //
CustomizeAuditorAware.class) //
.accept(repository -> {
AuditingAnnotatedDummyEntity entity = repository.save(new AuditingAnnotatedDummyEntity());
softly.assertThat(entity.id).isNotNull();
softly.assertThat(entity.getCreatedBy()).isEqualTo("user");
softly.assertThat(entity.getCreatedDate()).isNull();
softly.assertThat(entity.getLastModifiedBy()).isEqualTo("user");
softly.assertThat(entity.getLastModifiedDate()).isNull();
});
}
/**
* Usage looks like this:
* <p>
* {@code configure(MyRepository.class, MyConfiguration) .accept(repository -> { // perform tests on repository here
* }); }
*
* @param repositoryType the type of repository you want to perform tests on.
* @param configurationClasses the classes containing the configuration for the
* {@link org.springframework.context.ApplicationContext}.
* @param <T> type of the entity managed by the repository.
* @param <R> type of the repository.
* @return a Consumer for repositories of type {@code R}.
*/
private <T, R extends CrudRepository<T, Long>> Consumer<Consumer<R>> configureRepositoryWith(Class<R> repositoryType,
Class... configurationClasses) {
return (Consumer<R> test) -> {
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(configurationClasses)) {
test.accept(context.getBean(repositoryType));
softly.assertAll();
}
};
}
interface AuditingAnnotatedDummyEntityRepository extends CrudRepository<AuditingAnnotatedDummyEntity, Long> {
private void sleepMillis(int timeout) {
try {
TimeUnit.MILLISECONDS.sleep(timeout);
} catch (InterruptedException e) {
throw new RuntimeException("Failed to sleep", e);
}
}
interface AuditingAnnotatedDummyEntityRepository extends CrudRepository<AuditingAnnotatedDummyEntity, Long> {}
@Data
static class AuditingAnnotatedDummyEntity {
@Id
private Long id;
private String name;
@CreatedBy
private String createdBy;
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedBy
private String lastModifiedBy;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
@Id Long id;
@CreatedBy String createdBy;
@CreatedDate LocalDateTime createdDate;
@LastModifiedBy String lastModifiedBy;
@LastModifiedDate LocalDateTime lastModifiedDate;
}
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
@Data
static class DummyEntity {
@Id
private Long id;
private String name;
@Id private Long id;
// not actually used, exists just to avoid empty value list during insert.
String name;
}
@ComponentScan("org.springframework.data.jdbc.testing")
@ -183,36 +227,42 @@ public class EnableJdbcAuditingHsqlIntegrationTests { @@ -183,36 +227,42 @@ public class EnableJdbcAuditingHsqlIntegrationTests {
@Bean
NamingStrategy namingStrategy() {
return new NamingStrategy() {
public String getTableName(Class<?> type) {
return "DummyEntity";
}
};
}
}
@EnableJdbcAuditing
static class AuditingConfiguration {
private static String currentAuditor;
static String currentAuditor;
@Bean
AuditorAware<String> auditorAware() {
return () -> Optional.ofNullable(currentAuditor);
}
}
@EnableJdbcAuditing(auditorAwareRef = "customAuditorAware", dateTimeProviderRef = "customDateTimeProvider", modifyOnCreate = false)
static class CustomizeAuditingConfiguration1 {
private static LocalDateTime currentDateTime;
@EnableJdbcAuditing(auditorAwareRef = "customAuditorAware", dateTimeProviderRef = "customDateTimeProvider",
modifyOnCreate = false)
static class CustomizeAuditorAwareAndDateTimeProvider {
static LocalDateTime currentDateTime;
@Bean
@Primary
AuditorAware<String> auditorAware() {
return () -> Optional.of("default user");
}
@Bean
AuditorAware<String> customAuditorAware() {
return () -> Optional.of("custom user");
}
@Bean
DateTimeProvider customDateTimeProvider() {
return () -> Optional.ofNullable(currentDateTime);
@ -220,11 +270,11 @@ public class EnableJdbcAuditingHsqlIntegrationTests { @@ -220,11 +270,11 @@ public class EnableJdbcAuditingHsqlIntegrationTests {
}
@EnableJdbcAuditing(setDates = false)
static class CustomizeAuditingConfiguration2 {
static class CustomizeAuditorAware {
@Bean
AuditorAware<String> auditorAware() {
return () -> Optional.of("user");
}
}
}

Loading…
Cancel
Save