diff --git a/pom.xml b/pom.xml index 4ffa5540e7..c3e7763d3d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-jdbc - 1.0.0.BUILD-SNAPSHOT + 1.0.0.DATAJDBC-120-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. diff --git a/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java b/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java index 41195a51b0..e6aadeeb56 100644 --- a/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java +++ b/src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java @@ -25,7 +25,6 @@ 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 @@ public void interpret(Update update) { public void interpret(Delete 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()); diff --git a/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java b/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java index 667827c90c..fcfa92116b 100644 --- a/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java +++ b/src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java @@ -128,10 +128,11 @@ public T getParameterValue(Parameter parameter) { 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); } } } diff --git a/src/main/java/org/springframework/data/jdbc/core/EventPublishingEntityRowMapper.java b/src/main/java/org/springframework/data/jdbc/core/EventPublishingEntityRowMapper.java index a543cd4494..8b39ee144f 100644 --- a/src/main/java/org/springframework/data/jdbc/core/EventPublishingEntityRowMapper.java +++ b/src/main/java/org/springframework/data/jdbc/core/EventPublishingEntityRowMapper.java @@ -50,7 +50,7 @@ public T mapRow(ResultSet resultSet, int i) throws SQLException { 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; } diff --git a/src/main/java/org/springframework/data/jdbc/core/JdbcEntityTemplate.java b/src/main/java/org/springframework/data/jdbc/core/JdbcEntityTemplate.java index e55754fbd5..6e596c0b67 100644 --- a/src/main/java/org/springframework/data/jdbc/core/JdbcEntityTemplate.java +++ b/src/main/java/org/springframework/data/jdbc/core/JdbcEntityTemplate.java @@ -36,11 +36,9 @@ 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 @@ private static GenericConversionService getDefaultConversionService() { @Override public void save(T instance, Class domainType) { - createChange(instance).executeWith(interpreter); + + JdbcPersistentEntityInformation 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 void insert(T instance, Class domainType, Map additionalParameters) { - publisher.publishEvent(new BeforeInsert(instance)); - KeyHolder holder = new GeneratedKeyHolder(); JdbcPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType); JdbcPersistentEntityInformation entityInformation = context @@ -132,23 +145,17 @@ public void insert(T instance, Class domainType, Map addi throw new IllegalStateException(String.format(ENTITY_NEW_AFTER_INSERT, persistentEntity)); } - publisher.publishEvent(new AfterInsert(Identifier.of(entityInformation.getRequiredId(instance)), instance)); - } @Override public void update(S instance, Class domainType) { JdbcPersistentEntity persistentEntity = getRequiredPersistentEntity(domainType); - JdbcPersistentEntityInformation 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 void deleteAll(Class domainType) { 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 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 @@ void doDelete(Object rootId, PropertyPath propertyPath) { } - void doDelete(Specified specifiedId, Optional optionalEntity, Class domainType) { - - publisher.publishEvent(new BeforeDelete(specifiedId, optionalEntity)); + void doDelete(Object id, Optional 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 AggregateChange createChange(T instance) { diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreation.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreation.java index b0f2e8edd6..6ceb9fa534 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreation.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreation.java @@ -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); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDelete.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDelete.java index f1f24d1952..dd4650a56d 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDelete.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDelete.java @@ -17,6 +17,7 @@ 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 instance) { - super(id, instance); + public AfterDelete(Specified id, Optional instance, AggregateChange change) { + super(id, instance, change); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsert.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsert.java deleted file mode 100644 index 5b82bd3c93..0000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsert.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java index 9c79e21398..2ce3278814 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java @@ -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); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdate.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdate.java deleted file mode 100644 index cda83304b2..0000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdate.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDelete.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDelete.java index ef7d1c0f12..7088060b34 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDelete.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDelete.java @@ -17,6 +17,7 @@ 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 BeforeDelete(Specified id, Optional entity) { - super(id, entity); + public BeforeDelete(Specified id, Optional entity, AggregateChange change) { + super(id, entity, change); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java deleted file mode 100644 index 37d2227034..0000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java +++ /dev/null @@ -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.
- * 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); - } -} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java index ae100d39a4..7db884ccda 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java @@ -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); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeUpdate.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeUpdate.java deleted file mode 100644 index 57f7a330e1..0000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeUpdate.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/Identifier.java b/src/main/java/org/springframework/data/jdbc/mapping/event/Identifier.java index 0db4664628..3c6cfe34d9 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/Identifier.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/Identifier.java @@ -40,6 +40,10 @@ static Specified of(Object 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. * diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithEntity.java b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithEntity.java index 1d325f6bd5..805dd7d4e8 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithEntity.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithEntity.java @@ -17,6 +17,8 @@ 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); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithId.java b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithId.java index d5c1ded08a..f5711e6286 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithId.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithId.java @@ -17,6 +17,7 @@ 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 entity) { + public JdbcEventWithId(Specified id, Optional entity, AggregateChange change) { - super(id, entity); + super(id, entity, change); this.id = id; } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithIdAndEntity.java b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithIdAndEntity.java index e9268bf4b6..621cc9d426 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithIdAndEntity.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithIdAndEntity.java @@ -19,6 +19,7 @@ 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); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/SimpleJdbcEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/SimpleJdbcEvent.java index bf5df81afa..321a27abb3 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/SimpleJdbcEvent.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/SimpleJdbcEvent.java @@ -18,6 +18,7 @@ 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 entity) { + SimpleJdbcEvent(Identifier id, Optional entity, AggregateChange change) { super(id); this.entity = entity.orElse(null); + this.change = change; } /* @@ -57,4 +60,8 @@ public Identifier getId() { public Optional getOptionalEntity() { return Optional.ofNullable(entity); } + + public AggregateChange getChange() { + return change; + } } diff --git a/src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperUnitTests.java b/src/test/java/org/springframework/data/jdbc/core/EventPublishingEntityRowMapperUnitTests.java similarity index 94% rename from src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperUnitTests.java rename to src/test/java/org/springframework/data/jdbc/core/EventPublishingEntityRowMapperUnitTests.java index 0c9b54b03f..d84ec2c8fc 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperUnitTests.java +++ b/src/test/java/org/springframework/data/jdbc/core/EventPublishingEntityRowMapperUnitTests.java @@ -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.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; diff --git a/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryManipulateDbActionsIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryManipulateDbActionsIntegrationTests.java new file mode 100644 index 0000000000..1615c05c3f --- /dev/null +++ b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryManipulateDbActionsIntegrationTests.java @@ -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 {} + + @Getter + @Setter + @RequiredArgsConstructor + private static class Log { + + @Id final Long id; + DummyEntity entity; + String text; + } + + private interface LogRepository extends CrudRepository {} + + @Configuration + @Import(TestConfiguration.class) + @EnableJdbcRepositories(considerNestedRepositories = true) + static class Config { + + static long lastLogId; + + @Bean + Class testClass() { + return JdbcRepositoryManipulateDbActionsIntegrationTests.class; + } + + @Bean + ApplicationListener softDeleteListener() { + + return event -> { + + DummyEntity entity = (DummyEntity) event.getOptionalEntity().orElseThrow(AssertionFailedError::new); + entity.deleted = true; + + List actions = event.getChange().getActions(); + actions.clear(); + actions.add(DbAction.update(entity, null)); + }; + } + + @Bean + ApplicationListener 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 actions = event.getChange().getActions(); + actions.add(DbAction.insert(log, null)); + }; + } + + } + +} diff --git a/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java index 8f485778a4..66cb618214 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryPropertyConversionIntegrationTests.java @@ -40,7 +40,7 @@ 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 @@ DummyEntityRepository dummyEntityRepository() { @Bean ApplicationListener applicationListener() { - return (ApplicationListener) beforeInsert -> ((EntityWithColumnsRequiringConversions) beforeInsert + return (ApplicationListener) beforeInsert -> ((EntityWithColumnsRequiringConversions) beforeInsert .getEntity()).setIdTimestamp(getNow()); } diff --git a/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java b/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java index af0a6e5e24..fe3b04ed13 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java @@ -2,26 +2,27 @@ 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 void publishesEventsOnSave() { 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 void publishesEventsOnSaveMany() { 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 void publishesEventsOnDelete() { 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 void publishesEventsOnDeleteById() { 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() { diff --git a/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-hsql.sql b/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-hsql.sql new file mode 100644 index 0000000000..27f8287ca4 --- /dev/null +++ b/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-hsql.sql @@ -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)); diff --git a/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-mysql.sql b/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-mysql.sql new file mode 100644 index 0000000000..9b52d44dab --- /dev/null +++ b/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-mysql.sql @@ -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)); diff --git a/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-postgres.sql b/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-postgres.sql new file mode 100644 index 0000000000..703cd57fb1 --- /dev/null +++ b/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryManipulateDbActionsIntegrationTests-postgres.sql @@ -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));