Introducing AfterConvertCallback/Event.

This is to replace the AfterLoadCallback/Event in order to match the naming of other store modules.

AfterLoadCallback/Event is still in place for now, but deprecated.

Closes #1053
Original pull request: #1060.
This commit is contained in:
Jens Schauder
2021-09-24 09:13:11 +02:00
committed by Mark Paluch
parent b02ee3c083
commit 3cd25ee01f
12 changed files with 154 additions and 25 deletions

View File

@@ -208,7 +208,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
T entity = accessStrategy.findById(id, domainType);
if (entity != null) {
return triggerAfterLoad(entity);
return triggerAfterConvert(entity);
}
return entity;
}
@@ -236,7 +236,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
Assert.notNull(domainType, "Domain type must not be null!");
Iterable<T> all = accessStrategy.findAll(domainType, sort);
return triggerAfterLoad(all);
return triggerAfterConvert(all);
}
/*
@@ -248,7 +248,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
Assert.notNull(domainType, "Domain type must not be null!");
Iterable<T> items = triggerAfterLoad(accessStrategy.findAll(domainType, pageable));
Iterable<T> items = triggerAfterConvert(accessStrategy.findAll(domainType, pageable));
List<T> content = StreamSupport.stream(items.spliterator(), false).collect(Collectors.toList());
return PageableExecutionUtils.getPage(content, pageable, () -> accessStrategy.count(domainType));
@@ -264,7 +264,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
Assert.notNull(domainType, "Domain type must not be null!");
Iterable<T> all = accessStrategy.findAll(domainType);
return triggerAfterLoad(all);
return triggerAfterConvert(all);
}
/*
@@ -278,7 +278,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
Assert.notNull(domainType, "Domain type must not be null!");
Iterable<T> allById = accessStrategy.findAllById(ids, domainType);
return triggerAfterLoad(allById);
return triggerAfterConvert(allById);
}
/*
@@ -385,22 +385,24 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
return aggregateChange;
}
private <T> Iterable<T> triggerAfterLoad(Iterable<T> all) {
private <T> Iterable<T> triggerAfterConvert(Iterable<T> all) {
List<T> result = new ArrayList<>();
for (T e : all) {
result.add(triggerAfterLoad(e));
result.add(triggerAfterConvert(e));
}
return result;
}
private <T> T triggerAfterLoad(T entity) {
private <T> T triggerAfterConvert(T entity) {
publisher.publishEvent(new AfterLoadEvent<>(entity));
publisher.publishEvent(new AfterConvertEvent<>(entity));
return entityCallbacks.callback(AfterLoadCallback.class, entity);
entity = entityCallbacks.callback(AfterLoadCallback.class, entity);
return entityCallbacks.callback(AfterConvertCallback.class, entity);
}
private <T> T triggerBeforeConvert(T aggregateRoot) {

View File

@@ -32,6 +32,8 @@ import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.event.AfterConvertCallback;
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
import org.springframework.data.relational.core.mapping.event.AfterLoadCallback;
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
import org.springframework.data.repository.core.NamedQueries;
@@ -157,9 +159,11 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy {
if (entity != null) {
publisher.publishEvent(new AfterLoadEvent<>(entity));
publisher.publishEvent(new AfterConvertEvent<>(entity));
if (callbacks != null) {
return callbacks.callback(AfterLoadCallback.class, entity);
entity = callbacks.callback(AfterLoadCallback.class, entity);
return callbacks.callback(AfterConvertCallback.class, entity);
}
}

View File

@@ -41,6 +41,7 @@ import org.springframework.data.relational.core.conversion.MutableAggregateChang
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.event.AfterConvertCallback;
import org.springframework.data.relational.core.mapping.event.AfterDeleteCallback;
import org.springframework.data.relational.core.mapping.event.AfterLoadCallback;
import org.springframework.data.relational.core.mapping.event.AfterSaveCallback;
@@ -136,7 +137,9 @@ public class JdbcAggregateTemplateUnitTests {
when(dataAccessStrategy.findAll(SampleEntity.class)).thenReturn(asList(alfred1, neumann1));
when(callbacks.callback(any(Class.class), eq(alfred1), any())).thenReturn(alfred2);
when(callbacks.callback(any(Class.class), eq(alfred2), any())).thenReturn(alfred2);
when(callbacks.callback(any(Class.class), eq(neumann1), any())).thenReturn(neumann2);
when(callbacks.callback(any(Class.class), eq(neumann2), any())).thenReturn(neumann2);
Iterable<SampleEntity> all = template.findAll(SampleEntity.class);
@@ -158,12 +161,16 @@ public class JdbcAggregateTemplateUnitTests {
when(dataAccessStrategy.findAll(SampleEntity.class, Sort.by("name"))).thenReturn(asList(alfred1, neumann1));
when(callbacks.callback(any(Class.class), eq(alfred1), any())).thenReturn(alfred2);
when(callbacks.callback(any(Class.class), eq(alfred2), any())).thenReturn(alfred2);
when(callbacks.callback(any(Class.class), eq(neumann1), any())).thenReturn(neumann2);
when(callbacks.callback(any(Class.class), eq(neumann2), any())).thenReturn(neumann2);
Iterable<SampleEntity> all = template.findAll(SampleEntity.class, Sort.by("name"));
verify(callbacks).callback(AfterLoadCallback.class, alfred1);
verify(callbacks).callback(AfterConvertCallback.class, alfred2);
verify(callbacks).callback(AfterLoadCallback.class, neumann1);
verify(callbacks).callback(AfterConvertCallback.class, neumann2);
assertThat(all).containsExactly(alfred2, neumann2);
}
@@ -180,12 +187,16 @@ public class JdbcAggregateTemplateUnitTests {
when(dataAccessStrategy.findAll(SampleEntity.class, PageRequest.of(0, 20))).thenReturn(asList(alfred1, neumann1));
when(callbacks.callback(any(Class.class), eq(alfred1), any())).thenReturn(alfred2);
when(callbacks.callback(any(Class.class), eq(alfred2), any())).thenReturn(alfred2);
when(callbacks.callback(any(Class.class), eq(neumann1), any())).thenReturn(neumann2);
when(callbacks.callback(any(Class.class), eq(neumann2), any())).thenReturn(neumann2);
Iterable<SampleEntity> all = template.findAll(SampleEntity.class, PageRequest.of(0, 20));
verify(callbacks).callback(AfterLoadCallback.class, alfred1);
verify(callbacks).callback(AfterConvertCallback.class, alfred2);
verify(callbacks).callback(AfterLoadCallback.class, neumann1);
verify(callbacks).callback(AfterConvertCallback.class, neumann2);
assertThat(all).containsExactly(alfred2, neumann2);
}

View File

@@ -59,6 +59,7 @@ import org.springframework.data.jdbc.testing.EnabledOnFeature;
import org.springframework.data.jdbc.testing.TestConfiguration;
import org.springframework.data.jdbc.testing.TestDatabaseFeatures;
import org.springframework.data.relational.core.mapping.event.AbstractRelationalEvent;
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.core.NamedQueries;
@@ -309,7 +310,7 @@ public class JdbcRepositoryIntegrationTests {
repository.findAllWithSql();
assertThat(eventListener.events).hasSize(1).hasOnlyElementsOfType(AfterLoadEvent.class);
assertThat(eventListener.events).hasSize(2).hasOnlyElementsOfTypes(AfterLoadEvent.class, AfterConvertEvent.class);
}
@Test // DATAJDBC-318

View File

@@ -51,15 +51,7 @@ import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.dialect.H2Dialect;
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.event.AfterDeleteEvent;
import org.springframework.data.relational.core.mapping.event.AfterLoadEvent;
import org.springframework.data.relational.core.mapping.event.AfterSaveEvent;
import org.springframework.data.relational.core.mapping.event.BeforeConvertEvent;
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.relational.core.mapping.event.*;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -198,7 +190,9 @@ public class SimpleJdbcRepositoryEventsUnitTests {
.extracting(e -> (Class) e.getClass()) //
.containsExactly( //
AfterLoadEvent.class, //
AfterLoadEvent.class //
AfterConvertEvent.class, //
AfterLoadEvent.class, //
AfterConvertEvent.class //
);
}
@@ -217,7 +211,9 @@ public class SimpleJdbcRepositoryEventsUnitTests {
.extracting(e -> (Class) e.getClass()) //
.containsExactly( //
AfterLoadEvent.class, //
AfterLoadEvent.class //
AfterConvertEvent.class, //
AfterLoadEvent.class, //
AfterConvertEvent.class //
);
}
@@ -234,7 +230,8 @@ public class SimpleJdbcRepositoryEventsUnitTests {
assertThat(publisher.events) //
.extracting(e -> (Class) e.getClass()) //
.containsExactly( //
AfterLoadEvent.class //
AfterLoadEvent.class, //
AfterConvertEvent.class //
);
}
@@ -253,7 +250,9 @@ public class SimpleJdbcRepositoryEventsUnitTests {
.extracting(e -> (Class) e.getClass()) //
.containsExactly( //
AfterLoadEvent.class, //
AfterLoadEvent.class //
AfterConvertEvent.class, //
AfterLoadEvent.class, //
AfterConvertEvent.class //
);
}
@@ -273,7 +272,9 @@ public class SimpleJdbcRepositoryEventsUnitTests {
.extracting(e -> (Class) e.getClass()) //
.containsExactly( //
AfterLoadEvent.class, //
AfterLoadEvent.class //
AfterConvertEvent.class, //
AfterLoadEvent.class, //
AfterConvertEvent.class //
);
}

View File

@@ -57,6 +57,8 @@ public class AbstractRelationalEventListener<E> implements ApplicationListener<A
if (event instanceof AfterLoadEvent) {
onAfterLoad((AfterLoadEvent<E>) event);
} else if (event instanceof AfterConvertEvent) {
onAfterConvert((AfterConvertEvent<E>) event);
} else if (event instanceof AfterDeleteEvent) {
onAfterDelete((AfterDeleteEvent<E>) event);
} else if (event instanceof AfterSaveEvent) {
@@ -110,6 +112,7 @@ public class AbstractRelationalEventListener<E> implements ApplicationListener<A
* Captures {@link AfterLoadEvent}.
*
* @param event will never be {@literal null}.
* @deprecated use {@link #onAfterConvert(AfterConvertEvent)} instead.
*/
protected void onAfterLoad(AfterLoadEvent<E> event) {
@@ -118,6 +121,18 @@ public class AbstractRelationalEventListener<E> implements ApplicationListener<A
}
}
/**
* Captures {@link AfterConvertEvent}.
*
* @param event will never be {@literal null}.
*/
protected void onAfterConvert(AfterConvertEvent<E> event) {
if (LOG.isDebugEnabled()) {
LOG.debug("onAfterConvert({})", event.getEntity());
}
}
/**
* Captures {@link AfterDeleteEvent}.
*

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2019-2021 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.data.mapping.callback.EntityCallback;
/**
* An {@link EntityCallback} that gets invoked after an aggregate was converted from the database into an entity.
*
* @author Jens Schauder
* @since 2.6
*/
@FunctionalInterface
public interface AfterConvertCallback<T> extends EntityCallback<T> {
/**
* Entity callback method invoked after an aggregate root was converted. Can return either the same or a modified
* instance of the domain object.
*
* @param aggregate the converted aggregate.
* @return the converted and possibly modified aggregate.
*/
T onAfterConvert(T aggregate);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2017-2021 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;
/**
* Gets published after instantiation and setting of all the properties of an entity. This allows to do some
* postprocessing of entities if the entities are mutable. For immutable entities use {@link AfterConvertCallback}.
*
* @author Jens Schauder
* @since 2.6
*/
public class AfterConvertEvent<E> extends RelationalEventWithEntity<E> {
private static final long serialVersionUID = 7343072117054666699L;
/**
* @param entity the newly instantiated entity. Must not be {@literal null}.
*/
public AfterConvertEvent(E entity) {
super(entity);
}
}

View File

@@ -23,7 +23,9 @@ import org.springframework.data.mapping.callback.EntityCallback;
* @author Jens Schauder
* @author Mark Paluch
* @since 1.1
* @deprecated Use {@link AfterConvertCallback} instead.
*/
@Deprecated
@FunctionalInterface
public interface AfterLoadCallback<T> extends EntityCallback<T> {

View File

@@ -20,7 +20,9 @@ package org.springframework.data.relational.core.mapping.event;
* postprocessing of entities if the entities are mutable. For immutable entities use {@link AfterLoadCallback}.
*
* @author Jens Schauder
* @deprecated Use {@link AfterConvertEvent} instead.
*/
@Deprecated
public class AfterLoadEvent<E> extends RelationalEventWithEntity<E> {
private static final long serialVersionUID = 7343072117054666699L;

View File

@@ -43,6 +43,14 @@ public class AbstractRelationalEventListenerUnitTests {
assertThat(events).containsExactly("afterLoad");
}
@Test // GH-1053
public void afterConvert() {
listener.onApplicationEvent(new AfterConvertEvent<>(dummyEntity));
assertThat(events).containsExactly("afterConvert");
}
@Test // DATAJDBC-454
public void beforeConvert() {
@@ -122,6 +130,11 @@ public class AbstractRelationalEventListenerUnitTests {
events.add("afterLoad");
}
@Override
protected void onAfterConvert(AfterConvertEvent<DummyEntity> event) {
events.add("afterConvert");
}
@Override
protected void onAfterDelete(AfterDeleteEvent<DummyEntity> event) {
events.add("afterDelete");

View File

@@ -871,6 +871,9 @@ The following table describes the available events:
| 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 properties get set. _Note: This is deprecated. Use `AfterConvert` instead_
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterConvertEvent.html[`AfterConvertEvent`]
| After an aggregate root gets created from a database `ResultSet` and all its properties get set.
|===
@@ -903,6 +906,9 @@ This is the correct callback if you want to set an id programmatically.
| 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. _This is deprecated, use `AfterConvertCallback` instead_
| {javadoc-base}org/springframework/data/relational/core/mapping/event/AfterConvertCallback.html[`AfterConvertCallback`]
| After an aggregate root gets created from a database `ResultSet` and all its property get set.
|===