Browse Source

DATAJDBC-454 - Redesigned events.

Ids are only contained if it can not be guaranteed that an entity is contained which applies to the delete events.
As a side effect Identifier got simplified into a single simple class.

Original pull request: #199.
pull/202/head
Jens Schauder 6 years ago committed by Mark Paluch
parent
commit
22f9eded60
No known key found for this signature in database
GPG Key ID: 51A00FA751B91849
  1. 41
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java
  2. 9
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java
  3. 232
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryManipulateDbActionsIntegrationTests.java
  4. 16
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
  5. 10
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java
  6. 20
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java
  7. 15
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadEvent.java
  8. 13
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveEvent.java
  9. 18
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeConvertEvent.java
  10. 15
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java
  11. 15
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveEvent.java
  12. 65
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/Identifier.java
  13. 71
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java
  14. 20
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java
  15. 26
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithEntity.java
  16. 52
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithId.java
  17. 39
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithIdAndEntity.java
  18. 41
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalSaveEvent.java
  19. 76
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/SimpleRelationalEvent.java
  20. 44
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/SpecifiedIdentifier.java
  21. 22
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithAggregateChange.java
  22. 7
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithEntity.java
  23. 9
      spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithId.java
  24. 57
      spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/IdentifierUnitTests.java

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

