Browse Source
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.pull/13/merge
25 changed files with 362 additions and 237 deletions
@ -1,37 +0,0 @@
@@ -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); |
||||
} |
||||
} |
||||
@ -1,37 +0,0 @@
@@ -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); |
||||
} |
||||
} |
||||
@ -1,36 +0,0 @@
@@ -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); |
||||
} |
||||
} |
||||
@ -1,51 +0,0 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,232 @@
@@ -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)); |
||||
}; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,2 @@
@@ -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 @@
@@ -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 @@
@@ -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)); |
||||
Loading…
Reference in new issue