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:
committed by
Greg Turnquist
parent
fe5ae93fd0
commit
1f2dc16d05
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository;
|
||||
package org.springframework.data.jdbc.core;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -31,7 +31,6 @@ import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.core.EventPublishingEntityRowMapper;
|
||||
import org.springframework.data.jdbc.mapping.event.AfterCreation;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityInformation;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.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.core.conversion.DbAction;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeDelete;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeSave;
|
||||
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
|
||||
import org.springframework.data.jdbc.testing.TestConfiguration;
|
||||
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
|
||||
*/
|
||||
@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 {
|
||||
|
||||
final @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 final 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<BeforeDelete> softDeleteListener() {
|
||||
|
||||
return event -> {
|
||||
|
||||
DummyEntity entity = (DummyEntity) event.getOptionalEntity().orElseThrow(AssertionFailedError::new);
|
||||
entity.deleted = true;
|
||||
|
||||
List<DbAction> actions = event.getChange().getActions();
|
||||
actions.clear();
|
||||
actions.add(DbAction.update(entity, null));
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
ApplicationListener<BeforeSave> logOnSaveListener() {
|
||||
|
||||
// this would actually be easier to implement with an AfterSave 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(lastLogId);
|
||||
log.entity = entity;
|
||||
log.text = entity.name + " saved";
|
||||
|
||||
|
||||
List<DbAction> actions = event.getChange().getActions();
|
||||
actions.add(DbAction.insert(log, null));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ 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.jdbc.mapping.event.BeforeInsert;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeSave;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.jdbc.testing.TestConfiguration;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
@@ -77,7 +77,7 @@ public class JdbcRepositoryPropertyConversionIntegrationTests {
|
||||
|
||||
@Bean
|
||||
ApplicationListener applicationListener() {
|
||||
return (ApplicationListener<BeforeInsert>) beforeInsert -> ((EntityWithColumnsRequiringConversions) beforeInsert
|
||||
return (ApplicationListener<BeforeSave>) beforeInsert -> ((EntityWithColumnsRequiringConversions) beforeInsert
|
||||
.getEntity()).setIdTimestamp(getNow());
|
||||
}
|
||||
|
||||
|
||||
@@ -2,26 +2,27 @@ package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.groups.Tuple;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.annotation.Id;
|
||||
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.JdbcEvent;
|
||||
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
|
||||
@@ -55,8 +56,12 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeUpdate.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterUpdate.class);
|
||||
assertThat(publisher.events) //
|
||||
.extracting(e -> (Class) e.getClass()) //
|
||||
.containsExactly( //
|
||||
BeforeSave.class, //
|
||||
AfterSave.class //
|
||||
);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
@@ -67,10 +72,14 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
|
||||
repository.saveAll(asList(entity1, entity2));
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeInsert.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterInsert.class);
|
||||
assertThat(publisher.events.get(2)).isInstanceOf(BeforeUpdate.class);
|
||||
assertThat(publisher.events.get(3)).isInstanceOf(AfterUpdate.class);
|
||||
assertThat(publisher.events) //
|
||||
.extracting(e -> (Class) e.getClass()) //
|
||||
.containsExactly( //
|
||||
BeforeSave.class, //
|
||||
AfterSave.class, //
|
||||
BeforeSave.class, //
|
||||
AfterSave.class //
|
||||
);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
@@ -80,14 +89,14 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
|
||||
repository.delete(entity);
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeDelete.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterDelete.class);
|
||||
|
||||
assertThat(publisher.events.get(0).getOptionalEntity()).hasValue(entity);
|
||||
assertThat(publisher.events.get(1).getOptionalEntity()).hasValue(entity);
|
||||
|
||||
assertThat(publisher.events.get(0).getId()).isEqualTo(Identifier.of(23L));
|
||||
assertThat(publisher.events.get(1).getId()).isEqualTo(Identifier.of(23L));
|
||||
assertThat(publisher.events).extracting( //
|
||||
e -> (Class) e.getClass(), //
|
||||
e -> e.getOptionalEntity().orElseGet(AssertionFailedError::new), //
|
||||
JdbcEvent::getId //
|
||||
).containsExactly( //
|
||||
Tuple.tuple(BeforeDelete.class, entity, Identifier.of(23L)), //
|
||||
Tuple.tuple(AfterDelete.class, entity, Identifier.of(23L)) //
|
||||
);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
@@ -95,8 +104,12 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
|
||||
repository.deleteById(23L);
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeDelete.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterDelete.class);
|
||||
assertThat(publisher.events) //
|
||||
.extracting(e -> (Class) e.getClass()) //
|
||||
.containsExactly( //
|
||||
BeforeDelete.class, //
|
||||
AfterDelete.class //
|
||||
);
|
||||
}
|
||||
|
||||
private static NamedParameterJdbcOperations createIdGeneratingOperations() {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE TABLE dummyentity ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100), DELETED CHAR(1), log BIGINT);
|
||||
CREATE TABLE log ( id BIGINT, TEXT VARCHAR(100));
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE TABLE dummyentity ( id BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100), DELETED CHAR(1), log BIGINT);
|
||||
CREATE TABLE log ( id BIGINT, TEXT VARCHAR(100));
|
||||
@@ -0,0 +1,4 @@
|
||||
DROP TABLE dummyentity;
|
||||
DROP TABLE log;
|
||||
CREATE TABLE dummyentity ( id SERIAL PRIMARY KEY, NAME VARCHAR(100), DELETED CHAR(5), log BIGINT);
|
||||
CREATE TABLE log ( id BIGINT, TEXT VARCHAR(100));
|
||||
Reference in New Issue
Block a user