@ -17,7 +17,6 @@ package org.springframework.data.jdbc.core; @@ -17,7 +17,6 @@ package org.springframework.data.jdbc.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@ -40,7 +39,6 @@ import org.springframework.data.relational.core.conversion.RelationalEntityUpdat @@ -40,7 +39,6 @@ import org.springframework.data.relational.core.conversion.RelationalEntityUpdat
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.event.*;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@ -212,7 +210,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -212,7 +210,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
T entity = accessStrategy.findById(id, domainType);
if (entity != null) {
return triggerAfterLoad(id, entity);
return triggerAfterLoad(entity);
}
return entity;
}
@ -337,8 +335,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -337,8 +335,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
AggregateChange<T> change = changeCreator.apply(aggregateRoot);
aggregateRoot = triggerBeforeSave(aggregateRoot,
persistentEntity.getIdentifierAccessor(aggregateRoot).getIdentifier(), change);
aggregateRoot = triggerBeforeSave(aggregateRoot, change);
change.setEntity(aggregateRoot);
@ -348,7 +345,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -348,7 +345,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
Assert.notNull(identifier, "After saving the identifier must not be null!");
return triggerAfterSave(change.getEntity(), identifier, change);
return triggerAfterSave(change.getEntity(), change);
}
private <T> void deleteTree(Object id, @Nullable T entity, Class<T> domainType) {
@ -396,19 +393,15 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -396,19 +393,15 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
List<T> result = new ArrayList<>();
for (T e : all) {
RelationalPersistentEntity<?> entity = context.getRequiredPersistentEntity(e.getClass());
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(e);
result.add(triggerAfterLoad(identifierAccessor.getRequiredIdentifier(), e));
result.add(triggerAfterLoad(e));
}
return result;
}
private <T> T triggerAfterLoad(Object id, T entity) {
private <T> T triggerAfterLoad(T entity) {
publisher.publishEvent(new AfterLoadEvent(Identifier.of(id), entity));
publisher.publishEvent(new AfterLoadEvent(entity));
return entityCallbacks.callback(AfterLoadCallback.class, entity);
}
@ -417,33 +410,27 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -417,33 +410,27 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
return entityCallbacks.callback(BeforeConvertCallback.class, aggregateRoot);
}
private <T> T triggerBeforeSave(T aggregateRoot, @Nullable Object id, AggregateChange<T> change) {
private <T> T triggerBeforeSave(T aggregateRoot, AggregateChange<T> change) {
publisher.publishEvent(new BeforeSaveEvent( //
Identifier.ofNullable(id), //
//
aggregateRoot, //
change //
));
//
change));
return entityCallbacks.callback(BeforeSaveCallback.class, aggregateRoot, change);
}
private <T> T triggerAfterSave(T aggregateRoot, Object id, AggregateChange<T> change) {
private <T> T triggerAfterSave(T aggregateRoot, AggregateChange<T> change) {
Specified identifier = Identifier.of(id);
publisher.publishEvent(new AfterSaveEvent( //
identifier, //
aggregateRoot, //
change //
));
publisher.publishEvent(new AfterSaveEvent(aggregateRoot, change));
return entityCallbacks.callback(AfterSaveCallback.class, aggregateRoot);
}
private <T> void triggerAfterDelete(@Nullable T aggregateRoot, Object id, AggregateChange<?> change) {
publisher.publishEvent(new AfterDeleteEvent(Identifier.of(id), Optional.ofNullable(aggregateRoot), change));
publisher.publishEvent(new AfterDeleteEvent(Identifier.of(id), aggregateRoot, change));
if (aggregateRoot != null) {
entityCallbacks.callback(AfterDeleteCallback.class, aggregateRoot);
@ -453,7 +440,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { @@ -453,7 +440,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
@Nullable
private <T> T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, AggregateChange<?> change) {
publisher.publishEvent(new BeforeDeleteEvent(Identifier.of(id), Optional.ofNullable(aggregateRoot), change));
publisher.publishEvent(new BeforeDeleteEvent(Identifier.of(id), aggregateRoot, change));
if (aggregateRoot != null) {
return entityCallbacks.callback(BeforeDeleteCallback.class, aggregateRoot, change);

9
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java

@ -31,7 +31,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext @@ -31,7 +31,6 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.event.AfterLoadCallback;
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
import org.springframework.data.relational.core.mapping.event.Identifier;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.jdbc.core.ResultSetExtractor;
@ -213,10 +212,8 @@ class JdbcRepositoryQuery implements RepositoryQuery { @@ -213,10 +212,8 @@ class JdbcRepositoryQuery implements RepositoryQuery {
MapSqlParameterSource parameters = new MapSqlParameterSource();
queryMethod.getParameters().getBindableParameters().forEach(p -> {
convertAndAddParameter(parameters, p, objects[p.getIndex()]);
});
queryMethod.getParameters().getBindableParameters()
.forEach(p -> convertAndAddParameter(parameters, p, objects[p.getIndex()]));
return parameters;
}
@ -291,7 +288,7 @@ class JdbcRepositoryQuery implements RepositoryQuery { @@ -291,7 +288,7 @@ class JdbcRepositoryQuery implements RepositoryQuery {
Object identifier = e.getIdentifierAccessor(entity).getIdentifier();
if (identifier != null) {
publisher.publishEvent(new AfterLoadEvent(Identifier.of(identifier), entity));
publisher.publishEvent(new AfterLoadEvent(entity));
}
callbacks.callback(AfterLoadCallback.class, entity);

232
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryManipulateDbActionsIntegrationTests.java

@ -1,232 +0,0 @@ @@ -1,232 +0,0 @@
/*
* Copyright 2017-2020 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.repository;
import static java.util.Arrays.*;
import static org.assertj.core.api.Assertions.*;
import junit.framework.AssertionFailedError;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.List;
import java.util.Random;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.relational.core.conversion.DbAction;
import org.springframework.data.relational.core.mapping.event.BeforeDeleteEvent;
import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.repository.CrudRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
/**
* Tests that the event infrastructure of Spring Data JDBC is sufficient to manipulate the {@link DbAction}s to be
* executed against the database.
*
* @author Jens Schauder
* @author Greg Turnquist
*/
@ContextConfiguration
public class JdbcRepositoryManipulateDbActionsIntegrationTests {
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
@Autowired DummyEntityRepository repository;
@Autowired LogRepository logRepository;
@Test // DATAJDBC-120
public void softDelete() {
// given a persistent entity
DummyEntity entity = new DummyEntity(null, "Hello");
repository.save(entity);
assertThat(entity.id).isNotNull();
// when I delete the entity
repository.delete(entity);
// it is still in the repository, but marked as deleted
assertThat(repository.findById(entity.id)) //
.contains(new DummyEntity( //
entity.id, //
entity.name, //
true) //
);
}
@Test // DATAJDBC-120
public void softDeleteMany() {
// given persistent entities
DummyEntity one = new DummyEntity(null, "One");
DummyEntity two = new DummyEntity(null, "Two");
repository.saveAll(asList(one, two));
assertThat(one.id).isNotNull();
// when I delete the entities
repository.deleteAll(asList(one, two));
// they are still in the repository, but marked as deleted
assertThat(repository.findById(one.id)) //
.contains(new DummyEntity( //
one.id, //
one.name, //
true) //
);
assertThat(repository.findById(two.id)) //
.contains(new DummyEntity( //
two.id, //
two.name, //
true) //
);
}
@Test // DATAJDBC-120
public void loggingOnSave() {
// given a new entity
DummyEntity one = new DummyEntity(null, "one");
repository.save(one);
assertThat(one.id).isNotNull();
// they are still in the repository, but marked as deleted
assertThat(logRepository.findById(Config.lastLogId)) //
.isNotEmpty() //
.map(Log::getText) //
.contains("one saved");
}
@Test // DATAJDBC-120
public void loggingOnSaveMany() {
// given a new entity
DummyEntity one = new DummyEntity(null, "one");
DummyEntity two = new DummyEntity(null, "two");
repository.saveAll(asList(one, two));
assertThat(one.id).isNotNull();
// they are still in the repository, but marked as deleted
assertThat(logRepository.findById(Config.lastLogId)) //
.isNotEmpty() //
.map(Log::getText) //
.contains("two saved");
}
@Data
private static class DummyEntity {
@Id Long id;
String name;
boolean deleted;
DummyEntity(Long id, String name) {
this.id = id;
this.name = name;
this.deleted = false;
}
@PersistenceConstructor
DummyEntity(Long id, String name, boolean deleted) {
this.id = id;
this.name = name;
this.deleted = deleted;
}
}
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
@Getter
@Setter
@RequiredArgsConstructor
private static class Log {
@Id Long id;
DummyEntity entity;
String text;
}
private interface LogRepository extends CrudRepository<Log, Long> {}
@Configuration
@Import(TestConfiguration.class)
@EnableJdbcRepositories(considerNestedRepositories = true)
static class Config {
static long lastLogId;
@Bean
Class<?> testClass() {
return JdbcRepositoryManipulateDbActionsIntegrationTests.class;
}
@Bean
ApplicationListener<BeforeDeleteEvent> softDeleteListener() {
return event -> {
DummyEntity entity = (DummyEntity) event.getOptionalEntity().orElseThrow(AssertionFailedError::new);
entity.deleted = true;
List<DbAction<?>> actions = event.getChange().getActions();
actions.clear();
actions.add(new DbAction.UpdateRoot<>(entity));
};
}
@Bean
ApplicationListener<BeforeSaveEvent> logOnSaveListener() {
// this would actually be easier to implement with an AfterSaveEvent listener, but we want to test AggregateChange
// manipulation.
return event -> {
DummyEntity entity = (DummyEntity) event.getOptionalEntity().orElseThrow(AssertionFailedError::new);
lastLogId = new Random().nextLong();
Log log = new Log();
log.setId(lastLogId);
log.entity = entity;
log.text = entity.name + " saved";
List<DbAction<?>> actions = event.getChange().getActions();
actions.add(new DbAction.InsertRoot<>(log));
};
}
}
}

16
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java

@ -20,7 +20,6 @@ import static org.assertj.core.api.Assertions.*; @@ -20,7 +20,6 @@ import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import junit.framework.AssertionFailedError;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.With;
@ -57,11 +56,13 @@ import org.springframework.data.relational.core.mapping.event.BeforeDeleteEvent; @@ -57,11 +56,13 @@ import org.springframework.data.relational.core.mapping.event.BeforeDeleteEvent;
import org.springframework.data.relational.core.mapping.event.BeforeSaveEvent;
import org.springframework.data.relational.core.mapping.event.Identifier;
import org.springframework.data.relational.core.mapping.event.RelationalEvent;
import org.springframework.data.relational.core.mapping.event.WithId;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.lang.Nullable;
/**
* Unit tests for application events via {@link SimpleJdbcRepository}.
@ -143,14 +144,23 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -143,14 +144,23 @@ public class SimpleJdbcRepositoryEventsUnitTests {
assertThat(publisher.events).extracting( //
RelationalEvent::getClass, //
e -> e.getOptionalEntity().orElseGet(AssertionFailedError::new), //
RelationalEvent::getId //
this::getEntity, //
this::getId //
).containsExactly( //
Tuple.tuple(BeforeDeleteEvent.class, entity, Identifier.of(23L)), //
Tuple.tuple(AfterDeleteEvent.class, entity, Identifier.of(23L)) //
);
}
private Identifier getId(RelationalEvent e) {
return ((WithId) e).getId();
}
@Nullable
private Object getEntity(RelationalEvent e) {
return e.getEntity();
}
@Test // DATAJDBC-99
@SuppressWarnings("rawtypes")
public void publishesEventsOnDeleteById() {

10
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBeanUnitTests.java

@ -61,8 +61,7 @@ public class JdbcRepositoryFactoryBeanUnitTests { @@ -61,8 +61,7 @@ public class JdbcRepositoryFactoryBeanUnitTests {
@Mock DataAccessStrategy dataAccessStrategy;
@Mock ApplicationEventPublisher publisher;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) ListableBeanFactory beanFactory;
@Mock
Dialect dialect;
@Mock Dialect dialect;
RelationalMappingContext mappingContext;
@ -78,10 +77,11 @@ public class JdbcRepositoryFactoryBeanUnitTests { @@ -78,10 +77,11 @@ public class JdbcRepositoryFactoryBeanUnitTests {
ObjectProvider<DataAccessStrategy> provider = mock(ObjectProvider.class);
when(beanFactory.getBeanProvider(DataAccessStrategy.class)).thenReturn(provider);
when(provider.getIfAvailable(any())).then((Answer) invocation -> ((Supplier) invocation.getArgument(0)).get());
when(provider.getIfAvailable(any()))
.then((Answer<?>) invocation -> ((Supplier<?>) invocation.getArgument(0)).get());
}
@Test
@Test // DATAJDBC-151
public void setsUpBasicInstanceCorrectly() {
factoryBean.setDataAccessStrategy(dataAccessStrategy);
@ -95,7 +95,7 @@ public class JdbcRepositoryFactoryBeanUnitTests { @@ -95,7 +95,7 @@ public class JdbcRepositoryFactoryBeanUnitTests {
assertThat(factoryBean.getObject()).isNotNull();
}
@Test(expected = IllegalArgumentException.class)
@Test(expected = IllegalArgumentException.class) // DATAJDBC-151
public void requiresListableBeanFactory() {
factoryBean.setBeanFactory(mock(BeanFactory.class));

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

@ -15,28 +15,28 @@ @@ -15,28 +15,28 @@
*/
package org.springframework.data.relational.core.mapping.event;
import java.util.Optional;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
import org.springframework.lang.Nullable;
/**
* Gets published after deletion of an entity. It will have a {@link Specified} identifier. If the entity is empty or
* Gets published after deletion of an entity. It will have a {@link Identifier} identifier. If the entity is {@literal null} or
* not depends on the delete method used.
*
* @author Jens Schauder
*/
public class AfterDeleteEvent extends RelationalEventWithId {
public class AfterDeleteEvent extends RelationalDeleteEvent {
private static final long serialVersionUID = 3594807189931141582L;
private static final long serialVersionUID = 2615043444207870206L;
/**
* @param id of the entity.
* @param instance the deleted entity if it is available.
* @param id of the entity. Must not be {@literal null}.
* @param instance the deleted entity if it is available. May be {@literal null}.
* @param change the {@link AggregateChange} encoding the actions that were performed on the database as part of the
* delete operation.
* delete operation. Must not be {@literal null}.
*/
public AfterDeleteEvent(Specified id, Optional<?> instance, AggregateChange change) {
public AfterDeleteEvent(Identifier id, @Nullable Object instance, AggregateChange<?> change) {
super(id, instance, change);
}
}

15
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadEvent.java

@ -15,23 +15,20 @@ @@ -15,23 +15,20 @@
*/
package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
/**
* Gets published after instantiation and setting of all the properties of an entity. This allows to do some
* postprocessing of entities.
* postprocessing of entities if the entities are mutable. For immutable entities use {@link AfterLoadCallback}.
*
* @author Jens Schauder
*/
public class AfterLoadEvent extends RelationalEventWithIdAndEntity {
public class AfterLoadEvent extends RelationalEventWithEntity {
private static final long serialVersionUID = -4185777271143436728L;
private static final long serialVersionUID = 7343072117054666699L;
/**
* @param id of the entity
* @param entity the newly instantiated entity.
* @param entity the newly instantiated entity. Must not be {@literal null}.
*/
public AfterLoadEvent(Specified id, Object entity) {
super(id, entity, null);
public AfterLoadEvent(Object entity) {
super(entity);
}
}

13
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveEvent.java

@ -16,23 +16,22 @@ @@ -16,23 +16,22 @@
package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
/**
* Gets published after a new instance or a changed instance was saved in the database.
*
* @author Jens Schauder
*/
public class AfterSaveEvent extends RelationalEventWithIdAndEntity {
public class AfterSaveEvent extends RelationalSaveEvent {
private static final long serialVersionUID = 8982085767296982848L;
private static final long serialVersionUID = 1681164473866370396L;
/**
* @param id identifier of the saved entity.
* @param instance the saved entity.
* @param instance the saved entity. Must not be {@literal null}.
* @param change the {@link AggregateChange} encoding the actions performed on the database as part of the delete.
* Must not be {@literal null}.
*/
public AfterSaveEvent(Specified id, Object instance, AggregateChange change) {
super(id, instance, change);
public AfterSaveEvent(Object instance, AggregateChange<?> change) {
super(instance, change);
}
}

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

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
/**
* Gets published before an aggregate gets converted into a database change.
@ -24,16 +23,19 @@ import org.springframework.data.relational.core.mapping.event.Identifier.Specifi @@ -24,16 +23,19 @@ import org.springframework.data.relational.core.mapping.event.Identifier.Specifi
* @since 1.1
* @author Jens Schauder
*/
public class BeforeConvertEvent extends RelationalEventWithIdAndEntity {
public class BeforeConvertEvent extends RelationalSaveEvent {
private static final long serialVersionUID = 3980149746683849019L;
private static final long serialVersionUID = -5716795164911939224L;
/**
* @param id identifier of the saved entity.
* @param instance the saved entity.
* @param change the {@link AggregateChange} encoding the actions performed on the database as part of the delete.
* @param instance the saved entity. Must not be {@literal null}.
* @param change the {@link AggregateChange} encoding the actions to be performed on the database as change. Since
* this event is fired before the conversion the change is actually empty, but contains information if the
* aggregate is considered new in {@link AggregateChange#getKind()}. Must not be {@literal null}.
*/
public BeforeConvertEvent(Specified id, Object instance, AggregateChange change) {
super(id, instance, change);
public BeforeConvertEvent(Object instance, AggregateChange<?> change) {
super(instance, change);
}
}

15
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java

@ -15,10 +15,8 @@ @@ -15,10 +15,8 @@
*/
package org.springframework.data.relational.core.mapping.event;
import java.util.Optional;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
import org.springframework.lang.Nullable;
/**
* Gets published when an entity is about to get deleted. The contained {@link AggregateChange} is mutable and may be
@ -26,16 +24,17 @@ import org.springframework.data.relational.core.mapping.event.Identifier.Specifi @@ -26,16 +24,17 @@ import org.springframework.data.relational.core.mapping.event.Identifier.Specifi
*
* @author Jens Schauder
*/
public class BeforeDeleteEvent extends RelationalEventWithId {
public class BeforeDeleteEvent extends RelationalDeleteEvent {
private static final long serialVersionUID = -5483432053368496651L;
private static final long serialVersionUID = -137285843224094551L;
/**
* @param id the id of the entity
* @param entity the entity about to get deleted. Might be empty.
* @param id the id of the entity. Must not be {@literal null}.
* @param entity the entity about to get deleted. May be {@literal null}.
* @param change the {@link AggregateChange} encoding the planned actions to be performed on the database.
*/
public <T> BeforeDeleteEvent(Specified id, Optional<?> entity, AggregateChange change) {
public <T> BeforeDeleteEvent(Identifier id, @Nullable Object entity, AggregateChange<?> change) {
super(id, entity, change);
}
}

15
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveEvent.java

@ -23,16 +23,17 @@ import org.springframework.data.relational.core.conversion.AggregateChange; @@ -23,16 +23,17 @@ import org.springframework.data.relational.core.conversion.AggregateChange;
*
* @author Jens Schauder
*/
public class BeforeSaveEvent extends RelationalEventWithEntity {
public class BeforeSaveEvent extends RelationalSaveEvent{
private static final long serialVersionUID = -6996874391990315443L;
private static final long serialVersionUID = -4935804431519314116L;
/**
* @param id of the entity to be saved.
* @param instance the entity about to get saved.
* @param change the {@link AggregateChange} that is going to get applied to the database.
* @param instance the entity about to get saved. Must not be {@literal null}.
* @param change the {@link AggregateChange} that is going to get applied to the database. Must not be {@literal null}.
*
*/
public BeforeSaveEvent(Identifier id, Object instance, AggregateChange change) {
super(id, instance, change);
public BeforeSaveEvent(Object instance, AggregateChange<?> change) {
super(instance, change);
}
}

65
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/Identifier.java

@ -15,62 +15,59 @@ @@ -15,62 +15,59 @@
*/
package org.springframework.data.relational.core.mapping.event;
import java.util.Optional;
import java.util.Objects;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Wrapper for an identifier of an entity. Might either be a {@link Specified} or {@link Unset#UNSET}
* Wrapper for an identifier of an entity.
*
* @author Jens Schauder
*/
public interface Identifier {
public final class Identifier {
/**
* Creates a new {@link Specified} identifier for the given, non-null value.
*
* @param identifier must not be {@literal null}.
* @return will never be {@literal null}.
*/
static Specified of(Object identifier) {
private final Object value;
private Identifier(Object value) {
Assert.notNull(identifier, "Identifier must not be null!");
Assert.notNull(value, "Identifier must not be null!");
return SpecifiedIdentifier.of(identifier);
this.value = value;
}
/**
* Produces an {@link Identifier} of appropriate type depending the argument being {@code null} or not.
* Creates a new {@link Identifier} identifier for the given, non-null value.
*
* @param identifier May be {@code null}.
* @return an {@link Identifier}.
* @param identifier must not be {@literal null}.
* @return will never be {@literal null}.
*/
static Identifier ofNullable(@Nullable Object identifier) {
return identifier == null ? Unset.UNSET : of(identifier);
public static Identifier of(Object identifier) {
return new Identifier(identifier);
}
/**
* Returns the identifier value.
*
* @return will never be {@code null}.
* @return will never be {@literal null}.
*/
Optional<?> getOptionalValue();
public Object getValue() {
return value;
}
/**
* A specified identifier that exposes a definitely present identifier value.
*
* @author Oliver Gierke
*/
interface Specified extends Identifier {
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Identifier that = (Identifier) o;
return value.equals(that.value);
}
/**
* Returns the identifier value.
*
* @return will never be {@literal null}.
*/
default Object getValue() {
return getOptionalValue().orElseThrow(() -> new IllegalStateException("Should not happen!"));
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}

71
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
/*
* Copyright 2017-2020 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.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Super class for events produced during deleting an aggregate. Such events have an {@link Identifier} and an
* {@link AggregateChange} and may also have an entity if the entity was provided to the method performing the delete.
*
* @author Jens Schauder
*/
abstract public class RelationalDeleteEvent extends ApplicationEvent implements WithId, WithAggregateChange {
private static final long serialVersionUID = -8071323168471611098L;
private final Identifier id;
@Nullable private final Object entity;
private final AggregateChange<?> change;
/**
* @param id the identifier of the aggregate that gets deleted. Must not be {@literal null}.
* @param entity is the aggregate root that gets deleted. Might be {@literal null}.
* @param change the {@link AggregateChange} for the deletion containing more detailed information about the deletion
* process.
*/
RelationalDeleteEvent(Identifier id, @Nullable Object entity, AggregateChange<?> change) {
super(id);
Assert.notNull(id, "Id must not be null.");
Assert.notNull(change, "Change must not be null.");
this.id = id;
this.entity = entity;
this.change = change;
}
@Override
public Identifier getId() {
return id;
}
@Override
@Nullable
public Object getEntity() {
return entity;
}
@Override
public AggregateChange<?> getAggregateChange() {
return change;
}
}

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

@ -15,28 +15,20 @@ @@ -15,28 +15,20 @@
*/
package org.springframework.data.relational.core.mapping.event;
import org.springframework.lang.Nullable;
import java.util.Optional;
/**
* an event signalling JDBC processing. It offers access to an {@link Identifier} of the aggregate root affected by the
* event.
* an event signalling JDBC processing.
*
* @author Oliver Gierke
*/
public interface RelationalEvent {
/**
* The identifier of the aggregate root, triggering this event.
*
* @return the source of the event as an {@link Identifier}. Guaranteed to be not {@code null}.
* @return the entity to which this event refers. Might be {@literal null}.
*/
Identifier getId();
/**
* Returns the aggregate root the event was triggered for.
*
* @return will never be {@code null}.
*/
Optional<Object> getOptionalEntity();
@Nullable
Object getEntity();
}

26
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithEntity.java

@ -15,20 +15,30 @@ @@ -15,20 +15,30 @@
*/
package org.springframework.data.relational.core.mapping.event;
import java.util.Optional;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.context.ApplicationEvent;
/**
* A {@link SimpleRelationalEvent} which is guaranteed to have an entity.
*
* An event that is guaranteed to have an entity.
*
* @author Jens Schauder
*/
public class RelationalEventWithEntity extends SimpleRelationalEvent implements WithEntity {
public class RelationalEventWithEntity extends ApplicationEvent implements WithEntity {
private static final long serialVersionUID = 4891455396602090638L;
private final Object entity;
RelationalEventWithEntity(Object entity) {
super(entity);
this.entity = entity;
}
RelationalEventWithEntity(Identifier id, Object entity, AggregateChange change) {
super(id, Optional.of(entity), change);
/**
* @return the entity to which this event refers. Guaranteed to be not {@literal null}.
*/
@Override
public Object getEntity() {
return entity;
}
}

52
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithId.java

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
/*
* Copyright 2017-2020 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.event;
import java.util.Optional;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
import org.springframework.lang.Nullable;
/**
* A {@link SimpleRelationalEvent} guaranteed to have an identifier.
*
* @author Jens Schauder
*/
public class RelationalEventWithId extends SimpleRelationalEvent implements WithId {
private static final long serialVersionUID = -8071323168471611098L;
private final Specified id;
public RelationalEventWithId(Specified id, Optional<?> entity, @Nullable AggregateChange change) {
super(id, entity, change);
this.id = id;
}
/**
* Events with an identifier will always return a {@link Specified} one.
*
* @deprecated since 1.1, obtain the id from the entity instead.
*/
@Override
@Deprecated
public Specified getId() {
return id;
}
}

39
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithIdAndEntity.java

@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
/*
* Copyright 2017-2020 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.event;
import lombok.Getter;
import java.util.Optional;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
import org.springframework.lang.Nullable;
/**
* A {@link SimpleRelationalEvent} which is guaranteed to have an identifier and an entity.
*
* @author Jens Schauder
*/
@Getter
public class RelationalEventWithIdAndEntity extends RelationalEventWithId implements WithEntity {
private static final long serialVersionUID = -3194462549552515519L;
public RelationalEventWithIdAndEntity(Specified id, Object entity, @Nullable AggregateChange change) {
super(id, Optional.of(entity), change);
}
}

41
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalSaveEvent.java

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
package org.springframework.data.relational.core.mapping.event;/*
* Copyright 2020 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.
*/
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.util.Assert;
/**
* Events triggered during saving of an aggregate.
* Events of this type always have an {@link AggregateChange} and an entity.
*/
public abstract class RelationalSaveEvent extends RelationalEventWithEntity implements WithAggregateChange{
private final AggregateChange<?> change;
RelationalSaveEvent(Object entity, AggregateChange<?> change) {
super(entity);
Assert.notNull(change, "Change must not be null");
this.change = change;
}
@Override
public AggregateChange<?> getAggregateChange() {
return change;
}
}

76
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/SimpleRelationalEvent.java

@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
/*
* Copyright 2017-2020 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.event;
import java.util.Optional;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.relational.core.conversion.AggregateChange;
import org.springframework.lang.Nullable;
/**
* The common superclass for all events published by JDBC repositories. {@link #getSource} contains the
* {@link Identifier} of the entity triggering the event.
*
* @author Jens Schauder
* @author Oliver Gierke
*/
class SimpleRelationalEvent extends ApplicationEvent implements RelationalEvent {
private static final long serialVersionUID = -1798807778668751659L;
private final Object entity;
private final AggregateChange change;
SimpleRelationalEvent(Identifier id, Optional<?> entity, @Nullable AggregateChange change) {
super(id);
this.entity = entity.orElse(null);
this.change = change;
}
/**
* @deprecated since 1.1, obtain the id from the entity instead.
*/
@Override
@Deprecated
public Identifier getId() {
return (Identifier) getSource();
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.mapping.event.JdbcEvent#getOptionalEntity()
*/
@Override
public Optional<Object> getOptionalEntity() {
return Optional.ofNullable(entity);
}
/**
* Returns the an {@link AggregateChange} instance representing the SQL statements performed by the action that
* triggered this event.
*
* @return Guaranteed to be not {@literal null}.
* @deprecated There is currently no replacement for this. If something like this is required please create an issue
* outlining your use case.
*/
@Deprecated
public AggregateChange getChange() {
return change;
}
}

44
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/SpecifiedIdentifier.java

@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
/*
* Copyright 2017-2020 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.event;
import lombok.NonNull;
import lombok.Value;
import java.util.Optional;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
/**
* Simple value object for {@link Specified}.
*
* @author Jens Schauder
* @author Oliver Gierke
*/
@Value(staticConstructor = "of")
class SpecifiedIdentifier implements Specified {
@NonNull Object value;
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.mapping.event.Identifier#getOptionalValue()
*/
@Override
public Optional<?> getOptionalValue() {
return Optional.of(value);
}
}

22
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/Unset.java → spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithAggregateChange.java

@ -15,24 +15,16 @@ @@ -15,24 +15,16 @@
*/
package org.springframework.data.relational.core.mapping.event;
import java.util.Optional;
import org.springframework.data.relational.core.conversion.AggregateChange;
/**
* An unset identifier. Always returns {@link Optional#empty()} as value.
*
* @author Jens Schaude
* @author Oliver Gierke
* {@link RelationalEvent} that represents a change to an aggregate and therefore has an {@link AggregateChange}
* @author Jens Schauder
*/
enum Unset implements Identifier {
UNSET;
public interface WithAggregateChange extends RelationalEvent {
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.core.mapping.event.Identifier#getOptionalValue()
/**
* @return Guaranteed to be not {@literal null}.
*/
@Override
public Optional<Object> getOptionalValue() {
return Optional.empty();
}
AggregateChange<?> getAggregateChange();
}

7
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithEntity.java

@ -16,8 +16,7 @@ @@ -16,8 +16,7 @@
package org.springframework.data.relational.core.mapping.event;
/**
* Interface for {@link SimpleRelationalEvent}s which are guaranteed to have an entity. Allows direct access to that
* entity, without going through an {@link java.util.Optional}
* Interface for events which are guaranteed to have an entity.
*
* @author Jens Schauder
*/
@ -26,7 +25,5 @@ public interface WithEntity extends RelationalEvent { @@ -26,7 +25,5 @@ public interface WithEntity extends RelationalEvent {
/**
* @return will never be {@literal null}.
*/
default Object getEntity() {
return getOptionalEntity().orElseThrow(() -> new IllegalStateException("Entity must not be NULL"));
}
Object getEntity();
}

9
spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithId.java

@ -15,18 +15,15 @@ @@ -15,18 +15,15 @@
*/
package org.springframework.data.relational.core.mapping.event;
import org.springframework.data.relational.core.mapping.event.Identifier.Specified;
/**
* Interface for {@link SimpleRelationalEvent}s which are guaranteed to have a {@link Specified} identifier. Offers
* direct access to the {@link Specified} identifier.
* Interface for {@link RelationalEvent}s which have an {@link Identifier} but might not have an entity.
*
* @author Jens Schauder
*/
public interface WithId extends RelationalEvent {
/**
* Events with an identifier will always return a {@link Specified} one.
* Events with an identifier will always return a {@link Identifier} one.
*/
Specified getId();
Identifier getId();
}

57
spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/IdentifierUnitTests.java

@ -1,57 +0,0 @@ @@ -1,57 +0,0 @@
/*
* Copyright 2018-2020 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.event;
import static org.assertj.core.api.Assertions.*;
import java.util.Optional;
import org.junit.Test;
/**
* Unit tests for {@link Identifier}
*
* @author Jens Schauder
*/
public class IdentifierUnitTests {
@SuppressWarnings("unchecked")
@Test
public void specifiedOffersTheIdentifierValue() {
Identifier.Specified identifier = Identifier.of("x");
assertThat(identifier.getValue()).isEqualTo("x");
assertThat((Optional<Object>) identifier.getOptionalValue()).contains("x");
}
@Test
public void indentifierOfNullHasEmptyValue() {
Identifier identifier = Identifier.ofNullable(null);
assertThat(identifier.getOptionalValue()).isEmpty();
}
@SuppressWarnings("unchecked")
@Test
public void indentifierOfXHasValueX() {
Identifier identifier = Identifier.ofNullable("x");
assertThat((Optional<Object>) identifier.getOptionalValue()).hasValue("x");
}
}
Loading…
Cancel
Save