DATAJDBC-120 - Changed events to support manipulation of DbChange.

This change enables the manipulation of the DbChange instance before it gets interpreted and turned into SQL statements.

Only for Aggregate Roots events get fired, since these are the abstraction the repositories work on.
Insert and Update events got removed, since this distinction doesn't exist on the Aggregate Root level.
It only exists on the level of entities and/or tables which is represented by DbActions.

Improved some tests to properly check all the events triggered.
This commit is contained in:
Jens Schauder
2017-08-08 11:20:57 +02:00
committed by Greg Turnquist
parent fe5ae93fd0
commit 1f2dc16d05
25 changed files with 362 additions and 237 deletions

View File

@@ -25,7 +25,6 @@ import org.springframework.data.jdbc.core.conversion.DbAction.DeleteAll;
import org.springframework.data.jdbc.core.conversion.DbAction.Insert;
import org.springframework.data.jdbc.core.conversion.DbAction.Update;
import org.springframework.data.jdbc.core.conversion.Interpreter;
import org.springframework.data.jdbc.mapping.event.Identifier;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
@@ -74,7 +73,7 @@ class DefaultJdbcInterpreter implements Interpreter {
public <T> void interpret(Delete<T> delete) {
if (delete.getPropertyPath() == null) {
template.doDelete(Identifier.of(delete.getRootId()), Optional.ofNullable(delete.getEntity()),
template.doDelete(delete.getRootId(), Optional.ofNullable(delete.getEntity()),
delete.getEntityType());
} else {
template.doDelete(delete.getRootId(), delete.getPropertyPath());

View File

@@ -128,10 +128,11 @@ class EntityRowMapper<T> implements RowMapper<T> {
return null;
}
String column = prefix + name;
try {
return conversionService.convert(resultSet.getObject(prefix + name), parameter.getType().getType());
return conversionService.convert(resultSet.getObject(column), parameter.getType().getType());
} catch (SQLException o_O) {
throw new MappingException(String.format("Couldn't read column %s from ResultSet.", name), o_O);
throw new MappingException(String.format("Couldn't read column %s from ResultSet.", column), o_O);
}
}
}

View File

@@ -50,7 +50,7 @@ public class EventPublishingEntityRowMapper<T> implements RowMapper<T> {
T instance = delegate.mapRow(resultSet, i);
publisher.publishEvent(new AfterCreation(Identifier.of(entityInformation.getRequiredId(instance)), instance));
publisher.publishEvent(new AfterCreation(Identifier.of(entityInformation.getRequiredId(instance)), instance, null));
return instance;
}

View File

@@ -36,11 +36,9 @@ import org.springframework.data.jdbc.core.conversion.Interpreter;
import org.springframework.data.jdbc.core.conversion.JdbcEntityDeleteWriter;
import org.springframework.data.jdbc.core.conversion.JdbcEntityWriter;
import org.springframework.data.jdbc.mapping.event.AfterDelete;
import org.springframework.data.jdbc.mapping.event.AfterInsert;
import org.springframework.data.jdbc.mapping.event.AfterUpdate;
import org.springframework.data.jdbc.mapping.event.AfterSave;
import org.springframework.data.jdbc.mapping.event.BeforeDelete;
import org.springframework.data.jdbc.mapping.event.BeforeInsert;
import org.springframework.data.jdbc.mapping.event.BeforeUpdate;
import org.springframework.data.jdbc.mapping.event.BeforeSave;
import org.springframework.data.jdbc.mapping.event.Identifier;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentEntityInformation;
@@ -102,14 +100,29 @@ public class JdbcEntityTemplate implements JdbcEntityOperations {
@Override
public <T> void save(T instance, Class<T> domainType) {
createChange(instance).executeWith(interpreter);
JdbcPersistentEntityInformation<T, ?> entityInformation = context.getRequiredPersistentEntityInformation(domainType);
AggregateChange change = createChange(instance);
publisher.publishEvent(new BeforeSave( //
Identifier.ofNullable(entityInformation.getId(instance)), //
instance, //
change //
));
change.executeWith(interpreter);
publisher.publishEvent(new AfterSave( //
Identifier.of(entityInformation.getId(instance)), //
instance, //
change //
));
}
@Override
public <T> void insert(T instance, Class<T> domainType, Map<String, Object> additionalParameters) {
publisher.publishEvent(new BeforeInsert(instance));
KeyHolder holder = new GeneratedKeyHolder();
JdbcPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
JdbcPersistentEntityInformation<T, ?> entityInformation = context
@@ -132,23 +145,17 @@ public class JdbcEntityTemplate implements JdbcEntityOperations {
throw new IllegalStateException(String.format(ENTITY_NEW_AFTER_INSERT, persistentEntity));
}
publisher.publishEvent(new AfterInsert(Identifier.of(entityInformation.getRequiredId(instance)), instance));
}
@Override
public <S> void update(S instance, Class<S> domainType) {
JdbcPersistentEntity<S> persistentEntity = getRequiredPersistentEntity(domainType);
JdbcPersistentEntityInformation<S, ?> entityInformation = context
.getRequiredPersistentEntityInformation(domainType);
Specified specifiedId = Identifier.of(entityInformation.getRequiredId(instance));
publisher.publishEvent(new BeforeUpdate(specifiedId, instance));
operations.update(sql(domainType).getUpdate(), getPropertyMap(instance, persistentEntity));
publisher.publishEvent(new AfterUpdate(specifiedId, instance));
}
@SuppressWarnings("ConstantConditions")
@Override
public long count(Class<?> domainType) {
return operations.getJdbcOperations().queryForObject(sql(domainType).getCount(), Long.class);
@@ -211,9 +218,21 @@ public class JdbcEntityTemplate implements JdbcEntityOperations {
change.executeWith(interpreter);
}
void doDelete(Object rootId, PropertyPath propertyPath) {
private void deleteTree(Object id, Object entity, Class<?> domainType) {
JdbcPersistentEntity<?> entityToDelete = context.getRequiredPersistentEntity(propertyPath.getTypeInformation());
AggregateChange change = createDeletingChange(id, entity, domainType);
Specified specifiedId = Identifier.of(id);
Optional<Object> optionalEntity = Optional.ofNullable(entity);
publisher.publishEvent(new BeforeDelete(specifiedId, optionalEntity, change));
change.executeWith(interpreter);
publisher.publishEvent(new AfterDelete(specifiedId, optionalEntity, change));
}
void doDelete(Object rootId, PropertyPath propertyPath) {
JdbcPersistentEntity<?> rootEntity = context.getRequiredPersistentEntity(propertyPath.getOwningType());
@@ -228,23 +247,12 @@ public class JdbcEntityTemplate implements JdbcEntityOperations {
}
void doDelete(Specified specifiedId, Optional<Object> optionalEntity, Class<?> domainType) {
publisher.publishEvent(new BeforeDelete(specifiedId, optionalEntity));
void doDelete(Object id, Optional<Object> optionalEntity, Class<?> domainType) {
String deleteByIdSql = sql(domainType).getDeleteById();
MapSqlParameterSource parameter = createIdParameterSource(specifiedId.getValue(), domainType);
MapSqlParameterSource parameter = createIdParameterSource(id, domainType);
operations.update(deleteByIdSql, parameter);
publisher.publishEvent(new AfterDelete(specifiedId, optionalEntity));
}
private void deleteTree(Object id, Object entity, Class<?> domainType) {
AggregateChange change = createDeletingChange(id, entity, domainType);
change.executeWith(interpreter);
}
private <T> AggregateChange createChange(T instance) {

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.jdbc.mapping.event;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
@@ -31,8 +32,9 @@ public class AfterCreation extends JdbcEventWithIdAndEntity {
/**
* @param id of the entity
* @param entity the newly instantiated entity.
* @param change
*/
public AfterCreation(Specified id, Object entity) {
super(id, entity);
public AfterCreation(Specified id, Object entity, AggregateChange change) {
super(id, entity, change);
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.jdbc.mapping.event;
import java.util.Optional;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
@@ -33,8 +34,9 @@ public class AfterDelete extends JdbcEventWithId {
/**
* @param id of the entity.
* @param instance the deleted entity if it is available.
* @param change the {@link AggregateChange} encoding the planned actions to be performed on the database.
*/
public AfterDelete(Specified id, Optional<Object> instance) {
super(id, instance);
public AfterDelete(Specified id, Optional<Object> instance, AggregateChange change) {
super(id, instance, change);
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2017 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
*
* http://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.mapping.event;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
* Gets published after an entity got inserted into the database.
*
* @author Jens Schauder
* @since 2.0
*/
public class AfterInsert extends AfterSave {
private static final long serialVersionUID = 1312645717283677063L;
/**
* @param id identifier of the entity triggering the event.
* @param instance the newly inserted entity.
*/
public AfterInsert(Specified id, Object instance) {
super(id, instance);
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.jdbc.mapping.event;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
@@ -30,8 +31,9 @@ public class AfterSave extends JdbcEventWithIdAndEntity {
/**
* @param id identifier of
* @param instance the newly saved entity.
* @param change the {@link AggregateChange} encoding the planned actions to be performed on the database.
*/
AfterSave(Specified id, Object instance) {
super(id, instance);
public AfterSave(Specified id, Object instance, AggregateChange change) {
super(id, instance, change);
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2017 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
*
* http://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.mapping.event;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
* Gets published after an entity was updated in the database.
*
* @author Jens Schauder
* @since 2.0
*/
public class AfterUpdate extends AfterSave {
private static final long serialVersionUID = -1765706904721563399L;
/**
* @param id of the entity
* @param instance the updated entity.
*/
public AfterUpdate(Specified id, Object instance) {
super(id, instance);
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.jdbc.mapping.event;
import java.util.Optional;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
@@ -32,8 +33,9 @@ public class BeforeDelete extends JdbcEventWithId {
/**
* @param id the id of the entity
* @param entity the entity about to get deleted. Might be empty.
* @param change the {@link AggregateChange} encoding the planned actions to be performed on the database.
*/
public <T> BeforeDelete(Specified id, Optional<Object> entity) {
super(id, entity);
public <T> BeforeDelete(Specified id, Optional<Object> entity, AggregateChange change) {
super(id, entity, change);
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2017 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
*
* http://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.mapping.event;
/**
* Gets published before an entity gets inserted into the database. When the id-property of the entity must get set
* manually, an event listener for this event may do so. <br>
* The {@link Identifier} is {@link Unset#UNSET}
*
* @author Jens Schauder
* @since 2.0
*/
public class BeforeInsert extends BeforeSave {
private static final long serialVersionUID = -5350552051337308870L;
/**
* @param instance the entity about to get inserted.
*/
public BeforeInsert(Object instance) {
super(Unset.UNSET, instance);
}
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.jdbc.mapping.event;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
/**
* Subclasses of this get published before an entity gets saved to the database.
*
@@ -28,8 +30,9 @@ public class BeforeSave extends JdbcEventWithEntity {
/**
* @param id of the entity to be saved.
* @param instance the entity about to get saved.
* @param change
*/
BeforeSave(Identifier id, Object instance) {
super(id, instance);
public BeforeSave(Identifier id, Object instance, AggregateChange change) {
super(id, instance, change);
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2017 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
*
* http://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.mapping.event;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
* Gets published before an entity gets updated in the database.
*
* @author Jens Schauder
* @since 2.0
*/
public class BeforeUpdate extends BeforeSave implements WithId {
private static final long serialVersionUID = 794561215071613972L;
private final Specified id;
/**
* @param id of the entity about to get updated
* @param instance the entity about to get updated.
*/
public BeforeUpdate(Specified id, Object instance) {
super(id, instance);
this.id = id;
}
/*
* (non-Javadoc)
* @see org.springframework.data.jdbc.mapping.event.JdbcEvent#getId()
*/
@Override
public Specified getId() {
return id;
}
}

View File

@@ -40,6 +40,10 @@ public interface Identifier {
return SpecifiedIdentifier.of(identifier);
}
static Identifier ofNullable(Object identifier) {
return identifier == null ? Unset.UNSET : of(identifier);
}
/**
* Creates a new {@link Identifier} for the given optional source value.
*

View File

@@ -17,6 +17,8 @@ package org.springframework.data.jdbc.mapping.event;
import java.util.Optional;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
/**
* A {@link SimpleJdbcEvent} which is guaranteed to have an entity.
*
@@ -27,7 +29,7 @@ public class JdbcEventWithEntity extends SimpleJdbcEvent implements WithEntity {
private static final long serialVersionUID = 4891455396602090638L;
public JdbcEventWithEntity(Identifier id, Object entity) {
super(id, Optional.of(entity));
public JdbcEventWithEntity(Identifier id, Object entity, AggregateChange change) {
super(id, Optional.of(entity), change);
}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.jdbc.mapping.event;
import java.util.Optional;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
@@ -31,9 +32,9 @@ public class JdbcEventWithId extends SimpleJdbcEvent implements WithId {
private final Specified id;
public JdbcEventWithId(Specified id, Optional<Object> entity) {
public JdbcEventWithId(Specified id, Optional<Object> entity, AggregateChange change) {
super(id, entity);
super(id, entity, change);
this.id = id;
}

View File

@@ -19,6 +19,7 @@ import lombok.Getter;
import java.util.Optional;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
@@ -32,7 +33,7 @@ public class JdbcEventWithIdAndEntity extends JdbcEventWithId implements WithEnt
private static final long serialVersionUID = -3194462549552515519L;
public JdbcEventWithIdAndEntity(Specified id, Object entity) {
super(id, Optional.of(entity));
public JdbcEventWithIdAndEntity(Specified id, Object entity, AggregateChange change) {
super(id, Optional.of(entity), change);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.data.jdbc.mapping.event;
import java.util.Optional;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.jdbc.core.conversion.AggregateChange;
/**
* The common superclass for all events published by JDBC repositories. {@link #getSource} contains the
@@ -32,12 +33,14 @@ class SimpleJdbcEvent extends ApplicationEvent implements JdbcEvent {
private static final long serialVersionUID = -1798807778668751659L;
private final Object entity;
private final AggregateChange change;
SimpleJdbcEvent(Identifier id, Optional<Object> entity) {
SimpleJdbcEvent(Identifier id, Optional<Object> entity, AggregateChange change) {
super(id);
this.entity = entity.orElse(null);
this.change = change;
}
/*
@@ -57,4 +60,8 @@ class SimpleJdbcEvent extends ApplicationEvent implements JdbcEvent {
public Optional<Object> getOptionalEntity() {
return Optional.ofNullable(entity);
}
public AggregateChange getChange() {
return change;
}
}