diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java index 8eae5681..36d2cc34 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java @@ -401,7 +401,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { private T triggerAfterLoad(T entity) { - publisher.publishEvent(new AfterLoadEvent(entity)); + publisher.publishEvent(new AfterLoadEvent<>(entity)); return entityCallbacks.callback(AfterLoadCallback.class, entity); } @@ -412,25 +412,21 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { private T triggerBeforeSave(T aggregateRoot, AggregateChange change) { - publisher.publishEvent(new BeforeSaveEvent( // - // - aggregateRoot, // - // - change)); + publisher.publishEvent(new BeforeSaveEvent<>(aggregateRoot, change)); return entityCallbacks.callback(BeforeSaveCallback.class, aggregateRoot, change); } private T triggerAfterSave(T aggregateRoot, AggregateChange change) { - publisher.publishEvent(new AfterSaveEvent(aggregateRoot, change)); + publisher.publishEvent(new AfterSaveEvent<>(aggregateRoot, change)); return entityCallbacks.callback(AfterSaveCallback.class, aggregateRoot); } - private void triggerAfterDelete(@Nullable T aggregateRoot, Object id, AggregateChange change) { + private void triggerAfterDelete(@Nullable T aggregateRoot, Object id, AggregateChange change) { - publisher.publishEvent(new AfterDeleteEvent(Identifier.of(id), aggregateRoot, change)); + publisher.publishEvent(new AfterDeleteEvent<>(Identifier.of(id), aggregateRoot, change)); if (aggregateRoot != null) { entityCallbacks.callback(AfterDeleteCallback.class, aggregateRoot); @@ -438,9 +434,9 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { } @Nullable - private T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, AggregateChange change) { + private T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, AggregateChange change) { - publisher.publishEvent(new BeforeDeleteEvent(Identifier.of(id), aggregateRoot, change)); + publisher.publishEvent(new BeforeDeleteEvent<>(Identifier.of(id), aggregateRoot, change)); if (aggregateRoot != null) { return entityCallbacks.callback(BeforeDeleteCallback.class, aggregateRoot, change); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEvent.java new file mode 100644 index 00000000..bf0ff1ca --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEvent.java @@ -0,0 +1,39 @@ +/* + * 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. + */ +package org.springframework.data.relational.core.mapping.event; + +import org.springframework.context.ApplicationEvent; + +/** + * Base class for mapping events of Spring Data Relational + * + * @param the type this event refers to. + * @author Mark Paluch + * @author Jens Schauder + * @since 2.0 + */ +public abstract class AbstractRelationalEvent extends ApplicationEvent implements RelationalEvent { + + /** + * Creates an event with the given source. + * The source might be an entity or an id of an entity, depending on the actual event subclass. + * + * @param source must not be {@literal null}. + */ + public AbstractRelationalEvent(Object source) { + super(source); + } +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEventListener.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEventListener.java new file mode 100644 index 00000000..ee838480 --- /dev/null +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEventListener.java @@ -0,0 +1,143 @@ +/* + * 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. + */ +package org.springframework.data.relational.core.mapping.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.core.GenericTypeResolver; + +/** + * Base class to implement domain class specific {@link ApplicationListener} classes. + * + * @param + * @since 2.0 + */ +public class AbstractRelationalEventListener implements ApplicationListener> { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractRelationalEventListener.class); + private final Class domainClass; + + /** + * Creates a new {@link AbstractRelationalEventListener}. + */ + public AbstractRelationalEventListener() { + + Class typeArgument = GenericTypeResolver.resolveTypeArgument(this.getClass(), + AbstractRelationalEventListener.class); + this.domainClass = typeArgument == null ? Object.class : typeArgument; + } + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + @SuppressWarnings("unchecked") + @Override + public void onApplicationEvent(AbstractRelationalEvent event) { + + if (!domainClass.isAssignableFrom(event.getType())) { + return; + } + + if (event instanceof AfterLoadEvent) { + onAfterLoad((AfterLoadEvent) event); + } else if (event instanceof AfterDeleteEvent) { + onAfterDelete((AfterDeleteEvent) event); + } else if (event instanceof AfterSaveEvent) { + onAfterSave((AfterSaveEvent) event); + } else if (event instanceof BeforeConvertEvent) { + onBeforeConvert((BeforeConvertEvent) event); + } else if (event instanceof BeforeDeleteEvent) { + onBeforeDelete((BeforeDeleteEvent) event); + } else if (event instanceof BeforeSaveEvent) { + onBeforeSave((BeforeSaveEvent) event); + } + } + + /** + * Captures {@link BeforeConvertEvent}. + * + * @param event never {@literal null}. + */ + protected void onBeforeConvert(BeforeConvertEvent event) { + + if (LOG.isDebugEnabled()) { + LOG.debug("onBeforeConvert({})", event.getEntity()); + } + } + + /** + * Captures {@link BeforeSaveEvent}. + * + * @param event will never be {@literal null}. + */ + protected void onBeforeSave(BeforeSaveEvent event) { + + if (LOG.isDebugEnabled()) { + LOG.debug("onBeforeSave({})", event.getAggregateChange()); + } + } + + /** + * Captures {@link AfterSaveEvent}. + * + * @param event will never be {@literal null}. + */ + protected void onAfterSave(AfterSaveEvent event) { + + if (LOG.isDebugEnabled()) { + LOG.debug("onAfterSave({})", event.getAggregateChange()); + } + } + + /** + * Captures {@link AfterLoadEvent}. + * + * @param event will never be {@literal null}. + */ + protected void onAfterLoad(AfterLoadEvent event) { + + if (LOG.isDebugEnabled()) { + LOG.debug("onAfterLoad({})", event.getEntity()); + } + } + + /** + * Captures {@link AfterDeleteEvent}. + * + * @param event will never be {@literal null}. + */ + protected void onAfterDelete(AfterDeleteEvent event) { + + if (LOG.isDebugEnabled()) { + LOG.debug("onAfterDelete({})", event.getAggregateChange()); + } + } + + /** + * Capture {@link BeforeDeleteEvent}. + * + * @param event will never be {@literal null}. + */ + protected void onBeforeDelete(BeforeDeleteEvent event) { + + if (LOG.isDebugEnabled()) { + LOG.debug("onBeforeDelete({})", event.getAggregateChange()); + } + } + +} diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java index b87e752f..93245339 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteEvent.java @@ -23,8 +23,9 @@ import org.springframework.lang.Nullable; * not depends on the delete method used. * * @author Jens Schauder + * @since 2.0 */ -public class AfterDeleteEvent extends RelationalDeleteEvent { +public class AfterDeleteEvent extends RelationalDeleteEvent { private static final long serialVersionUID = 2615043444207870206L; @@ -34,7 +35,7 @@ public class AfterDeleteEvent extends RelationalDeleteEvent { * @param change the {@link AggregateChange} encoding the actions that were performed on the database as part of the * delete operation. Must not be {@literal null}. */ - public AfterDeleteEvent(Identifier id, @Nullable Object instance, AggregateChange change) { + public AfterDeleteEvent(Identifier id, @Nullable E instance, AggregateChange change) { super(id, instance, change); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadEvent.java index a39bd257..22eaa0d8 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadEvent.java @@ -21,14 +21,14 @@ package org.springframework.data.relational.core.mapping.event; * * @author Jens Schauder */ -public class AfterLoadEvent extends RelationalEventWithEntity { +public class AfterLoadEvent extends RelationalEventWithEntity { private static final long serialVersionUID = 7343072117054666699L; /** * @param entity the newly instantiated entity. Must not be {@literal null}. */ - public AfterLoadEvent(Object entity) { + public AfterLoadEvent(E entity) { super(entity); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveEvent.java index 36dda0e4..f761b1b2 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveEvent.java @@ -22,7 +22,7 @@ import org.springframework.data.relational.core.conversion.AggregateChange; * * @author Jens Schauder */ -public class AfterSaveEvent extends RelationalSaveEvent { +public class AfterSaveEvent extends RelationalSaveEvent { private static final long serialVersionUID = 1681164473866370396L; @@ -31,7 +31,7 @@ public class AfterSaveEvent extends RelationalSaveEvent { * @param change the {@link AggregateChange} encoding the actions performed on the database as part of the delete. * Must not be {@literal null}. */ - public AfterSaveEvent(Object instance, AggregateChange change) { + public AfterSaveEvent(E instance, AggregateChange change) { super(instance, change); } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeConvertEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeConvertEvent.java index d99a00fc..c3611778 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeConvertEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeConvertEvent.java @@ -23,7 +23,7 @@ import org.springframework.data.relational.core.conversion.AggregateChange; * @since 1.1 * @author Jens Schauder */ -public class BeforeConvertEvent extends RelationalSaveEvent { +public class BeforeConvertEvent extends RelationalSaveEvent { private static final long serialVersionUID = -5716795164911939224L; @@ -33,7 +33,7 @@ public class BeforeConvertEvent extends RelationalSaveEvent { * 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(Object instance, AggregateChange change) { + public BeforeConvertEvent(E instance, AggregateChange change) { super(instance, change); diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java index f9509093..f836a6f2 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeDeleteEvent.java @@ -24,7 +24,7 @@ import org.springframework.lang.Nullable; * * @author Jens Schauder */ -public class BeforeDeleteEvent extends RelationalDeleteEvent { +public class BeforeDeleteEvent extends RelationalDeleteEvent { private static final long serialVersionUID = -137285843224094551L; @@ -33,7 +33,7 @@ public class BeforeDeleteEvent extends RelationalDeleteEvent { * @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 BeforeDeleteEvent(Identifier id, @Nullable Object entity, AggregateChange change) { + public BeforeDeleteEvent(Identifier id, @Nullable E entity, AggregateChange change) { super(id, entity, change); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveEvent.java index 7c9293d5..214cca74 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/BeforeSaveEvent.java @@ -23,7 +23,7 @@ import org.springframework.data.relational.core.conversion.AggregateChange; * * @author Jens Schauder */ -public class BeforeSaveEvent extends RelationalSaveEvent{ +public class BeforeSaveEvent extends RelationalSaveEvent{ private static final long serialVersionUID = -4935804431519314116L; @@ -32,7 +32,7 @@ public class BeforeSaveEvent extends RelationalSaveEvent{ * @param change the {@link AggregateChange} that is going to get applied to the database. Must not be {@literal null}. * */ - public BeforeSaveEvent(Object instance, AggregateChange change) { + public BeforeSaveEvent(E instance, AggregateChange change) { super(instance, change); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java index e8167f47..d6b8d0c8 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalDeleteEvent.java @@ -25,22 +25,22 @@ import org.springframework.util.Assert; * {@link AggregateChange} and may also have an entity if the entity was provided to the method performing the delete. * * @author Jens Schauder + * @since 2.0 */ -abstract public class RelationalDeleteEvent extends ApplicationEvent implements WithId, WithAggregateChange { +public abstract class RelationalDeleteEvent extends AbstractRelationalEvent implements WithId, WithAggregateChange { private static final long serialVersionUID = -8071323168471611098L; private final Identifier id; - @Nullable private final Object entity; - private final AggregateChange change; + @Nullable private final E 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) { + RelationalDeleteEvent(Identifier id, @Nullable E entity, AggregateChange change) { super(id); @@ -59,13 +59,17 @@ abstract public class RelationalDeleteEvent extends ApplicationEvent implements @Override @Nullable - public Object getEntity() { + public E getEntity() { return entity; } @Override - public AggregateChange getAggregateChange() { + public AggregateChange getAggregateChange() { return change; } + @Override + public Class getType() { + return change.getEntityType(); + } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java index e7caf89d..597b1803 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEvent.java @@ -17,18 +17,25 @@ package org.springframework.data.relational.core.mapping.event; import org.springframework.lang.Nullable; -import java.util.Optional; - /** * an event signalling JDBC processing. * + * @param the type of the entity to which the event relates. * @author Oliver Gierke + * @author Mark Paluch + * @author Jens Schauder */ -public interface RelationalEvent { +public interface RelationalEvent { /** * @return the entity to which this event refers. Might be {@literal null}. */ @Nullable - Object getEntity(); + E getEntity(); + + /** + * @return the type of the entity to which the event relates. + * @since 2.0 + */ + Class getType(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithEntity.java index 7c3bef28..c1f0d170 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalEventWithEntity.java @@ -22,12 +22,12 @@ import org.springframework.context.ApplicationEvent; * * @author Jens Schauder */ -public class RelationalEventWithEntity extends ApplicationEvent implements WithEntity { +public class RelationalEventWithEntity extends AbstractRelationalEvent implements WithEntity { private static final long serialVersionUID = 4891455396602090638L; - private final Object entity; + private final E entity; - RelationalEventWithEntity(Object entity) { + RelationalEventWithEntity(E entity) { super(entity); @@ -38,7 +38,12 @@ public class RelationalEventWithEntity extends ApplicationEvent implements WithE * @return the entity to which this event refers. Guaranteed to be not {@literal null}. */ @Override - public Object getEntity() { + public E getEntity() { return entity; } + + @Override + public Class getType() { + return (Class) entity.getClass(); + } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalSaveEvent.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalSaveEvent.java index 17f611bc..8ffe1aad 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalSaveEvent.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/RelationalSaveEvent.java @@ -1,4 +1,4 @@ -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"); @@ -13,19 +13,23 @@ package org.springframework.data.relational.core.mapping.event;/* * 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.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. + * Events triggered during saving of an aggregate. Events of this type always have an {@link AggregateChange} and an + * entity. + * + * @author Jens Schauder + * @since 2.0 */ -public abstract class RelationalSaveEvent extends RelationalEventWithEntity implements WithAggregateChange{ +public abstract class RelationalSaveEvent extends RelationalEventWithEntity implements WithAggregateChange { - private final AggregateChange change; + private final AggregateChange change; - RelationalSaveEvent(Object entity, AggregateChange change) { + RelationalSaveEvent(E entity, AggregateChange change) { super(entity); @@ -35,7 +39,7 @@ public abstract class RelationalSaveEvent extends RelationalEventWithEntity impl } @Override - public AggregateChange getAggregateChange() { + public AggregateChange getAggregateChange() { return change; } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithAggregateChange.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithAggregateChange.java index 23fc8779..5c60bf48 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithAggregateChange.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithAggregateChange.java @@ -21,10 +21,10 @@ import org.springframework.data.relational.core.conversion.AggregateChange; * {@link RelationalEvent} that represents a change to an aggregate and therefore has an {@link AggregateChange} * @author Jens Schauder */ -public interface WithAggregateChange extends RelationalEvent { +public interface WithAggregateChange extends RelationalEvent { /** * @return Guaranteed to be not {@literal null}. */ - AggregateChange getAggregateChange(); + AggregateChange getAggregateChange(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithEntity.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithEntity.java index c84cb8a8..777e9e64 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithEntity.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithEntity.java @@ -15,15 +15,21 @@ */ package org.springframework.data.relational.core.mapping.event; +import org.springframework.lang.NonNull; + /** * Interface for events which are guaranteed to have an entity. * * @author Jens Schauder */ -public interface WithEntity extends RelationalEvent { +public interface WithEntity extends RelationalEvent { /** + * Overridden in order to change nullability. + * * @return will never be {@literal null}. */ - Object getEntity(); + @Override + @NonNull + E getEntity(); } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithId.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithId.java index e0c5713e..ca36151f 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithId.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/WithId.java @@ -20,7 +20,7 @@ package org.springframework.data.relational.core.mapping.event; * * @author Jens Schauder */ -public interface WithId extends RelationalEvent { +public interface WithId extends RelationalEvent { /** * Events with an identifier will always return a {@link Identifier} one. diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingCallback.java b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingCallback.java index d73972eb..b9531190 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingCallback.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/domain/support/RelationalAuditingCallback.java @@ -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.Identifier; /** * {@link BeforeConvertCallback} to capture auditing information on persisting and updating entities. diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEventListenerUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEventListenerUnitTests.java new file mode 100644 index 00000000..05f0964b --- /dev/null +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/event/AbstractRelationalEventListenerUnitTests.java @@ -0,0 +1,135 @@ +/* + * 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. + */ +package org.springframework.data.relational.core.mapping.event; + +import static org.assertj.core.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.springframework.data.relational.core.conversion.AggregateChange; + +/** + * Unit tests for {@link AbstractRelationalEventListener}. + * + * @author Mark Paluch + * @author Jens Schauder + */ +public class AbstractRelationalEventListenerUnitTests { + + List events = new ArrayList<>(); + EventListenerUnderTest listener = new EventListenerUnderTest(); + private DummyEntity dummyEntity = new DummyEntity(); + + @Test // DATAJDBC-454 + public void afterLoad() { + + listener.onApplicationEvent(new AfterLoadEvent<>(dummyEntity)); + + assertThat(events).containsExactly("afterLoad"); + } + + @Test // DATAJDBC-454 + public void beforeConvert() { + + listener.onApplicationEvent(new BeforeConvertEvent<>(dummyEntity, AggregateChange.forDelete(dummyEntity))); + + assertThat(events).containsExactly("beforeConvert"); + } + + @Test // DATAJDBC-454 + public void beforeSave() { + + listener.onApplicationEvent(new BeforeSaveEvent<>(dummyEntity, AggregateChange.forSave(dummyEntity))); + + assertThat(events).containsExactly("beforeSave"); + } + + @Test // DATAJDBC-454 + public void afterSave() { + + listener.onApplicationEvent(new AfterSaveEvent<>(dummyEntity, AggregateChange.forDelete(dummyEntity))); + + assertThat(events).containsExactly("afterSave"); + } + + @Test // DATAJDBC-454 + public void beforeDelete() { + + listener.onApplicationEvent( + new BeforeDeleteEvent<>(Identifier.of(23), dummyEntity, AggregateChange.forDelete(dummyEntity))); + + assertThat(events).containsExactly("beforeDelete"); + } + + @Test // DATAJDBC-454 + public void afterDelete() { + + listener.onApplicationEvent( + new AfterDeleteEvent<>(Identifier.of(23), dummyEntity, AggregateChange.forDelete(dummyEntity))); + + assertThat(events).containsExactly("afterDelete"); + } + + @Test // DATAJDBC-454 + public void eventWithNonMatchingDomainType() { + + String notADummyEntity = "I'm not a dummy entity"; + + listener.onApplicationEvent( + new AfterDeleteEvent<>(Identifier.of(23), notADummyEntity, AggregateChange.forDelete(notADummyEntity))); + + assertThat(events).isEmpty(); + } + + static class DummyEntity { + + } + + private class EventListenerUnderTest extends AbstractRelationalEventListener { + + @Override + protected void onBeforeConvert(BeforeConvertEvent event) { + events.add("beforeConvert"); + } + + @Override + protected void onBeforeSave(BeforeSaveEvent event) { + events.add("beforeSave"); + } + + @Override + protected void onAfterSave(AfterSaveEvent event) { + events.add("afterSave"); + } + + @Override + protected void onAfterLoad(AfterLoadEvent event) { + events.add("afterLoad"); + } + + @Override + protected void onAfterDelete(AfterDeleteEvent event) { + events.add("afterDelete"); + } + + @Override + protected void onBeforeDelete(BeforeDeleteEvent event) { + events.add("beforeDelete"); + } + } +} diff --git a/src/main/asciidoc/jdbc.adoc b/src/main/asciidoc/jdbc.adoc index a03fa8d5..fe45832d 100644 --- a/src/main/asciidoc/jdbc.adoc +++ b/src/main/asciidoc/jdbc.adoc @@ -609,20 +609,33 @@ For example, the following listener gets invoked before an aggregate gets saved: [source,java] ---- @Bean -public ApplicationListener timeStampingSaveTime() { +public ApplicationListener loggingSaves() { return event -> { Object entity = event.getEntity(); - if (entity instanceof Category) { - Category category = (Category) entity; - category.timeStamp(); - } + LOG.info("{} is getting saved."; }; } ---- ==== +If you want to handle events only for a specific domain type you may derive your listener from `AbstractRelationalEventListener` and overwrite one or more of the `onXXX` methods, +where `XXX` stands for an event type. It will only get invoked for events related to the domain type you used in the declaration and provides typed events, so no casting of the entity is required. + +==== +[source,java] +---- +public class PersonLoadListener extends AbstractRelationalEventListener { + + @Override + protected void onAfterLoad(AfterLoadEvent personLoad) { + LOG.info(personLoad.getEntity().setLoadTimeStamp(new Date()); + } +} +---- +==== + The following table describes the available events: .Available events