DATAJDBC-99 - Event support.
The repository publishes events before and after inserting, updating and deleting entities, as well as after instantiation of entities. JdbcEvent ist the common super class of all events and makes the id and the entity available (if possible). Added issue id comments to tests from previous issues. Original pull request: #5.
This commit is contained in:
committed by
Oliver Gierke
parent
1f1753e734
commit
25fa2d423e
@@ -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 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 <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
public <T> AfterCreationEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -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 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 <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
public <T> AfterDeleteEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -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 java.util.function.Function;
|
||||
|
||||
/**
|
||||
* gets published after an entity got inserted into the database.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class AfterInsertEvent extends AfterSaveEvent {
|
||||
|
||||
/**
|
||||
* @param instance the newly inserted entity.
|
||||
* @param idProvider a function providing the id, for the instance.
|
||||
* @param <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
public <T> AfterInsertEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* subclasses of this get published after a new instance or a changed instance was saved in the database
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class AfterSaveEvent extends JdbcEvent{
|
||||
|
||||
/**
|
||||
* @param instance the newly saved entity.
|
||||
* @param idProvider a function providing the id, for the instance.
|
||||
* @param <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
<T> AfterSaveEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -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 java.util.function.Function;
|
||||
|
||||
/**
|
||||
* gets published after an entity was updated in the database.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class AfterUpdateEvent extends AfterSaveEvent {
|
||||
|
||||
/**
|
||||
* @param instance the updated entity.
|
||||
* @param idProvider a function providing the id, for the instance.
|
||||
* @param <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
public <T> AfterUpdateEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
public <T> BeforeDeleteEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -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.function.Function;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class BeforeInsertEvent extends BeforeSaveEvent {
|
||||
|
||||
/**
|
||||
* @param instance the entity about to get inserted.
|
||||
* @param idProvider a function providing the id, for the instance.
|
||||
* @param <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
public <T> BeforeInsertEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -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.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 <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
<T> BeforeSaveEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -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 java.util.function.Function;
|
||||
|
||||
/**
|
||||
* gets published before an entity gets updated in the database.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class BeforeUpdateEvent extends BeforeSaveEvent {
|
||||
|
||||
/**
|
||||
* @param instance the entity about to get saved.
|
||||
* @param idProvider a function providing the id, for the instance.
|
||||
* @param <T> type of the entity and the argument of the {@code idProvider}
|
||||
*/
|
||||
public <T> BeforeUpdateEvent(T instance, Function<T, Object> idProvider) {
|
||||
super(instance, idProvider);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class JdbcEvent extends ApplicationEvent {
|
||||
|
||||
private final Object id;
|
||||
private final Object instance;
|
||||
|
||||
<T> JdbcEvent(T instance, Function<T, Object> 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
|
||||
* was provided to the delete method.
|
||||
*
|
||||
* @return instance of the entity triggering this event.
|
||||
*/
|
||||
public Object getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* the id of the entity, triggering this event. Guaranteed not to be {@literal NULL}.
|
||||
* @return
|
||||
*/
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -26,13 +26,14 @@ import org.springframework.data.mapping.PreferredConstructor;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* maps a ResultSet to an entity of type {@code T}
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class EntityRowMapper<T> implements org.springframework.jdbc.core.RowMapper<T> {
|
||||
class EntityRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
private final JdbcPersistentEntity<T> entity;
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 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.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* a RowMapper that publishes events after a delegate, did the actual work of mapping a {@link ResultSet} to an entity.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class EventPublishingEntityRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
private final RowMapper<T> delegate;
|
||||
private final JdbcPersistentEntity<T> entity;
|
||||
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<T> delegate,JdbcPersistentEntity<T> 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));
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,15 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
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.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation;
|
||||
@@ -28,47 +36,46 @@ import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRepository<T, ID> {
|
||||
public class SimpleJdbcRepository<T, ID extends Serializable>
|
||||
implements CrudRepository<T, ID>, ApplicationEventPublisherAware {
|
||||
|
||||
private final JdbcPersistentEntity<T> entity;
|
||||
private final JdbcPersistentEntityInformation<T,ID> entityInformation;
|
||||
private final NamedParameterJdbcOperations template;
|
||||
private final JdbcPersistentEntityInformation<T, ID> entityInformation;
|
||||
private final NamedParameterJdbcOperations operations;
|
||||
private final SqlGenerator sql;
|
||||
|
||||
private final EntityRowMapper<T> entityRowMapper;
|
||||
private final ApplicationEventPublisher publisher;
|
||||
|
||||
public SimpleJdbcRepository(JdbcPersistentEntity<T> entity, DataSource dataSource) {
|
||||
public SimpleJdbcRepository(JdbcPersistentEntity<T> persistentEntity, NamedParameterJdbcOperations jdbcOperations, ApplicationEventPublisher publisher) {
|
||||
|
||||
this.entity = entity;
|
||||
this.entityInformation = new JdbcPersistentEntityInformation<T, ID>(entity);
|
||||
this.template = new NamedParameterJdbcTemplate(dataSource);
|
||||
Assert.notNull(persistentEntity, "PersistentEntity must not be null.");
|
||||
Assert.notNull(jdbcOperations, "JdbcOperations must not be null.");
|
||||
Assert.notNull(publisher, "Publisher must not be null.");
|
||||
|
||||
entityRowMapper = new EntityRowMapper<T>(entity);
|
||||
sql = new SqlGenerator(entity);
|
||||
this.entity = persistentEntity;
|
||||
this.entityInformation = new JdbcPersistentEntityInformation<T, ID>(persistentEntity);
|
||||
this.operations = jdbcOperations;
|
||||
this.publisher = publisher;
|
||||
|
||||
entityRowMapper = new EntityRowMapper<T>(persistentEntity);
|
||||
sql = new SqlGenerator(persistentEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends T> S save(S instance) {
|
||||
|
||||
if (entityInformation.isNew(instance)) {
|
||||
|
||||
KeyHolder holder = new GeneratedKeyHolder();
|
||||
|
||||
template.update(
|
||||
sql.getInsert(),
|
||||
new MapSqlParameterSource(getPropertyMap(instance)),
|
||||
holder);
|
||||
|
||||
entity.setId(instance, holder.getKey());
|
||||
doInsert(instance);
|
||||
} else {
|
||||
template.update(sql.getUpdate(), getPropertyMap(instance));
|
||||
doUpdate(instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
@@ -85,7 +92,7 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
@Override
|
||||
public T findOne(ID id) {
|
||||
|
||||
return template.queryForObject(
|
||||
return operations.queryForObject(
|
||||
sql.getFindOne(),
|
||||
new MapSqlParameterSource("id", id),
|
||||
entityRowMapper
|
||||
@@ -95,7 +102,7 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
@Override
|
||||
public boolean exists(ID id) {
|
||||
|
||||
return template.queryForObject(
|
||||
return operations.queryForObject(
|
||||
sql.getExists(),
|
||||
new MapSqlParameterSource("id", id),
|
||||
Boolean.class
|
||||
@@ -104,37 +111,34 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAll() {
|
||||
return template.query(sql.getFindAll(), entityRowMapper);
|
||||
return operations.query(sql.getFindAll(), entityRowMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> findAll(Iterable<ID> ids) {
|
||||
return template.query(sql.getFindAllInList(), new MapSqlParameterSource("ids", ids), entityRowMapper);
|
||||
return operations.query(sql.getFindAllInList(), new MapSqlParameterSource("ids", ids), entityRowMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return template.getJdbcOperations().queryForObject(sql.getCount(), Long.class);
|
||||
return operations.getJdbcOperations().queryForObject(sql.getCount(), Long.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(ID id) {
|
||||
template.update(sql.getDeleteById(), new MapSqlParameterSource("id", id));
|
||||
doDelete(id, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(T instance) {
|
||||
|
||||
template.update(
|
||||
sql.getDeleteById(),
|
||||
new MapSqlParameterSource("id",
|
||||
entity.getIdValue(instance)));
|
||||
doDelete((ID) entity.getIdValue(instance), instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Iterable<? extends T> entities) {
|
||||
|
||||
template.update(
|
||||
operations.update(
|
||||
sql.getDeleteByList(),
|
||||
new MapSqlParameterSource("ids",
|
||||
StreamSupport
|
||||
@@ -147,7 +151,12 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
template.getJdbcOperations().update(sql.getDeleteAll());
|
||||
operations.getJdbcOperations().update(sql.getDeleteAll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
|
||||
}
|
||||
|
||||
private <S extends T> Map<String, Object> getPropertyMap(final S instance) {
|
||||
@@ -163,4 +172,34 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private <S extends T> void doInsert(S instance) {
|
||||
publisher.publishEvent(new BeforeInsertEvent(instance, entity::getIdValue));
|
||||
|
||||
KeyHolder holder = new GeneratedKeyHolder();
|
||||
|
||||
operations.update(
|
||||
sql.getInsert(),
|
||||
new MapSqlParameterSource(getPropertyMap(instance)),
|
||||
holder);
|
||||
|
||||
entity.setId(instance, holder.getKey());
|
||||
|
||||
publisher.publishEvent(new AfterInsertEvent(instance, entity::getIdValue));
|
||||
}
|
||||
|
||||
private void doDelete(ID id, Object instance) {
|
||||
|
||||
publisher.publishEvent(new BeforeDeleteEvent(instance, o -> id));
|
||||
operations.update(sql.getDeleteById(), new MapSqlParameterSource("id", id));
|
||||
publisher.publishEvent(new AfterDeleteEvent(instance, o -> id));
|
||||
}
|
||||
|
||||
private <S extends T> void doUpdate(S instance) {
|
||||
publisher.publishEvent(new BeforeUpdateEvent(instance, entity::getIdValue));
|
||||
|
||||
operations.update(sql.getUpdate(), getPropertyMap(instance));
|
||||
|
||||
publisher.publishEvent(new AfterUpdateEvent(instance, entity::getIdValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.jdbc.mapping.context.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.repository.SimpleJdbcRepository;
|
||||
@@ -24,27 +25,38 @@ import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class JdbcRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private final DataSource dataSource;
|
||||
private final JdbcMappingContext context = new JdbcMappingContext();
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final NamedParameterJdbcOperations jdbcOperations;
|
||||
|
||||
public JdbcRepositoryFactory(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
public JdbcRepositoryFactory(
|
||||
ApplicationEventPublisher publisher,
|
||||
NamedParameterJdbcOperations jdbcOperations
|
||||
) {
|
||||
|
||||
this.publisher = publisher;
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, ID extends Serializable> EntityInformation<T, ID> getEntityInformation(Class<T> aClass) {
|
||||
return new JdbcPersistentEntityInformation<T, ID>((JdbcPersistentEntity<T>) context.getPersistentEntity(aClass));
|
||||
return new JdbcPersistentEntityInformation<>((JdbcPersistentEntity<T>) context.getPersistentEntity(aClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getTargetRepository(RepositoryInformation repositoryInformation) {
|
||||
return new SimpleJdbcRepository(context.getPersistentEntity(repositoryInformation.getDomainType()), dataSource);
|
||||
|
||||
return new SimpleJdbcRepository(
|
||||
context.getPersistentEntity(repositoryInformation.getDomainType()),
|
||||
jdbcOperations,
|
||||
publisher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
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.jdbc.core.RowMapper;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class EventPublishingEntityRowMapperTest {
|
||||
|
||||
private RowMapper rowMapperDelegate = mock(RowMapper.class);
|
||||
private JdbcPersistentEntity<DummyEntity> entity = mock(JdbcPersistentEntity.class);
|
||||
private ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void eventGetsPublishedAfterInstantiation() throws SQLException {
|
||||
|
||||
when(entity.getIdValue(any())).thenReturn(1L);
|
||||
|
||||
EventPublishingEntityRowMapper<DummyEntity> rowMapper = new EventPublishingEntityRowMapper<>(
|
||||
rowMapperDelegate,
|
||||
entity,
|
||||
publisher);
|
||||
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
rowMapper.mapRow(resultSet, 1);
|
||||
|
||||
verify(publisher).publishEvent(isA(AfterCreationEvent.class));
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class DummyEntity {
|
||||
|
||||
@Id private final Long Id;
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,11 @@ 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 org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
@@ -56,7 +58,7 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
|
||||
db.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-98
|
||||
public void idWithoutSetterGetsSet() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -73,7 +75,7 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
|
||||
reloadedEntity.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-98
|
||||
public void primitiveIdGetsSet() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -92,7 +94,11 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
|
||||
|
||||
|
||||
private static ReadOnlyIdEntityRepository createRepository(EmbeddedDatabase db) {
|
||||
return new JdbcRepositoryFactory(db).getRepository(ReadOnlyIdEntityRepository.class);
|
||||
|
||||
return new JdbcRepositoryFactory(
|
||||
mock(ApplicationEventPublisher.class),
|
||||
new NamedParameterJdbcTemplate(db)
|
||||
).getRepository(ReadOnlyIdEntityRepository.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,9 +18,11 @@ 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 org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
@@ -58,8 +60,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
db.shutdown();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-95
|
||||
public void canSaveAnEntity() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -74,7 +75,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
count);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-95
|
||||
public void canSaveAndLoadAnEntity() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -89,17 +90,22 @@ public class JdbcRepositoryIntegrationTests {
|
||||
reloadedEntity.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-97
|
||||
public void saveMany() {
|
||||
|
||||
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
|
||||
@Test // DATAJDBC-97
|
||||
public void existsReturnsTrueIffEntityExists() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -108,7 +114,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertFalse(repository.exists(entity.getIdProp() + 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllEntities() {
|
||||
|
||||
DummyEntity other = createDummyEntity();
|
||||
@@ -118,10 +124,11 @@ public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
Iterable<DummyEntity> all = repository.findAll();
|
||||
|
||||
assertThat(all).extracting("idProp").containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
assertThat(all).extracting("idProp")
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllSpecifiedEntities() {
|
||||
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
@@ -130,10 +137,11 @@ public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
Iterable<DummyEntity> 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
|
||||
@Test // DATAJDBC-97
|
||||
public void count() {
|
||||
|
||||
repository.save(createDummyEntity());
|
||||
@@ -143,7 +151,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(repository.count()).isEqualTo(3L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteById() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -157,7 +165,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), three.getIdProp());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByEntity() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -166,11 +174,16 @@ 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
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByList() {
|
||||
|
||||
repository.save(entity);
|
||||
@@ -182,7 +195,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(repository.findAll()).extracting(DummyEntity::getIdProp).containsExactlyInAnyOrder(two.getIdProp());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteAll() {
|
||||
|
||||
repository.save(entity);
|
||||
@@ -195,7 +208,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-98
|
||||
public void update() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -209,7 +222,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(reloaded.getName()).isEqualTo(entity.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // DATAJDBC-98
|
||||
public void updateMany() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
@@ -226,7 +239,9 @@ public class JdbcRepositoryIntegrationTests {
|
||||
}
|
||||
|
||||
private static DummyEntityRepository createRepository(EmbeddedDatabase db) {
|
||||
return new JdbcRepositoryFactory(db).getRepository(DummyEntityRepository.class);
|
||||
|
||||
return new JdbcRepositoryFactory(mock(ApplicationEventPublisher.class), new NamedParameterJdbcTemplate(db))
|
||||
.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.util.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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.JdbcEvent;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
|
||||
private FakePublisher publisher = new FakePublisher();
|
||||
|
||||
private DummyEntityRepository repository;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
JdbcRepositoryFactory factory = new JdbcRepositoryFactory(publisher, mock(NamedParameterJdbcOperations.class));
|
||||
repository = factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnSave() {
|
||||
|
||||
DummyEntity entity = new DummyEntity(23L);
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
isInstanceOf(BeforeUpdateEvent.class, publisher.events.get(0));
|
||||
isInstanceOf(AfterUpdateEvent.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));
|
||||
}
|
||||
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnDelete() {
|
||||
|
||||
DummyEntity entity = new DummyEntity(23L);
|
||||
|
||||
repository.delete(entity);
|
||||
|
||||
isInstanceOf(BeforeDeleteEvent.class, publisher.events.get(0));
|
||||
isInstanceOf(AfterDeleteEvent.class, publisher.events.get(1));
|
||||
|
||||
assertEquals(entity, publisher.events.get(0).getInstance());
|
||||
assertEquals(entity, publisher.events.get(1).getInstance());
|
||||
|
||||
assertEquals(23L, publisher.events.get(0).getId());
|
||||
assertEquals(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));
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
private static class DummyEntity {
|
||||
|
||||
@Id private final Long id;
|
||||
}
|
||||
|
||||
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
|
||||
|
||||
}
|
||||
|
||||
static class FakePublisher implements ApplicationEventPublisher {
|
||||
|
||||
List<JdbcEvent> events = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void publishEvent(Object o) {
|
||||
events.add((JdbcEvent) o);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user