diff --git a/lombok.config b/lombok.config new file mode 100644 index 000000000..e50c7ea43 --- /dev/null +++ b/lombok.config @@ -0,0 +1,2 @@ +lombok.nonNull.exceptionType = IllegalArgumentException +lombok.log.fieldName = LOG diff --git a/pom.xml b/pom.xml index 4ada10172..cef9f3149 100644 --- a/pom.xml +++ b/pom.xml @@ -128,61 +128,6 @@ org.apache.maven.plugins maven-surefire-plugin 2.12 - - - org.springframework - spring-instrument - ${spring} - runtime - - - org.hsqldb - hsqldb - ${hsqldb1} - runtime - - - - - default-test - - - **/* - - - - - unit-tests - - test - - test - - - **/*UnitTests.java - - - - - integration-tests - - test - - test - - - **/*IntegrationTests.java - **/*Tests.java - - - **/*UnitTests.java -\ - - -javaagent:${settings.localRepository}/org/jacoco/org.jacoco.agent/${jacoco}/org.jacoco.agent-${jacoco}-runtime.jar=destfile=${jacoco.destfile} - - - - org.apache.maven.plugins diff --git a/src/main/java/org/springframework/data/jdbc/mapping/context/JdbcMappingContext.java b/src/main/java/org/springframework/data/jdbc/mapping/context/JdbcMappingContext.java index e9fd47d31..a2cb02078 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/context/JdbcMappingContext.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/context/JdbcMappingContext.java @@ -17,6 +17,8 @@ package org.springframework.data.jdbc.mapping.context; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; + +import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentProperty; import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty; import org.springframework.data.mapping.context.AbstractMappingContext; @@ -25,17 +27,22 @@ import org.springframework.data.util.TypeInformation; /** * @author Jens Schauder + * @since 2.0 */ public class JdbcMappingContext extends AbstractMappingContext, JdbcPersistentProperty> { - @Override protected JdbcPersistentEntity createPersistentEntity(TypeInformation typeInformation) { return new JdbcPersistentEntity(typeInformation); } @Override - protected JdbcPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, JdbcPersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { - return new JdbcPersistentProperty(field, descriptor, owner, simpleTypeHolder); + protected JdbcPersistentProperty createPersistentProperty( // + Field field, // + PropertyDescriptor descriptor, // + JdbcPersistentEntity owner, // + SimpleTypeHolder simpleTypeHolder // + ) { + return new BasicJdbcPersistentProperty(field, descriptor, owner, simpleTypeHolder); } } 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 new file mode 100644 index 000000000..9515ff80b --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreation.java @@ -0,0 +1,35 @@ +/* + * 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 instantiation and setting of all the properties of an entity. This allows to do some postprocessing of entities. + * + * @author Jens Schauder + * @since 2.0 + */ +public class AfterCreation extends JdbcEventWithIdAndEntity { + + /** + * @param id of the entity + * @param entity the newly instantiated entity. + */ + public AfterCreation(Specified id, Object entity) { + super(id, entity); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreationEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreationEvent.java deleted file mode 100644 index 1e96c4406..000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreationEvent.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; - -import java.util.function.Function; - -/** - * gets published after instantiation and setting of all the properties of an entity. This allows to do some - * postprocessing of entities. - * - * @author Jens Schauder - */ -public class AfterCreationEvent extends JdbcEvent{ - - /** - * @param instance the newly instantiated entity. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} - */ - public AfterCreationEvent(T instance, Function idProvider) { - super(instance, idProvider); - } -} 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 new file mode 100644 index 000000000..73c889b07 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDelete.java @@ -0,0 +1,39 @@ +/* + * 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 java.util.Optional; + +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; + +/** + * Gets published after deletion of an entity. It will have a {@link Specified} identifier. + * + * If the entity is empty or not depends on the delete method used. + * + * @author Jens Schauder + * @since 2.0 + */ +public class AfterDelete extends JdbcEventWithId{ + + /** + * @param id of the entity. + * @param instance the deleted entity if it is available. + */ + public AfterDelete(Specified id, Optional instance) { + super(id, instance); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDeleteEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDeleteEvent.java deleted file mode 100644 index 982ebbc2a..000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterDeleteEvent.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; - -import java.util.function.Function; - -/** - * get published after deletion of an entity. The source might contain the Id or the actual entity, depending on the - * {@code delete(...)} method used. - * - * @author Jens Schauder - */ -public class AfterDeleteEvent extends JdbcEvent{ - - /** - * @param instance the deleted entity. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} - */ - public AfterDeleteEvent(T instance, Function idProvider) { - super(instance, idProvider); - } -} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsertEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsert.java similarity index 65% rename from src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsertEvent.java rename to src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsert.java index aa733f910..9a6f4145d 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsertEvent.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterInsert.java @@ -15,21 +15,21 @@ */ package org.springframework.data.jdbc.mapping.event; -import java.util.function.Function; +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; /** - * gets published after an entity got inserted into the database. + * Gets published after an entity got inserted into the database. * * @author Jens Schauder + * @since 2.0 */ -public class AfterInsertEvent extends AfterSaveEvent { +public class AfterInsert extends AfterSave { /** + * @param id identifier of the entity triggering the event. * @param instance the newly inserted entity. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} */ - public AfterInsertEvent(T instance, Function idProvider) { - super(instance, idProvider); + public AfterInsert(Specified id, Object instance) { + super(id, instance); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSaveEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java similarity index 63% rename from src/main/java/org/springframework/data/jdbc/mapping/event/AfterSaveEvent.java rename to src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java index bf25700f6..e029e1aa5 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSaveEvent.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java @@ -15,20 +15,23 @@ */ package org.springframework.data.jdbc.mapping.event; -import java.util.function.Function; +import java.util.Optional; + +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; /** - * subclasses of this get published after a new instance or a changed instance was saved in the database + * Subclasses of this get published after a new instance or a changed instance was saved in the database. + * * @author Jens Schauder + * @since 2.0 */ -public class AfterSaveEvent extends JdbcEvent{ +public class AfterSave extends JdbcEventWithIdAndEntity { /** + * @param id identifier of * @param instance the newly saved entity. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} */ - AfterSaveEvent(T instance, Function idProvider) { - super(instance, idProvider); + AfterSave(Specified id, Object instance) { + super(id, instance); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdateEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdate.java similarity index 65% rename from src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdateEvent.java rename to src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdate.java index 075fde2be..b95cd0930 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdateEvent.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/AfterUpdate.java @@ -15,21 +15,21 @@ */ package org.springframework.data.jdbc.mapping.event; -import java.util.function.Function; +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; /** - * gets published after an entity was updated in the database. + * Gets published after an entity was updated in the database. * * @author Jens Schauder + * @since 2.0 */ -public class AfterUpdateEvent extends AfterSaveEvent { +public class AfterUpdate extends AfterSave { /** + * @param id of the entity * @param instance the updated entity. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} */ - public AfterUpdateEvent(T instance, Function idProvider) { - super(instance, idProvider); + 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 new file mode 100644 index 000000000..4d221b2ec --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDelete.java @@ -0,0 +1,37 @@ +/* + * 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 java.util.Optional; + +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; + +/** + * Gets published when an entity is about to get deleted. + * + * @author Jens Schauder + * @since 2.0 + */ +public class BeforeDelete extends JdbcEventWithId { + + /** + * @param id the id of the entity + * @param entity the entity about to get deleted. Might be empty. + */ + public BeforeDelete(Specified id, Optional entity) { + super(id, entity); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDeleteEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDeleteEvent.java deleted file mode 100644 index 1e9a78997..000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeDeleteEvent.java +++ /dev/null @@ -1,38 +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 java.util.function.Function; - -import org.springframework.context.ApplicationEvent; - -/** - * gets published when an entity is about to get deleted. {@link ApplicationEvent#getSource()} might contain either the - * entity or the id of the entity, depending on which delete method was used. - * - * @author Jens Schauder - */ -public class BeforeDeleteEvent extends JdbcEvent { - - /** - * @param instance the entity about to get deleted. Might be {@literal NULL} - * @param idProvider a function providing the id, for the instance. Must provide a not {@literal NULL} id, when called with {@link #instance} - * @param type of the entity and the argument of the {@code idProvider} - */ - public BeforeDeleteEvent(T instance, Function idProvider) { - super(instance, idProvider); - } -} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsertEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java similarity index 60% rename from src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsertEvent.java rename to src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java index 45ecba984..c290cd442 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsertEvent.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java @@ -15,23 +15,23 @@ */ package org.springframework.data.jdbc.mapping.event; -import java.util.function.Function; +import org.springframework.data.jdbc.mapping.event.Identifier.Unset; /** - * gets published before an entity gets inserted into the database. + * 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. * - * 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 org.springframework.data.jdbc.mapping.event.Identifier.Unset#UNSET} * * @author Jens Schauder + * @since 2.0 */ -public class BeforeInsertEvent extends BeforeSaveEvent { +public class BeforeInsert extends BeforeSave { /** * @param instance the entity about to get inserted. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} */ - public BeforeInsertEvent(T instance, Function idProvider) { - super(instance, idProvider); + public BeforeInsert(Object instance) { + super(Unset.UNSET, instance); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeUpdateEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java similarity index 65% rename from src/main/java/org/springframework/data/jdbc/mapping/event/BeforeUpdateEvent.java rename to src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java index 543cbcd78..bf20430db 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeUpdateEvent.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java @@ -15,21 +15,21 @@ */ package org.springframework.data.jdbc.mapping.event; -import java.util.function.Function; +import java.util.Optional; /** - * gets published before an entity gets updated in the database. + * Subclasses of this get published before an entity gets saved to the database. * * @author Jens Schauder + * @since 2.0 */ -public class BeforeUpdateEvent extends BeforeSaveEvent { +public class BeforeSave extends JdbcEventWithEntity { /** + * @param id of the entity to be saved. * @param instance the entity about to get saved. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} */ - public BeforeUpdateEvent(T instance, Function idProvider) { - super(instance, idProvider); + BeforeSave(Identifier id, Object instance) { + super(id, instance); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSaveEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSaveEvent.java deleted file mode 100644 index 626c6f038..000000000 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSaveEvent.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 java.util.function.Function; - -import org.springframework.context.ApplicationEvent; - -/** - * subclasses of this get published before an entity gets saved to the database. - * - * @author Jens Schauder - */ -public class BeforeSaveEvent extends JdbcEvent { - - /** - * @param instance the entity about to get saved. - * @param idProvider a function providing the id, for the instance. - * @param type of the entity and the argument of the {@code idProvider} - */ - BeforeSaveEvent(T instance, Function idProvider) { - super(instance, idProvider); - } -} 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 new file mode 100644 index 000000000..72ad695d5 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/BeforeUpdate.java @@ -0,0 +1,35 @@ +/* + * 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 { + + /** + * @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); + } +} 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 new file mode 100644 index 000000000..492cc1edc --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/Identifier.java @@ -0,0 +1,65 @@ +/* + * 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 lombok.Data; +import lombok.NonNull; + +import java.util.Optional; + +/** + * Wrapper for an identifier of an entity. Might either be a {@link Specified} or {@link Unset#UNSET} + * + * @author Jens Schauder + * @since 2.0 + */ +public interface Identifier { + + Optional getOptionalValue(); + + static Identifier fromNullable(Object value) { + return (value != null) ? new Specified(value) : Unset.UNSET; + } + + /** + * An unset identifier. Always returns {@link Optional#empty()} as value. + */ + enum Unset implements Identifier { + UNSET { + @Override + public Optional getOptionalValue() { + return Optional.empty(); + } + } + } + + /** + * An {@link Identifier} guaranteed to have a non empty value. + * + * Since it is guaranteed to exist the value can get access directly. + */ + @Data + class Specified implements Identifier { + + @NonNull + private final Object value; + + @Override + public Optional getOptionalValue() { + return Optional.of(value); + } + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEvent.java b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEvent.java index ad0df42f2..59a1911d3 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEvent.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEvent.java @@ -15,48 +15,43 @@ */ package org.springframework.data.jdbc.mapping.event; -import java.util.function.Function; +import java.util.Optional; import org.springframework.context.ApplicationEvent; +import lombok.Getter; + /** - * is the common superclass for all events published by JDBC repositories. - * - * It is recommendet not to use the {@link #getSource()} since it may contain the entity if it was available, when the - * event was published, or in case of delete events only the Id. - * - * Use the dedicated methods {@link #getId()} or {@link #getInstance()} instead. Note that the later might be - * {@literal NULL} in the cases mentioned above. + * The common superclass for all events published by JDBC repositories. + * {@link #getSource} contains the {@link Identifier} of the entity triggering the event. * * @author Jens Schauder + * @since 2.0 */ +@Getter public class JdbcEvent extends ApplicationEvent { - private final Object id; - private final Object instance; - - JdbcEvent(T instance, Function idProvider) { - - super(instance == null ? idProvider.apply(instance) : instance); - this.instance = instance; - this.id = idProvider.apply(instance); - } - /** - * the entity for which this event was publish. Might be {@literal NULL} in cases of delete events where only the id + * The optional entity for which this event was published. Might be empty in cases of delete events where only the identifier * was provided to the delete method. * - * @return instance of the entity triggering this event. + * @return The entity triggering this event or empty. */ - public Object getInstance() { - return instance; + private final Optional optionalEntity; + + public JdbcEvent(Identifier id, Optional optionalEntity) { + super(id); + this.optionalEntity = optionalEntity; } /** - * the id of the entity, triggering this event. Guaranteed not to be {@literal NULL}. + * The identifier of the entity, triggering this event. Also available via + * {@link #getSource()}. + * * @return */ - public Object getId() { - return id; + public Identifier getId() { + return (Identifier) getSource(); } + } 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 new file mode 100644 index 000000000..c103dd6e0 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithEntity.java @@ -0,0 +1,31 @@ +/* + * 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 java.util.Optional; + +/** + * A {@link JdbcEvent} which is guaranteed to have an entity. + * + * @author Jens Schauder + * @since 2.0 + */ +public class JdbcEventWithEntity extends JdbcEvent implements WithEntity { + + public JdbcEventWithEntity(Identifier id, Object entity) { + super(id, Optional.of(entity)); + } +} 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 new file mode 100644 index 000000000..fa0c61d65 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithId.java @@ -0,0 +1,33 @@ +/* + * 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 java.util.Optional; + +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; + +/** + * A {@link JdbcEvent} guaranteed to have an identifier. + * + * @author Jens Schauder + * @since 2.0 + */ +public class JdbcEventWithId extends JdbcEvent implements WithId{ + + public JdbcEventWithId(Specified id, Optional entity) { + super(id, entity); + } +} 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 new file mode 100644 index 000000000..819ef8962 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithIdAndEntity.java @@ -0,0 +1,36 @@ +/* + * 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 lombok.Getter; + +import java.util.Optional; + +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; + +/** + * A {@link JdbcEvent} which is guaranteed to have an identifier and an entity. + * + * @author Jens Schauder + * @since 2.0 + */ +@Getter +public class JdbcEventWithIdAndEntity extends JdbcEvent implements WithId, WithEntity { + + public JdbcEventWithIdAndEntity(Specified id, Object entity) { + super(id, Optional.of(entity)); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/WithEntity.java b/src/main/java/org/springframework/data/jdbc/mapping/event/WithEntity.java new file mode 100644 index 000000000..a465fd54b --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/WithEntity.java @@ -0,0 +1,30 @@ +/* + * 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; + +/** + * Interface for {@link JdbcEvent}s which are guaranteed to have an entity. Allows direct access to that entity, without going through an {@link java.util.Optional} + * + * @author Jens Schauder + * @since 2.0 + */ +public interface WithEntity { + + default Object getEntity() { + return ((JdbcEvent) this).getOptionalEntity() + .orElseThrow(() -> new IllegalStateException("Entity must not be NULL")); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/event/WithId.java b/src/main/java/org/springframework/data/jdbc/mapping/event/WithId.java new file mode 100644 index 000000000..108a419ac --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/event/WithId.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * Interface for {@link JdbcEvent}s which are guaranteed to have a {@link Specified} identifier. + * + * Offers direct access to the {@link Specified} identifier. + * + * @author Jens Schauder + * @since 2.0 + */ +public interface WithId { + + default Specified getSpecifiedId(){ + return (Specified) ((JdbcEvent)this).getId(); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentProperty.java b/src/main/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentProperty.java new file mode 100644 index 000000000..a5edb0d00 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/mapping/model/BasicJdbcPersistentProperty.java @@ -0,0 +1,60 @@ +/* + * 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.model; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; + +import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; +import org.springframework.data.mapping.model.SimpleTypeHolder; + +/** + * Meta data about a property to be used by repository implementations. + * + * @author Jens Schauder + * @since 2.0 + */ +public class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProperty + implements JdbcPersistentProperty { + + /** + * Creates a new {@link AnnotationBasedPersistentProperty}. + * + * @param field must not be {@literal null}. + * @param propertyDescriptor can be {@literal null}. + * @param owner must not be {@literal null}. + * @param simpleTypeHolder + */ + public BasicJdbcPersistentProperty( // + Field field, // + PropertyDescriptor propertyDescriptor, // + PersistentEntity owner, // + SimpleTypeHolder simpleTypeHolder // + ) { + super(field, propertyDescriptor, owner, simpleTypeHolder); + } + + @Override + protected Association createAssociation() { + return null; + } + + public String getColumnName() { + return getName(); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntity.java b/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntity.java index bbe2d69cf..1c3a342f3 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntity.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntity.java @@ -20,38 +20,26 @@ import org.springframework.data.util.TypeInformation; /** * meta data a repository might need for implementing persistence operations for instances of type {@code T} + * * @author Jens Schauder + * @since 2.0 */ public class JdbcPersistentEntity extends BasicPersistentEntity { - private String tableName; - private String idColumn; + private final String tableName; public JdbcPersistentEntity(TypeInformation information) { + super(information); + + tableName = getType().getSimpleName(); } public String getTableName() { - - if (tableName == null) - tableName = getType().getSimpleName(); - return tableName; } public String getIdColumn() { - - if (idColumn == null) - idColumn = getIdProperty().getName(); - - return idColumn; - } - - public Object getIdValue(T instance) { - return getPropertyAccessor(instance).getProperty(getIdProperty()); - } - - public void setId(T instance, Object value) { - getPropertyAccessor(instance).setProperty(getIdProperty(),value); + return getIdProperty().getName(); } } diff --git a/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentProperty.java b/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentProperty.java index 841d64c65..e63cd4faf 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentProperty.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentProperty.java @@ -15,38 +15,13 @@ */ package org.springframework.data.jdbc.mapping.model; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import org.springframework.data.mapping.Association; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; -import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.mapping.PersistentProperty; /** - * meta data about a property to be used by repository implementations. - * * @author Jens Schauder + * @since 2.0 */ -public class JdbcPersistentProperty extends AnnotationBasedPersistentProperty { - - /** - * Creates a new {@link AnnotationBasedPersistentProperty}. - * - * @param field must not be {@literal null}. - * @param propertyDescriptor can be {@literal null}. - * @param owner must not be {@literal null}. - * @param simpleTypeHolder - */ - public JdbcPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { - super(field, propertyDescriptor, owner, simpleTypeHolder); - } - - @Override - protected Association createAssociation() { - return null; - } +public interface JdbcPersistentProperty extends PersistentProperty { - public String getColumnName() { - return getName(); - } + String getColumnName(); } diff --git a/src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java b/src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java index b60435215..7d6e19044 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java +++ b/src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.repository; import java.sql.ResultSet; import java.sql.SQLException; + import org.springframework.data.convert.ClassGeneratingEntityInstantiator; import org.springframework.data.convert.EntityInstantiator; import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; @@ -32,6 +33,7 @@ import org.springframework.jdbc.core.RowMapper; * maps a ResultSet to an entity of type {@code T} * * @author Jens Schauder + * @since 2.0 */ class EntityRowMapper implements RowMapper { @@ -48,7 +50,7 @@ class EntityRowMapper implements RowMapper { T t = createInstance(rs); - entity.doWithProperties((PropertyHandler) property -> { + entity.doWithProperties((PropertyHandler) property -> { setProperty(rs, t, property); }); @@ -57,12 +59,15 @@ class EntityRowMapper implements RowMapper { private T createInstance(ResultSet rs) { return instantiator.createInstance(entity, new ParameterValueProvider() { + @SuppressWarnings("unchecked") @Override public T getParameterValue(PreferredConstructor.Parameter parameter) { try { return (T) rs.getObject(parameter.getName()); } catch (SQLException e) { - throw new MappingException(String.format("Couldn't read column %s from ResultSet.", parameter.getName())); + throw new MappingException( // + String.format("Couldn't read column %s from ResultSet.", parameter.getName()) // + ); } } }); @@ -76,4 +81,4 @@ class EntityRowMapper implements RowMapper { throw new RuntimeException(String.format("Couldn't set property %s.", property.getName()), e); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapper.java b/src/main/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapper.java index b499296c8..1721c7873 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapper.java +++ b/src/main/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapper.java @@ -15,44 +15,37 @@ */ package org.springframework.data.jdbc.repository; +import java.io.Serializable; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.data.jdbc.mapping.event.AfterCreationEvent; -import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; +import org.springframework.data.jdbc.mapping.event.AfterCreation; +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; +import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation; import org.springframework.jdbc.core.RowMapper; +import lombok.RequiredArgsConstructor; + /** - * a RowMapper that publishes events after a delegate, did the actual work of mapping a {@link ResultSet} to an entity. + * a RowMapper that publishes events after a delegate, did the actual work of mapping a {@link ResultSet} to an entityInformation. * * @author Jens Schauder + * @since 2.0 */ -public class EventPublishingEntityRowMapper implements RowMapper { +@RequiredArgsConstructor +public class EventPublishingEntityRowMapper implements RowMapper { private final RowMapper delegate; - private final JdbcPersistentEntity entity; + private final JdbcPersistentEntityInformation entityInformation; private final ApplicationEventPublisher publisher; - /** - * - * @param delegate does the actuall mapping. - * @param entity provides functionality to create ids from entities - * @param publisher used for event publishing after the mapping. - */ - EventPublishingEntityRowMapper(RowMapper delegate,JdbcPersistentEntity entity, ApplicationEventPublisher publisher) { - - this.delegate = delegate; - this.entity = entity; - this.publisher = publisher; - } - @Override public T mapRow(ResultSet resultSet, int i) throws SQLException { T instance = delegate.mapRow(resultSet, i); - publisher.publishEvent(new AfterCreationEvent(instance, entity::getIdValue)); + publisher.publishEvent(new AfterCreation(new Specified(entityInformation.getId(instance)), instance)); return instance; } diff --git a/src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java b/src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java index 04aaf4672..ebff6a34d 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java +++ b/src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java @@ -16,21 +16,26 @@ package org.springframework.data.jdbc.repository; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.data.jdbc.mapping.event.AfterDeleteEvent; -import org.springframework.data.jdbc.mapping.event.AfterInsertEvent; -import org.springframework.data.jdbc.mapping.event.AfterUpdateEvent; -import org.springframework.data.jdbc.mapping.event.BeforeDeleteEvent; -import org.springframework.data.jdbc.mapping.event.BeforeInsertEvent; -import org.springframework.data.jdbc.mapping.event.BeforeUpdateEvent; +import org.springframework.dao.NonTransientDataAccessException; +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.BeforeDelete; +import org.springframework.data.jdbc.mapping.event.BeforeInsert; +import org.springframework.data.jdbc.mapping.event.BeforeUpdate; +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty; +import org.springframework.data.jdbc.repository.support.BasicJdbcPersistentEntityInformation; import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.repository.CrudRepository; @@ -42,11 +47,15 @@ import org.springframework.util.Assert; /** * @author Jens Schauder + * @since 2.0 */ -public class SimpleJdbcRepository - implements CrudRepository, ApplicationEventPublisherAware { +public class SimpleJdbcRepository implements CrudRepository { - private final JdbcPersistentEntity entity; + private static final String ENTITY_NEW_AFTER_INSERT = "Entity [%s] still 'new' after insert. Please set either" + + " the id property in a before insert event handler, or ensure the database creates a value and your " + + "JDBC driver returns it."; + + private final JdbcPersistentEntity persistentEntity; private final JdbcPersistentEntityInformation entityInformation; private final NamedParameterJdbcOperations operations; private final SqlGenerator sql; @@ -54,18 +63,19 @@ public class SimpleJdbcRepository private final EntityRowMapper entityRowMapper; private final ApplicationEventPublisher publisher; - public SimpleJdbcRepository(JdbcPersistentEntity persistentEntity, NamedParameterJdbcOperations jdbcOperations, ApplicationEventPublisher publisher) { + public SimpleJdbcRepository(JdbcPersistentEntity persistentEntity, NamedParameterJdbcOperations jdbcOperations, + ApplicationEventPublisher publisher) { Assert.notNull(persistentEntity, "PersistentEntity must not be null."); Assert.notNull(jdbcOperations, "JdbcOperations must not be null."); Assert.notNull(publisher, "Publisher must not be null."); - this.entity = persistentEntity; - this.entityInformation = new JdbcPersistentEntityInformation(persistentEntity); + this.persistentEntity = persistentEntity; + this.entityInformation = new BasicJdbcPersistentEntityInformation<>(persistentEntity); this.operations = jdbcOperations; this.publisher = publisher; - entityRowMapper = new EntityRowMapper(persistentEntity); + entityRowMapper = new EntityRowMapper<>(persistentEntity); sql = new SqlGenerator(persistentEntity); } @@ -84,29 +94,23 @@ public class SimpleJdbcRepository @Override public Iterable save(Iterable entities) { - entities.forEach(this::save); + List savedEntities = new ArrayList<>(); + + entities.forEach(e -> savedEntities.add(save(e))); - return entities; + return savedEntities; } @Override public T findOne(ID id) { - return operations.queryForObject( - sql.getFindOne(), - new MapSqlParameterSource("id", id), - entityRowMapper - ); + return operations.queryForObject(sql.getFindOne(), new MapSqlParameterSource("id", id), entityRowMapper); } @Override public boolean exists(ID id) { - return operations.queryForObject( - sql.getExists(), - new MapSqlParameterSource("id", id), - Boolean.class - ); + return operations.queryForObject(sql.getExists(), new MapSqlParameterSource("id", id), Boolean.class); } @Override @@ -126,27 +130,19 @@ public class SimpleJdbcRepository @Override public void delete(ID id) { - doDelete(id, null); + doDelete(new Specified(id), Optional.empty()); } @Override public void delete(T instance) { - - doDelete((ID) entity.getIdValue(instance), instance); + doDelete(new Specified(entityInformation.getId(instance)), Optional.of(instance)); } @Override public void delete(Iterable entities) { - operations.update( - sql.getDeleteByList(), - new MapSqlParameterSource("ids", - StreamSupport - .stream(entities.spliterator(), false) - .map(entity::getIdValue) - .collect(Collectors.toList()) - ) - ); + operations.update(sql.getDeleteByList(), new MapSqlParameterSource("ids", StreamSupport + .stream(entities.spliterator(), false).map(entityInformation::getId).collect(Collectors.toList()))); } @Override @@ -154,52 +150,73 @@ public class SimpleJdbcRepository operations.getJdbcOperations().update(sql.getDeleteAll()); } - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - - } - private Map getPropertyMap(final S instance) { Map parameters = new HashMap<>(); - this.entity.doWithProperties(new PropertyHandler() { - @Override - public void doWithPersistentProperty(JdbcPersistentProperty persistentProperty) { - parameters.put(persistentProperty.getColumnName(), entity.getPropertyAccessor(instance).getProperty(persistentProperty)); - } - }); + this.persistentEntity.doWithProperties((PropertyHandler) // + property -> parameters.put( // + property.getColumnName(), // + persistentEntity.getPropertyAccessor(instance).getProperty(property)) // + ); return parameters; } private void doInsert(S instance) { - publisher.publishEvent(new BeforeInsertEvent(instance, entity::getIdValue)); + + publisher.publishEvent(new BeforeInsert(instance)); KeyHolder holder = new GeneratedKeyHolder(); - operations.update( - sql.getInsert(), - new MapSqlParameterSource(getPropertyMap(instance)), - holder); + Map propertyMap = getPropertyMap(instance); + propertyMap.put(persistentEntity.getIdColumn(), getIdValueOrNull(instance)); - entity.setId(instance, holder.getKey()); + operations.update(sql.getInsert(), new MapSqlParameterSource(propertyMap), holder); + setIdFromJdbc(instance, holder); - publisher.publishEvent(new AfterInsertEvent(instance, entity::getIdValue)); + if (entityInformation.isNew(instance)) { + throw new IllegalStateException(String.format(ENTITY_NEW_AFTER_INSERT, persistentEntity)); + } + + publisher.publishEvent(new AfterInsert(new Specified(entityInformation.getId(instance)), instance)); } - private void doDelete(ID id, Object instance) { + private ID getIdValueOrNull(S instance) { - publisher.publishEvent(new BeforeDeleteEvent(instance, o -> id)); - operations.update(sql.getDeleteById(), new MapSqlParameterSource("id", id)); - publisher.publishEvent(new AfterDeleteEvent(instance, o -> id)); + ID idValue = entityInformation.getId(instance); + return isIdPropertySimpleTypeAndValueZero(idValue) ? null : idValue; + } + + private boolean isIdPropertySimpleTypeAndValueZero(ID idValue) { + + return (persistentEntity.getIdProperty().getType() == int.class && idValue.equals(0)) + || (persistentEntity.getIdProperty().getType() == long.class && idValue.equals(0L)); + } + + private void setIdFromJdbc(S instance, KeyHolder holder) { + try { + Number idValueFromJdbc = holder.getKey(); + if (idValueFromJdbc != null) { + entityInformation.setId(instance, idValueFromJdbc); + } + } catch (NonTransientDataAccessException e) { + throw new UnableToSetIdException("Unable to set id of " + instance, e); + } + } + + private void doDelete(Specified specifiedId, Optional optionalEntity) { + + publisher.publishEvent(new BeforeDelete(specifiedId, optionalEntity)); + operations.update(sql.getDeleteById(), new MapSqlParameterSource("id", specifiedId.getValue())); + publisher.publishEvent(new AfterDelete(specifiedId, optionalEntity)); } private void doUpdate(S instance) { - publisher.publishEvent(new BeforeUpdateEvent(instance, entity::getIdValue)); + Specified specifiedId = new Specified(entityInformation.getId(instance)); + publisher.publishEvent(new BeforeUpdate(specifiedId, instance)); operations.update(sql.getUpdate(), getPropertyMap(instance)); - - publisher.publishEvent(new AfterUpdateEvent(instance, entity::getIdValue)); + publisher.publishEvent(new AfterUpdate(specifiedId, instance)); } } diff --git a/src/main/java/org/springframework/data/jdbc/repository/SqlGenerator.java b/src/main/java/org/springframework/data/jdbc/repository/SqlGenerator.java index 2d613d78a..5036b979f 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/SqlGenerator.java +++ b/src/main/java/org/springframework/data/jdbc/repository/SqlGenerator.java @@ -24,6 +24,7 @@ import org.springframework.data.mapping.PropertyHandler; /** * @author Jens Schauder + * @since 2.0 */ class SqlGenerator { diff --git a/src/main/java/org/springframework/data/jdbc/repository/UnableToSetIdException.java b/src/main/java/org/springframework/data/jdbc/repository/UnableToSetIdException.java new file mode 100644 index 000000000..1bbb0126f --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/repository/UnableToSetIdException.java @@ -0,0 +1,31 @@ +/* + * 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 org.springframework.dao.NonTransientDataAccessException; + +/** + * Signals failure to set the id property of an entity. + * + * @author Jens Schauder + * @since 2.0 + */ +public class UnableToSetIdException extends NonTransientDataAccessException { + + public UnableToSetIdException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/BasicJdbcPersistentEntityInformation.java b/src/main/java/org/springframework/data/jdbc/repository/support/BasicJdbcPersistentEntityInformation.java new file mode 100644 index 000000000..ea22fccae --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/repository/support/BasicJdbcPersistentEntityInformation.java @@ -0,0 +1,42 @@ +/* + * 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.support; + +import java.io.Serializable; + +import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; +import org.springframework.data.repository.core.support.PersistentEntityInformation; + +/** + * @author Jens Schauder + * @since 2.0 + */ +public class BasicJdbcPersistentEntityInformation extends PersistentEntityInformation + implements JdbcPersistentEntityInformation { + + private final JdbcPersistentEntity persistentEntity; + + public BasicJdbcPersistentEntityInformation(JdbcPersistentEntity persistentEntity) { + super(persistentEntity); + + this.persistentEntity = persistentEntity; + } + + @Override + public void setId(T instance, Object value) { + persistentEntity.getPropertyAccessor(instance).setProperty(persistentEntity.getIdProperty(), value); + } +} diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcPersistentEntityInformation.java b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcPersistentEntityInformation.java index 97664acf0..53df278c9 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcPersistentEntityInformation.java +++ b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcPersistentEntityInformation.java @@ -16,15 +16,14 @@ package org.springframework.data.jdbc.repository.support; import java.io.Serializable; -import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; -import org.springframework.data.repository.core.support.PersistentEntityInformation; + +import org.springframework.data.repository.core.EntityInformation; /** * @author Jens Schauder + * @since 2.0 */ -public class JdbcPersistentEntityInformation extends PersistentEntityInformation { +public interface JdbcPersistentEntityInformation extends EntityInformation { - public JdbcPersistentEntityInformation(JdbcPersistentEntity persistentEntity) { - super(persistentEntity); - } + void setId(T instance, Object value); } diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java index f2e9b8952..2b92c3317 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jdbc.repository.support; +import lombok.RequiredArgsConstructor; + import java.io.Serializable; import org.springframework.context.ApplicationEventPublisher; @@ -29,34 +31,26 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; /** * @author Jens Schauder + * @since 2.0 */ +@RequiredArgsConstructor public class JdbcRepositoryFactory extends RepositoryFactorySupport { private final JdbcMappingContext context = new JdbcMappingContext(); - private final ApplicationEventPublisher publisher; private final NamedParameterJdbcOperations jdbcOperations; + private final ApplicationEventPublisher publisher; - public JdbcRepositoryFactory( - ApplicationEventPublisher publisher, - NamedParameterJdbcOperations jdbcOperations - ) { - - this.publisher = publisher; - this.jdbcOperations = jdbcOperations; - } - + @SuppressWarnings("unchecked") @Override public EntityInformation getEntityInformation(Class aClass) { - return new JdbcPersistentEntityInformation<>((JdbcPersistentEntity) context.getPersistentEntity(aClass)); + return new BasicJdbcPersistentEntityInformation<>((JdbcPersistentEntity) context.getPersistentEntity(aClass)); } @Override protected Object getTargetRepository(RepositoryInformation repositoryInformation) { - return new SimpleJdbcRepository( - context.getPersistentEntity(repositoryInformation.getDomainType()), - jdbcOperations, - publisher); + JdbcPersistentEntity persistentEntity = context.getPersistentEntity(repositoryInformation.getDomainType()); + return new SimpleJdbcRepository<>(persistentEntity, jdbcOperations, publisher); } @Override diff --git a/src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperTest.java b/src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperTest.java index 44113c48c..9fefea380 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperTest.java +++ b/src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperTest.java @@ -2,46 +2,46 @@ package org.springframework.data.jdbc.repository; import static org.mockito.Mockito.*; +import lombok.Data; + import java.sql.ResultSet; import java.sql.SQLException; import org.junit.Test; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.mapping.event.AfterCreationEvent; -import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity; +import org.springframework.data.jdbc.mapping.event.AfterCreation; +import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation; import org.springframework.jdbc.core.RowMapper; -import lombok.Data; - /** * @author Jens Schauder */ public class EventPublishingEntityRowMapperTest { - private RowMapper rowMapperDelegate = mock(RowMapper.class); - private JdbcPersistentEntity entity = mock(JdbcPersistentEntity.class); - private ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class); + RowMapper rowMapperDelegate = mock(RowMapper.class); + JdbcPersistentEntityInformation entityInformation = mock(JdbcPersistentEntityInformation.class); + ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class); @Test // DATAJDBC-99 public void eventGetsPublishedAfterInstantiation() throws SQLException { - when(entity.getIdValue(any())).thenReturn(1L); + when(entityInformation.getId(any())).thenReturn(1L); - EventPublishingEntityRowMapper rowMapper = new EventPublishingEntityRowMapper<>( - rowMapperDelegate, - entity, + EventPublishingEntityRowMapper rowMapper = new EventPublishingEntityRowMapper<>( // + rowMapperDelegate, // + entityInformation, // publisher); ResultSet resultSet = mock(ResultSet.class); rowMapper.mapRow(resultSet, 1); - verify(publisher).publishEvent(isA(AfterCreationEvent.class)); + verify(publisher).publishEvent(isA(AfterCreation.class)); } @Data - private static class DummyEntity { + static class DummyEntity { @Id private final Long Id; } -} \ No newline at end of file +} diff --git a/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java index f537f6d00..9a1cf50d2 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java @@ -17,96 +17,71 @@ package org.springframework.data.jdbc.repository; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.*; + +import lombok.Data; -import org.junit.After; import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.data.annotation.Id; +import org.springframework.data.jdbc.repository.JdbcRepositoryIdGenerationIntegrationTests.TestConfiguration; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; - -import lombok.Data; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** - * testing special cases for Id generation with JdbcRepositories. + * Testing special cases for id generation with {@link SimpleJdbcRepository}. * * @author Jens Schauder */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestConfiguration.class) public class JdbcRepositoryIdGenerationIntegrationTests { - private final EmbeddedDatabase db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .ignoreFailedDrops(true) - .addScript("org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql") - .build(); + @Autowired NamedParameterJdbcTemplate template; - private final NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(db); + @Autowired ReadOnlyIdEntityRepository readOnlyIdrepository; - private final ReadOnlyIdEntityRepository repository = createRepository(db); - - private ReadOnlyIdEntity entity = createDummyEntity(); - - @After - public void after() { - db.shutdown(); - } + @Autowired PrimitiveIdEntityRepository primitiveIdRepository; @Test // DATAJDBC-98 public void idWithoutSetterGetsSet() { - entity = repository.save(entity); + ReadOnlyIdEntity entity1 = new ReadOnlyIdEntity(null); + entity1.setName("Entity Name"); + ReadOnlyIdEntity entity = entity1; + entity = readOnlyIdrepository.save(entity); assertThat(entity.getId()).isNotNull(); - ReadOnlyIdEntity reloadedEntity = repository.findOne(entity.getId()); + ReadOnlyIdEntity reloadedEntity = readOnlyIdrepository.findOne(entity.getId()); - assertEquals( - entity.getId(), - reloadedEntity.getId()); - assertEquals( - entity.getName(), - reloadedEntity.getName()); + assertEquals(entity.getId(), reloadedEntity.getId()); + assertEquals(entity.getName(), reloadedEntity.getName()); } @Test // DATAJDBC-98 public void primitiveIdGetsSet() { - entity = repository.save(entity); + PrimitiveIdEntity entity = new PrimitiveIdEntity(0); + entity.setName("Entity Name"); + entity = primitiveIdRepository.save(entity); assertThat(entity.getId()).isNotNull(); - ReadOnlyIdEntity reloadedEntity = repository.findOne(entity.getId()); - - assertEquals( - entity.getId(), - reloadedEntity.getId()); - assertEquals( - entity.getName(), - reloadedEntity.getName()); - } - - - private static ReadOnlyIdEntityRepository createRepository(EmbeddedDatabase db) { - - return new JdbcRepositoryFactory( - mock(ApplicationEventPublisher.class), - new NamedParameterJdbcTemplate(db) - ).getRepository(ReadOnlyIdEntityRepository.class); - } - + PrimitiveIdEntity reloadedEntity = primitiveIdRepository.findOne(entity.getId()); - private static ReadOnlyIdEntity createDummyEntity() { - - ReadOnlyIdEntity entity = new ReadOnlyIdEntity(null); - entity.setName("Entity Name"); - return entity; + assertEquals(entity.getId(), reloadedEntity.getId()); + assertEquals(entity.getName(), reloadedEntity.getName()); } private interface ReadOnlyIdEntityRepository extends CrudRepository { @@ -116,8 +91,7 @@ public class JdbcRepositoryIdGenerationIntegrationTests { @Data static class ReadOnlyIdEntity { - @Id - private final Long id; + @Id private final Long id; String name; } @@ -128,8 +102,43 @@ public class JdbcRepositoryIdGenerationIntegrationTests { @Data static class PrimitiveIdEntity { - @Id - private final Long id; + @Id private final long id; String name; } + + @Configuration + static class TestConfiguration { + + @Bean + EmbeddedDatabase dataSource() { + + System.out.println(" creating datasource"); + return new EmbeddedDatabaseBuilder() // + .generateUniqueName(true) // + .setType(EmbeddedDatabaseType.HSQL) // + .setScriptEncoding("UTF-8") // + .ignoreFailedDrops(true) // + .addScript("org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql") + .build(); + } + + @Bean + NamedParameterJdbcTemplate template(EmbeddedDatabase db) { + return new NamedParameterJdbcTemplate(db); + } + + @Bean + ReadOnlyIdEntityRepository readOnlyIdRepository(EmbeddedDatabase db) { + + return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class)) + .getRepository(ReadOnlyIdEntityRepository.class); + } + + @Bean + PrimitiveIdEntityRepository primitiveIdRepository(NamedParameterJdbcTemplate template) { + + return new JdbcRepositoryFactory(template, mock(ApplicationEventPublisher.class)) + .getRepository(PrimitiveIdEntityRepository.class); + } + } } diff --git a/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java index 8a90a9c54..9bebfc09e 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java @@ -18,91 +18,86 @@ package org.springframework.data.jdbc.repository; import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.*; + +import lombok.Data; + +import javax.sql.DataSource; -import org.junit.After; import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.data.annotation.Id; +import org.springframework.data.jdbc.repository.JdbcRepositoryIntegrationTests.TestConfiguration; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; - -import lombok.Data; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Transactional; /** - * very simple use cases for creation and usage of JdbcRepositories. + * Very simple use cases for creation and usage of JdbcRepositories. * * @author Jens Schauder */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestConfiguration.class) +@Transactional public class JdbcRepositoryIntegrationTests { - private final EmbeddedDatabase db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .ignoreFailedDrops(true) - .addScript("org.springframework.data.jdbc.repository/jdbc-repository-integration-tests.sql") - .build(); - - private final NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(db); + @Autowired NamedParameterJdbcTemplate template; - private final DummyEntityRepository repository = createRepository(db); + @Autowired DummyEntityRepository repository; private DummyEntity entity = createDummyEntity(); - @After - public void after() { - db.shutdown(); - } - @Test // DATAJDBC-95 - public void canSaveAnEntity() { + public void savesAnEntity() { entity = repository.save(entity); - int count = template.queryForObject( - "SELECT count(*) FROM dummyentity WHERE idProp = :id", - new MapSqlParameterSource("id", entity.getIdProp()), - Integer.class); + int count = template.queryForObject( // + "SELECT count(*) FROM dummyentity WHERE idProp = :id", // + new MapSqlParameterSource("id", entity.getIdProp()), // + Integer.class // + ); - assertEquals( - 1, - count); + assertEquals(1, count); } @Test // DATAJDBC-95 - public void canSaveAndLoadAnEntity() { + public void saveAndLoadAnEntity() { entity = repository.save(entity); DummyEntity reloadedEntity = repository.findOne(entity.getIdProp()); - assertEquals( - entity.getIdProp(), - reloadedEntity.getIdProp()); - assertEquals( - entity.getName(), - reloadedEntity.getName()); + assertEquals(entity.getIdProp(), reloadedEntity.getIdProp()); + assertEquals(entity.getName(), reloadedEntity.getName()); } @Test // DATAJDBC-97 - public void saveMany() { + public void savesManyEntities() { DummyEntity other = createDummyEntity(); repository.save(asList(entity, other)); - assertThat(repository.findAll()) - .extracting(DummyEntity::getIdProp) - .containsExactlyInAnyOrder( - entity.getIdProp(), - other.getIdProp() - ); + assertThat(repository.findAll()) // + .extracting(DummyEntity::getIdProp) // + .containsExactlyInAnyOrder( // + entity.getIdProp(), other.getIdProp() // + ); } @Test // DATAJDBC-97 @@ -124,8 +119,7 @@ public class JdbcRepositoryIntegrationTests { Iterable all = repository.findAll(); - assertThat(all).extracting("idProp") - .containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp()); + assertThat(all).extracting("idProp").containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp()); } @Test // DATAJDBC-97 @@ -137,12 +131,11 @@ public class JdbcRepositoryIntegrationTests { Iterable all = repository.findAll(asList(entity.getIdProp(), three.getIdProp())); - assertThat(all).extracting("idProp") - .containsExactlyInAnyOrder(entity.getIdProp(), three.getIdProp()); + assertThat(all).extracting("idProp").containsExactlyInAnyOrder(entity.getIdProp(), three.getIdProp()); } @Test // DATAJDBC-97 - public void count() { + public void countsEntities() { repository.save(createDummyEntity()); repository.save(createDummyEntity()); @@ -151,7 +144,7 @@ public class JdbcRepositoryIntegrationTests { assertThat(repository.count()).isEqualTo(3L); } - @Test // DATAJDBC-97 + @Test // DATAJDBC-97 public void deleteById() { entity = repository.save(entity); @@ -160,9 +153,11 @@ public class JdbcRepositoryIntegrationTests { repository.delete(two.getIdProp()); - assertThat(repository.findAll()) - .extracting(DummyEntity::getIdProp) - .containsExactlyInAnyOrder(entity.getIdProp(), three.getIdProp()); + assertThat(repository.findAll()) // + .extracting(DummyEntity::getIdProp) // + .containsExactlyInAnyOrder( // + entity.getIdProp(), three.getIdProp() // + ); } @Test // DATAJDBC-97 @@ -174,15 +169,13 @@ public class JdbcRepositoryIntegrationTests { repository.delete(entity); - assertThat(repository.findAll()) - .extracting(DummyEntity::getIdProp) - .containsExactlyInAnyOrder( - two.getIdProp(), - three.getIdProp() - ); + assertThat(repository.findAll()) // + .extracting(DummyEntity::getIdProp) // + .containsExactlyInAnyOrder( // + two.getIdProp(), three.getIdProp() // + ); } - @Test // DATAJDBC-97 public void deleteByList() { @@ -195,7 +188,7 @@ public class JdbcRepositoryIntegrationTests { assertThat(repository.findAll()).extracting(DummyEntity::getIdProp).containsExactlyInAnyOrder(two.getIdProp()); } - @Test // DATAJDBC-97 + @Test // DATAJDBC-97 public void deleteAll() { repository.save(entity); @@ -207,7 +200,6 @@ public class JdbcRepositoryIntegrationTests { assertThat(repository.findAll()).isEmpty(); } - @Test // DATAJDBC-98 public void update() { @@ -233,18 +225,16 @@ public class JdbcRepositoryIntegrationTests { repository.save(asList(entity, other)); - assertThat(repository.findAll()) - .extracting(DummyEntity::getName) - .containsExactlyInAnyOrder(entity.getName(), other.getName()); + assertThat(repository.findAll()).extracting(DummyEntity::getName).containsExactlyInAnyOrder(entity.getName(), + other.getName()); } private static DummyEntityRepository createRepository(EmbeddedDatabase db) { - return new JdbcRepositoryFactory(mock(ApplicationEventPublisher.class), new NamedParameterJdbcTemplate(db)) + return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class)) .getRepository(DummyEntityRepository.class); } - private static DummyEntity createDummyEntity() { DummyEntity entity = new DummyEntity(); @@ -259,8 +249,42 @@ public class JdbcRepositoryIntegrationTests { @Data static class DummyEntity { - @Id - private Long idProp; + @Id private Long idProp; String name; } + + @Configuration + static class TestConfiguration { + + @Bean + EmbeddedDatabase dataSource() { + + System.out.println(" creating datasource"); + return new EmbeddedDatabaseBuilder() // + .generateUniqueName(true) // + .setType(EmbeddedDatabaseType.HSQL) // + .setScriptEncoding("UTF-8") // + .ignoreFailedDrops(true) // + .addScript("org.springframework.data.jdbc.repository/jdbc-repository-integration-tests.sql") // + .build(); + } + + @Bean + NamedParameterJdbcTemplate template(EmbeddedDatabase db) { + return new NamedParameterJdbcTemplate(db); + } + + @Bean + DummyEntityRepository readOnlyIdRepository(NamedParameterJdbcTemplate template) { + + return new JdbcRepositoryFactory(template, mock(ApplicationEventPublisher.class)) + .getRepository(DummyEntityRepository.class); + } + + @Bean + PlatformTransactionManager transactionManager(DataSource db) { + return new DataSourceTransactionManager(db); + } + + } } 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 36c06e17c..6c34bd934 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java @@ -1,27 +1,33 @@ package org.springframework.data.jdbc.repository; import static java.util.Arrays.*; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; import static org.springframework.util.Assert.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import org.junit.Before; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.annotation.Id; -import org.springframework.data.jdbc.mapping.event.AfterDeleteEvent; -import org.springframework.data.jdbc.mapping.event.AfterInsertEvent; -import org.springframework.data.jdbc.mapping.event.AfterUpdateEvent; -import org.springframework.data.jdbc.mapping.event.BeforeDeleteEvent; -import org.springframework.data.jdbc.mapping.event.BeforeInsertEvent; -import org.springframework.data.jdbc.mapping.event.BeforeUpdateEvent; +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.BeforeDelete; +import org.springframework.data.jdbc.mapping.event.BeforeInsert; +import org.springframework.data.jdbc.mapping.event.BeforeUpdate; +import org.springframework.data.jdbc.mapping.event.Identifier.Specified; import org.springframework.data.jdbc.mapping.event.JdbcEvent; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.repository.CrudRepository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.support.KeyHolder; import lombok.Data; @@ -36,10 +42,25 @@ public class SimpleJdbcRepositoryEventsUnitTests { @Before public void before() { - JdbcRepositoryFactory factory = new JdbcRepositoryFactory(publisher, mock(NamedParameterJdbcOperations.class)); + NamedParameterJdbcOperations operations = createIdGeneratingOperations(); + JdbcRepositoryFactory factory = new JdbcRepositoryFactory(operations, publisher); repository = factory.getRepository(DummyEntityRepository.class); } + private NamedParameterJdbcOperations createIdGeneratingOperations() { + NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class); + when(operations.update(anyString(), any(SqlParameterSource.class), any(KeyHolder.class))).thenAnswer(new Answer() { + @Override + public Integer answer(InvocationOnMock invocation) throws Throwable { + HashMap keys = new HashMap<>(); + keys.put("id", 4711L); + invocation.getArgumentAt(2, KeyHolder.class).getKeyList().add(keys); + return 1; + } + }); + return operations; + } + @Test // DATAJDBC-99 public void publishesEventsOnSave() { @@ -47,22 +68,24 @@ public class SimpleJdbcRepositoryEventsUnitTests { repository.save(entity); - isInstanceOf(BeforeUpdateEvent.class, publisher.events.get(0)); - isInstanceOf(AfterUpdateEvent.class, publisher.events.get(1)); + isInstanceOf(BeforeUpdate.class, publisher.events.get(0)); + isInstanceOf(AfterUpdate.class, publisher.events.get(1)); } @Test // DATAJDBC-99 public void publishesEventsOnSaveMany() { + + DummyEntity entity1 = new DummyEntity(null); DummyEntity entity2 = new DummyEntity(23L); repository.save(asList(entity1, entity2)); - isInstanceOf(BeforeInsertEvent.class, publisher.events.get(0)); - isInstanceOf(AfterInsertEvent.class, publisher.events.get(1)); - isInstanceOf(BeforeUpdateEvent.class, publisher.events.get(2)); - isInstanceOf(AfterUpdateEvent.class, publisher.events.get(3)); + isInstanceOf(BeforeInsert.class, publisher.events.get(0)); + isInstanceOf(AfterInsert.class, publisher.events.get(1)); + isInstanceOf(BeforeUpdate.class, publisher.events.get(2)); + isInstanceOf(AfterUpdate.class, publisher.events.get(3)); } @@ -73,27 +96,25 @@ public class SimpleJdbcRepositoryEventsUnitTests { repository.delete(entity); - isInstanceOf(BeforeDeleteEvent.class, publisher.events.get(0)); - isInstanceOf(AfterDeleteEvent.class, publisher.events.get(1)); + isInstanceOf(BeforeDelete.class, publisher.events.get(0)); + isInstanceOf(AfterDelete.class, publisher.events.get(1)); - assertEquals(entity, publisher.events.get(0).getInstance()); - assertEquals(entity, publisher.events.get(1).getInstance()); + assertEquals(entity, publisher.events.get(0).getOptionalEntity().get()); + assertEquals(entity, publisher.events.get(1).getOptionalEntity().get()); - assertEquals(23L, publisher.events.get(0).getId()); - assertEquals(23L, publisher.events.get(1).getId()); + assertEquals(new Specified(23L), publisher.events.get(0).getId()); + assertEquals(new Specified(23L), publisher.events.get(1).getId()); } - @Test // DATAJDBC-99 public void publishesEventsOnDeleteById() { repository.delete(23L); - isInstanceOf(BeforeDeleteEvent.class, publisher.events.get(0)); - isInstanceOf(AfterDeleteEvent.class, publisher.events.get(1)); + isInstanceOf(BeforeDelete.class, publisher.events.get(0)); + isInstanceOf(AfterDelete.class, publisher.events.get(1)); } - @Data private static class DummyEntity { diff --git a/src/test/resources/org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql b/src/test/resources/org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql index 0e008210e..c7a764b23 100644 --- a/src/test/resources/org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql +++ b/src/test/resources/org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql @@ -1,3 +1,5 @@ -- noinspection SqlNoDataSourceInspectionForFile -CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)) \ No newline at end of file +CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)) + +CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100)) \ No newline at end of file