DATAJDBC-282 - Polishing.
Formatting. Removed AbstractRelationalEntityWriter. Fixed integration test for MS-SQL. Extracted some common test infrastructure. Original pull request: #107.
This commit is contained in:
@@ -21,106 +21,111 @@ import org.springframework.lang.Nullable;
|
||||
* Specifies a operations one can perform on a database, based on an <em>Domain Type</em>.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Thomas Lang
|
||||
*/
|
||||
public interface JdbcAggregateOperations {
|
||||
|
||||
/**
|
||||
* Saves an instance of an aggregate, including all the members of the aggregate.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be saved. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
<T> T save(T instance);
|
||||
/**
|
||||
* Saves an instance of an aggregate, including all the members of the aggregate.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be saved. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
<T> T save(T instance);
|
||||
|
||||
/**
|
||||
* Dedicated insert function to do just the insert of an instance of an aggregate, including all the members of the aggregate.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be inserted. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
<T> T insert(T instance);
|
||||
/**
|
||||
* Dedicated insert function. This skips the test if the aggregate root is new and makes an insert.
|
||||
* <p>
|
||||
* This is useful if the client provides an id for new aggregate roots.
|
||||
* </p>
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be inserted. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
<T> T insert(T instance);
|
||||
|
||||
/**
|
||||
* Dedicated update function to do just an update of an instance of an aggregate, including all the members of the aggregate.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be inserted. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
<T> T update(T instance);
|
||||
/**
|
||||
* Dedicated update function. This skips the test if the aggregate root is new or not and always performs an update
|
||||
* operation.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be inserted. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
<T> T update(T instance);
|
||||
|
||||
/**
|
||||
* Deletes a single Aggregate including all entities contained in that aggregate.
|
||||
*
|
||||
* @param id the id of the aggregate root of the aggregate to be deleted. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root.
|
||||
* @param <T> the type of the aggregate root.
|
||||
*/
|
||||
<T> void deleteById(Object id, Class<T> domainType);
|
||||
/**
|
||||
* Deletes a single Aggregate including all entities contained in that aggregate.
|
||||
*
|
||||
* @param id the id of the aggregate root of the aggregate to be deleted. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root.
|
||||
* @param <T> the type of the aggregate root.
|
||||
*/
|
||||
<T> void deleteById(Object id, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Delete an aggregate identified by it's aggregate root.
|
||||
*
|
||||
* @param aggregateRoot to delete. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
*/
|
||||
<T> void delete(T aggregateRoot, Class<T> domainType);
|
||||
/**
|
||||
* Delete an aggregate identified by it's aggregate root.
|
||||
*
|
||||
* @param aggregateRoot to delete. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
*/
|
||||
<T> void delete(T aggregateRoot, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Delete all aggregates of a given type.
|
||||
*
|
||||
* @param domainType type of the aggregate roots to be deleted. Must not be {@code null}.
|
||||
*/
|
||||
void deleteAll(Class<?> domainType);
|
||||
/**
|
||||
* Delete all aggregates of a given type.
|
||||
*
|
||||
* @param domainType type of the aggregate roots to be deleted. Must not be {@code null}.
|
||||
*/
|
||||
void deleteAll(Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Counts the number of aggregates of a given type.
|
||||
*
|
||||
* @param domainType the type of the aggregates to be counted.
|
||||
* @return the number of instances stored in the database. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
long count(Class<?> domainType);
|
||||
/**
|
||||
* Counts the number of aggregates of a given type.
|
||||
*
|
||||
* @param domainType the type of the aggregates to be counted.
|
||||
* @return the number of instances stored in the database. Guaranteed to be not {@code null}.
|
||||
*/
|
||||
long count(Class<?> domainType);
|
||||
|
||||
/**
|
||||
* Load an aggregate from the database.
|
||||
*
|
||||
* @param id the id of the aggregate to load. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the loaded aggregate. Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
<T> T findById(Object id, Class<T> domainType);
|
||||
/**
|
||||
* Load an aggregate from the database.
|
||||
*
|
||||
* @param id the id of the aggregate to load. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate root. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return the loaded aggregate. Might return {@code null}.
|
||||
*/
|
||||
@Nullable
|
||||
<T> T findById(Object id, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Load all aggregates of a given type that are identified by the given ids.
|
||||
*
|
||||
* @param ids of the aggregate roots identifying the aggregates to load. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate roots. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate roots. Must not be {@code null}.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType);
|
||||
/**
|
||||
* Load all aggregates of a given type that are identified by the given ids.
|
||||
*
|
||||
* @param ids of the aggregate roots identifying the aggregates to load. Must not be {@code null}.
|
||||
* @param domainType the type of the aggregate roots. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate roots. Must not be {@code null}.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Load all aggregates of a given type.
|
||||
*
|
||||
* @param domainType the type of the aggregate roots. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate roots. Must not be {@code null}.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAll(Class<T> domainType);
|
||||
/**
|
||||
* Load all aggregates of a given type.
|
||||
*
|
||||
* @param domainType the type of the aggregate roots. Must not be {@code null}.
|
||||
* @param <T> the type of the aggregate roots. Must not be {@code null}.
|
||||
* @return Guaranteed to be not {@code null}.
|
||||
*/
|
||||
<T> Iterable<T> findAll(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Checks if an aggregate identified by type and id exists in the database.
|
||||
*
|
||||
* @param id the id of the aggregate root.
|
||||
* @param domainType the type of the aggregate root.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return whether the aggregate exists.
|
||||
*/
|
||||
<T> boolean existsById(Object id, Class<T> domainType);
|
||||
/**
|
||||
* Checks if an aggregate identified by type and id exists in the database.
|
||||
*
|
||||
* @param id the id of the aggregate root.
|
||||
* @param domainType the type of the aggregate root.
|
||||
* @param <T> the type of the aggregate root.
|
||||
* @return whether the aggregate exists.
|
||||
*/
|
||||
<T> boolean existsById(Object id, Class<T> domainType);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
* @author Thomas Lang
|
||||
*/
|
||||
public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
@@ -59,36 +60,12 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
|
||||
private final DataAccessStrategy accessStrategy;
|
||||
|
||||
private <T> T store(T instance, IdentifierAccessor identifierAccessor, AggregateChange<T> change,
|
||||
RelationalPersistentEntity<?> persistentEntity) {
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
publisher.publishEvent(new BeforeSaveEvent( //
|
||||
Identifier.ofNullable(identifierAccessor.getIdentifier()), //
|
||||
instance, //
|
||||
change //
|
||||
));
|
||||
|
||||
change.executeWith(interpreter, context, converter);
|
||||
|
||||
Object identifier = persistentEntity.getIdentifierAccessor(change.getEntity()).getIdentifier();
|
||||
|
||||
Assert.notNull(identifier, "After saving the identifier must not be null");
|
||||
|
||||
publisher.publishEvent(new AfterSaveEvent( //
|
||||
Identifier.of(identifier), //
|
||||
change.getEntity(), //
|
||||
change //
|
||||
));
|
||||
|
||||
return (T) change.getEntity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationEventPublisher},
|
||||
* {@link RelationalMappingContext} and {@link DataAccessStrategy}.
|
||||
*
|
||||
* @param publisher must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
* @param publisher must not be {@literal null}.
|
||||
* @param context must not be {@literal null}.
|
||||
* @param dataAccessStrategy must not be {@literal null}.
|
||||
*/
|
||||
public JdbcAggregateTemplate(ApplicationEventPublisher publisher, RelationalMappingContext context,
|
||||
@@ -115,7 +92,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#save(java.lang.Object)
|
||||
*/
|
||||
@Override public <T> T save(T instance) {
|
||||
@Override
|
||||
public <T> T save(T instance) {
|
||||
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
@@ -128,12 +106,15 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dedicated insert function to do just the insert of an instance of an aggregate, including all the members of the aggregate.
|
||||
* Dedicated insert function to do just the insert of an instance of an aggregate, including all the members of the
|
||||
* aggregate.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be inserted. Must not be {@code null}.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
@Override public <T> T insert(T instance) {
|
||||
@Override
|
||||
public <T> T insert(T instance) {
|
||||
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
@@ -145,12 +126,15 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dedicated update function to do just an update of an instance of an aggregate, including all the members of the aggregate.
|
||||
* Dedicated update function to do just an update of an instance of an aggregate, including all the members of the
|
||||
* aggregate.
|
||||
*
|
||||
* @param instance the aggregate root of the aggregate to be inserted. Must not be {@code null}.
|
||||
* @return the saved instance.
|
||||
*/
|
||||
@Override public <T> T update(T instance) {
|
||||
@Override
|
||||
public <T> T update(T instance) {
|
||||
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
|
||||
@@ -165,7 +149,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#count(java.lang.Class)
|
||||
*/
|
||||
@Override public long count(Class<?> domainType) {
|
||||
@Override
|
||||
public long count(Class<?> domainType) {
|
||||
return accessStrategy.count(domainType);
|
||||
}
|
||||
|
||||
@@ -173,7 +158,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findById(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override public <T> T findById(Object id, Class<T> domainType) {
|
||||
@Override
|
||||
public <T> T findById(Object id, Class<T> domainType) {
|
||||
|
||||
T entity = accessStrategy.findById(id, domainType);
|
||||
if (entity != null) {
|
||||
@@ -186,7 +172,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#existsById(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override public <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
@Override
|
||||
public <T> boolean existsById(Object id, Class<T> domainType) {
|
||||
return accessStrategy.existsById(id, domainType);
|
||||
}
|
||||
|
||||
@@ -194,7 +181,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAll(java.lang.Class)
|
||||
*/
|
||||
@Override public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
@Override
|
||||
public <T> Iterable<T> findAll(Class<T> domainType) {
|
||||
|
||||
Iterable<T> all = accessStrategy.findAll(domainType);
|
||||
publishAfterLoad(all);
|
||||
@@ -205,7 +193,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#findAllById(java.lang.Iterable, java.lang.Class)
|
||||
*/
|
||||
@Override public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
@Override
|
||||
public <T> Iterable<T> findAllById(Iterable<?> ids, Class<T> domainType) {
|
||||
|
||||
Iterable<T> allById = accessStrategy.findAllById(ids, domainType);
|
||||
publishAfterLoad(allById);
|
||||
@@ -216,7 +205,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#delete(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override public <S> void delete(S aggregateRoot, Class<S> domainType) {
|
||||
@Override
|
||||
public <S> void delete(S aggregateRoot, Class<S> domainType) {
|
||||
|
||||
IdentifierAccessor identifierAccessor = context.getRequiredPersistentEntity(domainType)
|
||||
.getIdentifierAccessor(aggregateRoot);
|
||||
@@ -228,7 +218,8 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#deleteById(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
@Override public <S> void deleteById(Object id, Class<S> domainType) {
|
||||
@Override
|
||||
public <S> void deleteById(Object id, Class<S> domainType) {
|
||||
deleteTree(id, null, domainType);
|
||||
}
|
||||
|
||||
@@ -236,12 +227,39 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.core.JdbcAggregateOperations#deleteAll(java.lang.Class)
|
||||
*/
|
||||
@Override public void deleteAll(Class<?> domainType) {
|
||||
@Override
|
||||
public void deleteAll(Class<?> domainType) {
|
||||
|
||||
AggregateChange<?> change = createDeletingChange(domainType);
|
||||
change.executeWith(interpreter, context, converter);
|
||||
}
|
||||
|
||||
private <T> T store(T instance, IdentifierAccessor identifierAccessor, AggregateChange<T> change,
|
||||
RelationalPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Assert.notNull(instance, "Aggregate instance must not be null!");
|
||||
|
||||
publisher.publishEvent(new BeforeSaveEvent( //
|
||||
Identifier.ofNullable(identifierAccessor.getIdentifier()), //
|
||||
instance, //
|
||||
change //
|
||||
));
|
||||
|
||||
change.executeWith(interpreter, context, converter);
|
||||
|
||||
Object identifier = persistentEntity.getIdentifierAccessor(change.getEntity()).getIdentifier();
|
||||
|
||||
Assert.notNull(identifier, "After saving the identifier must not be null");
|
||||
|
||||
publisher.publishEvent(new AfterSaveEvent( //
|
||||
Identifier.of(identifier), //
|
||||
change.getEntity(), //
|
||||
change //
|
||||
));
|
||||
|
||||
return (T) change.getEntity();
|
||||
}
|
||||
|
||||
private void deleteTree(Object id, @Nullable Object entity, Class<?> domainType) {
|
||||
|
||||
AggregateChange<?> change = createDeletingChange(id, entity, domainType);
|
||||
@@ -255,21 +273,24 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
|
||||
publisher.publishEvent(new AfterDeleteEvent(specifiedId, optionalEntity, change));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) private <T> AggregateChange<T> createChange(T instance) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private <T> AggregateChange<T> createChange(T instance) {
|
||||
|
||||
AggregateChange<T> aggregateChange = new AggregateChange(Kind.SAVE, instance.getClass(), instance);
|
||||
jdbcEntityWriter.write(instance, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) private <T> AggregateChange<T> createInsertChange(T instance) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private <T> AggregateChange<T> createInsertChange(T instance) {
|
||||
|
||||
AggregateChange<T> aggregateChange = new AggregateChange(Kind.SAVE, instance.getClass(), instance);
|
||||
jdbcEntityInsertWriter.write(instance, aggregateChange);
|
||||
return aggregateChange;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" }) private <T> AggregateChange<T> createUpdateChange(T instance) {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private <T> AggregateChange<T> createUpdateChange(T instance) {
|
||||
|
||||
AggregateChange<T> aggregateChange = new AggregateChange(Kind.SAVE, instance.getClass(), instance);
|
||||
jdbcEntityUpdateWriter.write(instance, aggregateChange);
|
||||
|
||||
@@ -3,16 +3,36 @@ package org.springframework.data.jdbc.core;
|
||||
import org.springframework.data.repository.Repository;
|
||||
|
||||
/**
|
||||
* Jdbc repository for dedicated insert(), update() and upsert() sql functions.
|
||||
* Other than {@link org.springframework.data.jdbc.repository.support.SimpleJdbcRepository}
|
||||
* there should be bypassing of the isNew check.
|
||||
* Jdbc repository for dedicated insert(), update() and upsert() sql functions. Other than
|
||||
* {@link org.springframework.data.jdbc.repository.support.SimpleJdbcRepository} there should be bypassing of the isNew
|
||||
* check.
|
||||
*
|
||||
* @author Thomas Lang
|
||||
* @see <a href="https://jira.spring.io/browse/DATAJDBC-282">DATAJDBC-282</a>
|
||||
* @since 1.1
|
||||
*/
|
||||
public interface JdbcRepository<T, ID> extends Repository<T, ID> {
|
||||
|
||||
<S extends T> S insert(S var1);
|
||||
/**
|
||||
* Dedicated insert function. This skips the test if the aggregate root is new and makes an insert.
|
||||
* <p>
|
||||
* This is useful if the client provides an id for new aggregate roots.
|
||||
* </p>
|
||||
*
|
||||
* @param aggregateRoot the aggregate root to be saved in the database. Must not be {@code null}.
|
||||
* @param <S> Type of the aggregate root.
|
||||
* @return the saved aggregate root. If the provided aggregate root was immutable and a value needed changing, e.g.
|
||||
* the id this will be a new instance.
|
||||
*/
|
||||
<S extends T> S insert(S aggregateRoot);
|
||||
|
||||
<S extends T> S update(S var1);
|
||||
/**
|
||||
* Dedicated update function. This skips the test if the aggregate root is new or not and always performs an update
|
||||
* operation.
|
||||
*
|
||||
* @param aggregateRoot the aggregate root to be saved in the database. Must not be {@code null}.
|
||||
* @param <S> Type of the aggregate root.
|
||||
* @return the saved aggregate root. If the provided aggregate root was immutable and a value needed changing, e.g.
|
||||
* the id this will be a new instance.
|
||||
*/
|
||||
<S extends T> S update(S aggregateRoot);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public class SimpleJdbcRepository<T, ID> implements CrudRepository<T, ID>, JdbcR
|
||||
* @see org.springframework.data.repository.JdbcRepository#update(T t)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> S update(S var1) {
|
||||
return entityOperations.update(var1);
|
||||
public <S extends T> S update(S aggregateRoot) {
|
||||
return entityOperations.update(aggregateRoot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.core.JdbcRepository;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.jdbc.testing.TestConfiguration;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.rules.SpringClassRule;
|
||||
import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Very simple use cases for creation and usage of JdbcRepositories.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@Transactional
|
||||
public class JdbcRepositoryInsertExistingIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Autowired JdbcRepositoryFactory factory;
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryInsertExistingIntegrationTests.class;
|
||||
}
|
||||
|
||||
@Bean
|
||||
DummyEntityRepository dummyEntityRepository() {
|
||||
return factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
|
||||
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
|
||||
|
||||
@Autowired NamedParameterJdbcTemplate template;
|
||||
@Autowired DummyEntityRepository repository;
|
||||
|
||||
@Test // DATAJDBC-282
|
||||
public void insertAnExistingEntity() {
|
||||
|
||||
DummyEntity existingDummyEntity = createDummyEntity();
|
||||
existingDummyEntity.idProp = 123L;
|
||||
|
||||
DummyEntity entity = repository.insert(existingDummyEntity);
|
||||
|
||||
assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity",
|
||||
"id_Prop = " + existingDummyEntity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
|
||||
private static DummyEntity createDummyEntity() {
|
||||
|
||||
DummyEntity entity = new DummyEntity();
|
||||
entity.setName("Entity Name");
|
||||
return entity;
|
||||
}
|
||||
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long>, JdbcRepository<DummyEntity, Long> {}
|
||||
|
||||
@Data
|
||||
static class DummyEntity {
|
||||
|
||||
String name;
|
||||
@Id private Long idProp;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,8 @@ import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Very simple use cases for creation and usage of JdbcRepositories.
|
||||
*
|
||||
@@ -49,246 +51,222 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional
|
||||
public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
static class Config {
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Autowired
|
||||
JdbcRepositoryFactory factory;
|
||||
@Autowired JdbcRepositoryFactory factory;
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryIntegrationTests.class;
|
||||
}
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryIntegrationTests.class;
|
||||
}
|
||||
|
||||
@Bean
|
||||
DummyEntityRepository dummyEntityRepository() {
|
||||
return factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
@Bean
|
||||
DummyEntityRepository dummyEntityRepository() {
|
||||
return factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ClassRule
|
||||
public static final SpringClassRule classRule = new SpringClassRule();
|
||||
@Rule
|
||||
public SpringMethodRule methodRule = new SpringMethodRule();
|
||||
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
|
||||
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
|
||||
|
||||
@Autowired
|
||||
NamedParameterJdbcTemplate template;
|
||||
@Autowired
|
||||
DummyEntityRepository repository;
|
||||
@Autowired NamedParameterJdbcTemplate template;
|
||||
@Autowired DummyEntityRepository repository;
|
||||
|
||||
@Test // DATAJDBC-95
|
||||
public void savesAnEntity() {
|
||||
@Test // DATAJDBC-95
|
||||
public void savesAnEntity() {
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity",
|
||||
"id_Prop = " + entity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity",
|
||||
"id_Prop = " + entity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-282
|
||||
public void insertAnEntity() {
|
||||
@Test // DATAJDBC-282
|
||||
public void insertAnEntity() {
|
||||
|
||||
DummyEntity entity = repository.insert(createDummyEntity());
|
||||
DummyEntity entity = repository.insert(createDummyEntity());
|
||||
|
||||
assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity",
|
||||
"id_Prop = " + entity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity",
|
||||
"id_Prop = " + entity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-282
|
||||
public void insertAnExistingEntity() {
|
||||
@Test // DATAJDBC-95
|
||||
public void saveAndLoadAnEntity() {
|
||||
|
||||
DummyEntity existingDummyEntity = createExistingDummyEntity();
|
||||
DummyEntity entity = repository.insert(existingDummyEntity);
|
||||
assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummy_entity",
|
||||
"id_Prop = " + existingDummyEntity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-95
|
||||
public void saveAndLoadAnEntity() {
|
||||
assertThat(repository.findById(entity.getIdProp())).hasValueSatisfying(it -> {
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
assertThat(it.getIdProp()).isEqualTo(entity.getIdProp());
|
||||
assertThat(it.getName()).isEqualTo(entity.getName());
|
||||
});
|
||||
}
|
||||
|
||||
assertThat(repository.findById(entity.getIdProp())).hasValueSatisfying(it -> {
|
||||
@Test // DATAJDBC-97
|
||||
public void savesManyEntities() {
|
||||
|
||||
assertThat(it.getIdProp()).isEqualTo(entity.getIdProp());
|
||||
assertThat(it.getName()).isEqualTo(entity.getName());
|
||||
});
|
||||
}
|
||||
DummyEntity entity = createDummyEntity();
|
||||
DummyEntity other = createDummyEntity();
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void savesManyEntities() {
|
||||
repository.saveAll(asList(entity, other));
|
||||
|
||||
DummyEntity entity = createDummyEntity();
|
||||
DummyEntity other = createDummyEntity();
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
|
||||
repository.saveAll(asList(entity, other));
|
||||
@Test // DATAJDBC-97
|
||||
public void existsReturnsTrueIffEntityExists() {
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void existsReturnsTrueIffEntityExists() {
|
||||
assertThat(repository.existsById(entity.getIdProp())).isTrue();
|
||||
assertThat(repository.existsById(entity.getIdProp() + 1)).isFalse();
|
||||
}
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllEntities() {
|
||||
|
||||
assertThat(repository.existsById(entity.getIdProp())).isTrue();
|
||||
assertThat(repository.existsById(entity.getIdProp() + 1)).isFalse();
|
||||
}
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllEntities() {
|
||||
Iterable<DummyEntity> all = repository.findAll();
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
assertThat(all)//
|
||||
.extracting(DummyEntity::getIdProp)//
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
|
||||
Iterable<DummyEntity> all = repository.findAll();
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllSpecifiedEntities() {
|
||||
|
||||
assertThat(all)//
|
||||
.extracting(DummyEntity::getIdProp)//
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllSpecifiedEntities() {
|
||||
assertThat(repository.findAllById(asList(entity.getIdProp(), other.getIdProp())))//
|
||||
.extracting(DummyEntity::getIdProp)//
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
@Test // DATAJDBC-97
|
||||
public void countsEntities() {
|
||||
|
||||
assertThat(repository.findAllById(asList(entity.getIdProp(), other.getIdProp())))//
|
||||
.extracting(DummyEntity::getIdProp)//
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void countsEntities() {
|
||||
assertThat(repository.count()).isEqualTo(3L);
|
||||
}
|
||||
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteById() {
|
||||
|
||||
assertThat(repository.count()).isEqualTo(3L);
|
||||
}
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteById() {
|
||||
repository.deleteById(two.getIdProp());
|
||||
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(one.getIdProp(), three.getIdProp());
|
||||
}
|
||||
|
||||
repository.deleteById(two.getIdProp());
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByEntity() {
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(one.getIdProp(), three.getIdProp());
|
||||
}
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByEntity() {
|
||||
repository.delete(one);
|
||||
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(two.getIdProp(), three.getIdProp());
|
||||
}
|
||||
|
||||
repository.delete(one);
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByList() {
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(two.getIdProp(), three.getIdProp());
|
||||
}
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByList() {
|
||||
repository.deleteAll(asList(one, three));
|
||||
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(two.getIdProp());
|
||||
}
|
||||
|
||||
repository.deleteAll(asList(one, three));
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteAll() {
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(two.getIdProp());
|
||||
}
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteAll() {
|
||||
assertThat(repository.findAll()).isNotEmpty();
|
||||
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
repository.deleteAll();
|
||||
|
||||
assertThat(repository.findAll()).isNotEmpty();
|
||||
assertThat(repository.findAll()).isEmpty();
|
||||
}
|
||||
|
||||
repository.deleteAll();
|
||||
@Test // DATAJDBC-98
|
||||
public void update() {
|
||||
|
||||
assertThat(repository.findAll()).isEmpty();
|
||||
}
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-98
|
||||
public void update() {
|
||||
entity.setName("something else");
|
||||
DummyEntity saved = repository.save(entity);
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
assertThat(repository.findById(entity.getIdProp())).hasValueSatisfying(it -> {
|
||||
assertThat(it.getName()).isEqualTo(saved.getName());
|
||||
});
|
||||
}
|
||||
|
||||
entity.setName("something else");
|
||||
DummyEntity saved = repository.save(entity);
|
||||
@Test // DATAJDBC-98
|
||||
public void updateMany() {
|
||||
|
||||
assertThat(repository.findById(entity.getIdProp())).hasValueSatisfying(it -> {
|
||||
assertThat(it.getName()).isEqualTo(saved.getName());
|
||||
});
|
||||
}
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
|
||||
@Test // DATAJDBC-98
|
||||
public void updateMany() {
|
||||
entity.setName("something else");
|
||||
other.setName("others Name");
|
||||
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
repository.saveAll(asList(entity, other));
|
||||
|
||||
entity.setName("something else");
|
||||
other.setName("others Name");
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getName) //
|
||||
.containsExactlyInAnyOrder(entity.getName(), other.getName());
|
||||
}
|
||||
|
||||
repository.saveAll(asList(entity, other));
|
||||
@Test // DATAJDBC-112
|
||||
public void findByIdReturnsEmptyWhenNoneFound() {
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getName) //
|
||||
.containsExactlyInAnyOrder(entity.getName(), other.getName());
|
||||
}
|
||||
// NOT saving anything, so DB is empty
|
||||
|
||||
@Test // DATAJDBC-112
|
||||
public void findByIdReturnsEmptyWhenNoneFound() {
|
||||
assertThat(repository.findById(-1L)).isEmpty();
|
||||
}
|
||||
|
||||
// NOT saving anything, so DB is empty
|
||||
private static DummyEntity createDummyEntity() {
|
||||
|
||||
assertThat(repository.findById(-1L)).isEmpty();
|
||||
}
|
||||
DummyEntity entity = new DummyEntity();
|
||||
entity.setName("Entity Name");
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static DummyEntity createDummyEntity() {
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long>, JdbcRepository<DummyEntity, Long> {}
|
||||
|
||||
DummyEntity entity = new DummyEntity();
|
||||
entity.setName("Entity Name");
|
||||
return entity;
|
||||
}
|
||||
@Data
|
||||
static class DummyEntity {
|
||||
|
||||
private static DummyEntity createExistingDummyEntity() {
|
||||
|
||||
DummyEntity entity = new DummyEntity();
|
||||
entity.setIdProp(Long.parseLong("123"));
|
||||
entity.setName("Entity Name");
|
||||
return entity;
|
||||
}
|
||||
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long>, JdbcRepository<DummyEntity, Long> {
|
||||
}
|
||||
|
||||
@Data
|
||||
static class DummyEntity {
|
||||
|
||||
String name;
|
||||
@Id
|
||||
private Long idProp;
|
||||
}
|
||||
String name;
|
||||
@Id private Long idProp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
CREATE TABLE dummy_entity ( id_Prop BIGINT PRIMARY KEY, NAME VARCHAR(100))
|
||||
@@ -0,0 +1 @@
|
||||
CREATE TABLE dummy_entity (id_Prop BIGINT PRIMARY KEY, NAME VARCHAR(100));
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS dummy_entity;
|
||||
CREATE TABLE dummy_entity (id_Prop BIGINT PRIMARY KEY, NAME VARCHAR(100));
|
||||
@@ -0,0 +1 @@
|
||||
CREATE TABLE dummy_entity (id_Prop BIGINT PRIMARY KEY, NAME VARCHAR(100));
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE dummy_entity;
|
||||
CREATE TABLE dummy_entity (id_Prop INTEGER PRIMARY KEY, NAME VARCHAR(100));
|
||||
@@ -1,274 +0,0 @@
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import lombok.Value;
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPaths;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used as an abstract class to derive relational writer actions (save, insert, update).
|
||||
* Implementations see {@link RelationalEntityWriter} and {@link RelationalEntityInsertWriter}
|
||||
*
|
||||
* @author Thomas Lang
|
||||
*/
|
||||
abstract class AbstractRelationalEntityWriter implements EntityWriter<Object, AggregateChange<?>> {
|
||||
|
||||
protected final RelationalMappingContext context;
|
||||
|
||||
AbstractRelationalEntityWriter(RelationalMappingContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds context information for the current save operation.
|
||||
*/
|
||||
class WritingContext {
|
||||
|
||||
private final Object root;
|
||||
private final Object entity;
|
||||
private final Class<?> entityType;
|
||||
private final PersistentPropertyPaths<?, RelationalPersistentProperty> paths;
|
||||
private final Map<PathNode, DbAction> previousActions = new HashMap<>();
|
||||
private Map<PersistentPropertyPath<RelationalPersistentProperty>, List<PathNode>> nodesCache = new HashMap<>();
|
||||
|
||||
WritingContext(Object root, AggregateChange<?> aggregateChange) {
|
||||
|
||||
this.root = root;
|
||||
this.entity = aggregateChange.getEntity();
|
||||
this.entityType = aggregateChange.getEntityType();
|
||||
this.paths = context.findPersistentPropertyPaths(entityType, PersistentProperty::isEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves out the isNew check as defined in #DATAJDBC-282
|
||||
*
|
||||
* @return List of {@link DbAction}s
|
||||
* @see <a href="https://jira.spring.io/browse/DATAJDBC-282">DAJDBC-282</a>
|
||||
*/
|
||||
List<DbAction<?>> insert() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
actions.add(setRootAction(new DbAction.InsertRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves out the isNew check as defined in #DATAJDBC-282
|
||||
*
|
||||
* @return List of {@link DbAction}s
|
||||
* @see <a href="https://jira.spring.io/browse/DATAJDBC-282">DAJDBC-282</a>
|
||||
*/
|
||||
List<DbAction<?>> update() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>(deleteReferenced());
|
||||
actions.add(setRootAction(new DbAction.UpdateRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
return actions;
|
||||
}
|
||||
|
||||
List<DbAction<?>> save() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
if (isNew(root)) {
|
||||
|
||||
actions.add(setRootAction(new DbAction.InsertRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
} else {
|
||||
|
||||
actions.addAll(deleteReferenced());
|
||||
actions.add(setRootAction(new DbAction.UpdateRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private boolean isNew(Object o) {
|
||||
return context.getRequiredPersistentEntity(o.getClass()).isNew(o);
|
||||
}
|
||||
|
||||
//// Operations on all paths
|
||||
|
||||
private List<DbAction<?>> insertReferenced() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
paths.forEach(path -> actions.addAll(insertAll(path)));
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private List<DbAction<?>> insertAll(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
from(path).forEach(node -> {
|
||||
|
||||
DbAction.Insert<Object> insert;
|
||||
if (node.path.getRequiredLeafProperty().isQualified()) {
|
||||
|
||||
Pair<Object, Object> value = (Pair) node.getValue();
|
||||
insert = new DbAction.Insert<>(value.getSecond(), path, getAction(node.parent));
|
||||
insert.getAdditionalValues().put(node.path.getRequiredLeafProperty().getKeyColumn(), value.getFirst());
|
||||
|
||||
} else {
|
||||
insert = new DbAction.Insert<>(node.getValue(), path, getAction(node.parent));
|
||||
}
|
||||
|
||||
previousActions.put(node, insert);
|
||||
actions.add(insert);
|
||||
});
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private List<DbAction<?>> deleteReferenced() {
|
||||
|
||||
List<DbAction<?>> deletes = new ArrayList<>();
|
||||
paths.forEach(path -> deletes.add(0, deleteReferenced(path)));
|
||||
|
||||
return deletes;
|
||||
}
|
||||
|
||||
/// Operations on a single path
|
||||
|
||||
private DbAction.Delete<?> deleteReferenced(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
Object id = context.getRequiredPersistentEntity(entityType).getIdentifierAccessor(entity).getIdentifier();
|
||||
|
||||
return new DbAction.Delete<>(id, path);
|
||||
}
|
||||
|
||||
//// methods not directly related to the creation of DbActions
|
||||
|
||||
private DbAction<?> setRootAction(DbAction<?> dbAction) {
|
||||
|
||||
previousActions.put(null, dbAction);
|
||||
return dbAction;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DbAction.WithEntity<?> getAction(@Nullable RelationalEntityInsertWriter.PathNode parent) {
|
||||
|
||||
DbAction action = previousActions.get(parent);
|
||||
|
||||
if (action != null) {
|
||||
|
||||
Assert.isInstanceOf( //
|
||||
DbAction.WithEntity.class, //
|
||||
action, //
|
||||
"dependsOn action is not a WithEntity, but " + action.getClass().getSimpleName() //
|
||||
);
|
||||
|
||||
return (DbAction.WithEntity<?>) action;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// commented as of #DATAJDBC-282
|
||||
// private boolean isNew(Object o) {
|
||||
// return context.getRequiredPersistentEntity(o.getClass()).isNew(o);
|
||||
// }
|
||||
|
||||
private List<RelationalEntityInsertWriter.PathNode> from(
|
||||
PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
List<RelationalEntityInsertWriter.PathNode> nodes = new ArrayList<>();
|
||||
|
||||
if (path.getLength() == 1) {
|
||||
|
||||
Object value = context //
|
||||
.getRequiredPersistentEntity(entityType) //
|
||||
.getPropertyAccessor(entity) //
|
||||
.getProperty(path.getRequiredLeafProperty());
|
||||
|
||||
nodes.addAll(createNodes(path, null, value));
|
||||
|
||||
} else {
|
||||
|
||||
List<RelationalEntityInsertWriter.PathNode> pathNodes = nodesCache.get(path.getParentPath());
|
||||
pathNodes.forEach(parentNode -> {
|
||||
|
||||
Object value = path.getRequiredLeafProperty().getOwner().getPropertyAccessor(parentNode.getValue())
|
||||
.getProperty(path.getRequiredLeafProperty());
|
||||
|
||||
nodes.addAll(createNodes(path, parentNode, value));
|
||||
});
|
||||
}
|
||||
|
||||
nodesCache.put(path, nodes);
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private List<RelationalEntityInsertWriter.PathNode> createNodes(
|
||||
PersistentPropertyPath<RelationalPersistentProperty> path,
|
||||
@Nullable RelationalEntityInsertWriter.PathNode parentNode, @Nullable Object value) {
|
||||
|
||||
if (value == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<RelationalEntityInsertWriter.PathNode> nodes = new ArrayList<>();
|
||||
|
||||
if (path.getRequiredLeafProperty().isQualified()) {
|
||||
|
||||
if (path.getRequiredLeafProperty().isMap()) {
|
||||
((Map<?, ?>) value)
|
||||
.forEach((k, v) -> nodes.add(new RelationalEntityInsertWriter.PathNode(path, parentNode, Pair.of(k, v))));
|
||||
} else {
|
||||
|
||||
List listValue = (List) value;
|
||||
for (int k = 0; k < listValue.size(); k++) {
|
||||
nodes.add(new RelationalEntityInsertWriter.PathNode(path, parentNode, Pair.of(k, listValue.get(k))));
|
||||
}
|
||||
}
|
||||
} else if (path.getRequiredLeafProperty().isCollectionLike()) { // collection value
|
||||
((Collection<?>) value).forEach(v -> nodes.add(new RelationalEntityInsertWriter.PathNode(path, parentNode, v)));
|
||||
} else { // single entity value
|
||||
nodes.add(new RelationalEntityInsertWriter.PathNode(path, parentNode, value));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single entity in an aggregate along with its property path from the root entity and the chain of
|
||||
* objects to traverse a long this path.
|
||||
*/
|
||||
@Value
|
||||
static class PathNode {
|
||||
|
||||
/**
|
||||
* The path to this entity
|
||||
*/
|
||||
PersistentPropertyPath<RelationalPersistentProperty> path;
|
||||
|
||||
/**
|
||||
* The parent {@link PathNode}. This is {@code null} if this is the root entity.
|
||||
*/
|
||||
@Nullable
|
||||
PathNode parent;
|
||||
|
||||
/**
|
||||
* The value of the entity.
|
||||
*/
|
||||
Object value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2019 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.relational.core.conversion;
|
||||
|
||||
import lombok.Value;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a single entity in an aggregate along with its property path from the root entity and the chain of
|
||||
* objects to traverse a long this path.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@Value
|
||||
class PathNode {
|
||||
|
||||
/**
|
||||
* The path to this entity
|
||||
*/
|
||||
PersistentPropertyPath<RelationalPersistentProperty> path;
|
||||
|
||||
/**
|
||||
* The parent {@link PathNode}. This is {@code null} if this is the root entity.
|
||||
*/
|
||||
@Nullable
|
||||
PathNode parent;
|
||||
|
||||
/**
|
||||
* The value of the entity.
|
||||
*/
|
||||
Object value;
|
||||
}
|
||||
@@ -15,30 +15,34 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
/**
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChange}.
|
||||
* Does not perform any isNew check.
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChange}. Does not perform any isNew check.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
* @author Thomas Lang
|
||||
* @author Jens Schauder
|
||||
* @since 1.1
|
||||
*/
|
||||
public class RelationalEntityInsertWriter extends AbstractRelationalEntityWriter {
|
||||
public class RelationalEntityInsertWriter implements EntityWriter<Object, AggregateChange<?>> {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
|
||||
public RelationalEntityInsertWriter(RelationalMappingContext context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.EntityWriter#save(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override public void write(Object root, AggregateChange<?> aggregateChange) {
|
||||
List<DbAction<?>> actions = new WritingContext(root, aggregateChange).insert();
|
||||
@Override
|
||||
public void write(Object root, AggregateChange<?> aggregateChange) {
|
||||
|
||||
List<DbAction<?>> actions = new WritingContext(context, root, aggregateChange).insert();
|
||||
actions.forEach(aggregateChange::addAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,30 +15,34 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
/**
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChange}.
|
||||
* Does not perform any isNew check.
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChange}. Does not perform any isNew check.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
* @author Thomas Lang
|
||||
* @author Jens Schauder
|
||||
* @since 1.1
|
||||
*/
|
||||
public class RelationalEntityUpdateWriter extends AbstractRelationalEntityWriter {
|
||||
public class RelationalEntityUpdateWriter implements EntityWriter<Object, AggregateChange<?>> {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
|
||||
public RelationalEntityUpdateWriter(RelationalMappingContext context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.EntityWriter#save(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override public void write(Object root, AggregateChange<?> aggregateChange) {
|
||||
List<DbAction<?>> actions = new WritingContext(root, aggregateChange).update();
|
||||
@Override
|
||||
public void write(Object root, AggregateChange<?> aggregateChange) {
|
||||
|
||||
List<DbAction<?>> actions = new WritingContext(context, root, aggregateChange).update();
|
||||
actions.forEach(aggregateChange::addAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,28 +15,33 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
/**
|
||||
* Converts an aggregate represented by its root into an {@link AggregateChange}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class RelationalEntityWriter extends AbstractRelationalEntityWriter {
|
||||
public class RelationalEntityWriter implements EntityWriter<Object, AggregateChange<?>> {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
|
||||
public RelationalEntityWriter(RelationalMappingContext context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.convert.EntityWriter#save(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override public void write(Object root, AggregateChange<?> aggregateChange) {
|
||||
List<DbAction<?>> actions = new WritingContext(root, aggregateChange).save();
|
||||
@Override
|
||||
public void write(Object root, AggregateChange<?> aggregateChange) {
|
||||
|
||||
List<DbAction<?>> actions = new WritingContext(context, root, aggregateChange).save();
|
||||
actions.forEach(aggregateChange::addAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2019 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.relational.core.conversion;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PersistentPropertyPaths;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds context information for the current save operation.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class WritingContext {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
private final Object root;
|
||||
private final Object entity;
|
||||
private final Class<?> entityType;
|
||||
private final PersistentPropertyPaths<?, RelationalPersistentProperty> paths;
|
||||
private final Map<PathNode, DbAction> previousActions = new HashMap<>();
|
||||
private Map<PersistentPropertyPath<RelationalPersistentProperty>, List<PathNode>> nodesCache = new HashMap<>();
|
||||
|
||||
WritingContext(RelationalMappingContext context, Object root, AggregateChange<?> aggregateChange) {
|
||||
|
||||
this.context = context;
|
||||
this.root = root;
|
||||
this.entity = aggregateChange.getEntity();
|
||||
this.entityType = aggregateChange.getEntityType();
|
||||
this.paths = context.findPersistentPropertyPaths(entityType, PersistentProperty::isEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves out the isNew check as defined in #DATAJDBC-282
|
||||
*
|
||||
* @return List of {@link DbAction}s
|
||||
* @see <a href="https://jira.spring.io/browse/DATAJDBC-282">DAJDBC-282</a>
|
||||
*/
|
||||
List<DbAction<?>> insert() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
actions.add(setRootAction(new DbAction.InsertRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves out the isNew check as defined in #DATAJDBC-282
|
||||
*
|
||||
* @return List of {@link DbAction}s
|
||||
* @see <a href="https://jira.spring.io/browse/DATAJDBC-282">DAJDBC-282</a>
|
||||
*/
|
||||
List<DbAction<?>> update() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>(deleteReferenced());
|
||||
actions.add(setRootAction(new DbAction.UpdateRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
return actions;
|
||||
}
|
||||
|
||||
List<DbAction<?>> save() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
if (isNew(root)) {
|
||||
|
||||
actions.add(setRootAction(new DbAction.InsertRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
} else {
|
||||
|
||||
actions.addAll(deleteReferenced());
|
||||
actions.add(setRootAction(new DbAction.UpdateRoot<>(entity)));
|
||||
actions.addAll(insertReferenced());
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private boolean isNew(Object o) {
|
||||
return context.getRequiredPersistentEntity(o.getClass()).isNew(o);
|
||||
}
|
||||
|
||||
//// Operations on all paths
|
||||
|
||||
private List<DbAction<?>> insertReferenced() {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
paths.forEach(path -> actions.addAll(insertAll(path)));
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private List<DbAction<?>> insertAll(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
List<DbAction<?>> actions = new ArrayList<>();
|
||||
|
||||
from(path).forEach(node -> {
|
||||
|
||||
DbAction.Insert<Object> insert;
|
||||
if (node.getPath().getRequiredLeafProperty().isQualified()) {
|
||||
|
||||
Pair<Object, Object> value = (Pair) node.getValue();
|
||||
insert = new DbAction.Insert<>(value.getSecond(), path, getAction(node.getParent()));
|
||||
insert.getAdditionalValues().put(node.getPath().getRequiredLeafProperty().getKeyColumn(), value.getFirst());
|
||||
|
||||
} else {
|
||||
insert = new DbAction.Insert<>(node.getValue(), path, getAction(node.getParent()));
|
||||
}
|
||||
|
||||
previousActions.put(node, insert);
|
||||
actions.add(insert);
|
||||
});
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
private List<DbAction<?>> deleteReferenced() {
|
||||
|
||||
List<DbAction<?>> deletes = new ArrayList<>();
|
||||
paths.forEach(path -> deletes.add(0, deleteReferenced(path)));
|
||||
|
||||
return deletes;
|
||||
}
|
||||
|
||||
/// Operations on a single path
|
||||
|
||||
private DbAction.Delete<?> deleteReferenced(PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
Object id = context.getRequiredPersistentEntity(entityType).getIdentifierAccessor(entity).getIdentifier();
|
||||
|
||||
return new DbAction.Delete<>(id, path);
|
||||
}
|
||||
|
||||
//// methods not directly related to the creation of DbActions
|
||||
|
||||
private DbAction<?> setRootAction(DbAction<?> dbAction) {
|
||||
|
||||
previousActions.put(null, dbAction);
|
||||
return dbAction;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DbAction.WithEntity<?> getAction(@Nullable PathNode parent) {
|
||||
|
||||
DbAction action = previousActions.get(parent);
|
||||
|
||||
if (action != null) {
|
||||
|
||||
Assert.isInstanceOf( //
|
||||
DbAction.WithEntity.class, //
|
||||
action, //
|
||||
"dependsOn action is not a WithEntity, but " + action.getClass().getSimpleName() //
|
||||
);
|
||||
|
||||
return (DbAction.WithEntity<?>) action;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// commented as of #DATAJDBC-282
|
||||
// private boolean isNew(Object o) {
|
||||
// return context.getRequiredPersistentEntity(o.getClass()).isNew(o);
|
||||
// }
|
||||
|
||||
private List<PathNode> from(
|
||||
PersistentPropertyPath<RelationalPersistentProperty> path) {
|
||||
|
||||
List<PathNode> nodes = new ArrayList<>();
|
||||
|
||||
if (path.getLength() == 1) {
|
||||
|
||||
Object value = context //
|
||||
.getRequiredPersistentEntity(entityType) //
|
||||
.getPropertyAccessor(entity) //
|
||||
.getProperty(path.getRequiredLeafProperty());
|
||||
|
||||
nodes.addAll(createNodes(path, null, value));
|
||||
|
||||
} else {
|
||||
|
||||
List<PathNode> pathNodes = nodesCache.get(path.getParentPath());
|
||||
pathNodes.forEach(parentNode -> {
|
||||
|
||||
Object value = path.getRequiredLeafProperty().getOwner().getPropertyAccessor(parentNode.getValue())
|
||||
.getProperty(path.getRequiredLeafProperty());
|
||||
|
||||
nodes.addAll(createNodes(path, parentNode, value));
|
||||
});
|
||||
}
|
||||
|
||||
nodesCache.put(path, nodes);
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private List<PathNode> createNodes(
|
||||
PersistentPropertyPath<RelationalPersistentProperty> path,
|
||||
@Nullable PathNode parentNode, @Nullable Object value) {
|
||||
|
||||
if (value == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<PathNode> nodes = new ArrayList<>();
|
||||
|
||||
if (path.getRequiredLeafProperty().isQualified()) {
|
||||
|
||||
if (path.getRequiredLeafProperty().isMap()) {
|
||||
((Map<?, ?>) value)
|
||||
.forEach((k, v) -> nodes.add(new PathNode(path, parentNode, Pair.of(k, v))));
|
||||
} else {
|
||||
|
||||
List listValue = (List) value;
|
||||
for (int k = 0; k < listValue.size(); k++) {
|
||||
nodes.add(new PathNode(path, parentNode, Pair.of(k, listValue.get(k))));
|
||||
}
|
||||
}
|
||||
} else if (path.getRequiredLeafProperty().isCollectionLike()) { // collection value
|
||||
((Collection<?>) value).forEach(v -> nodes.add(new PathNode(path, parentNode, v)));
|
||||
} else { // single entity value
|
||||
nodes.add(new PathNode(path, parentNode, value));
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2018 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.relational.core.conversion;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
/**
|
||||
* Utility class for analyzing DbActions in tests.
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@UtilityClass
|
||||
class DbActionTestSupport {
|
||||
|
||||
static String extractPath(DbAction action) {
|
||||
|
||||
if (action instanceof DbAction.WithPropertyPath) {
|
||||
return ((DbAction.WithPropertyPath<?>) action).getPropertyPath().toDotPath();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static boolean isWithDependsOn(DbAction dbAction) {
|
||||
return dbAction instanceof DbAction.WithDependingOn;
|
||||
}
|
||||
|
||||
static Class<?> actualEntityType(DbAction a) {
|
||||
|
||||
if (a instanceof DbAction.WithEntity) {
|
||||
return ((DbAction.WithEntity) a).getEntity().getClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChange.Kind;
|
||||
import org.springframework.data.relational.core.conversion.DbAction.Delete;
|
||||
import org.springframework.data.relational.core.conversion.DbAction.DeleteAll;
|
||||
@@ -41,15 +40,6 @@ public class RelationalEntityDeleteWriterUnitTests {
|
||||
|
||||
RelationalEntityDeleteWriter converter = new RelationalEntityDeleteWriter(new RelationalMappingContext());
|
||||
|
||||
private static Object dotPath(DbAction dba) {
|
||||
if (dba instanceof DbAction.WithPropertyPath) {
|
||||
PersistentPropertyPath propertyPath = ((DbAction.WithPropertyPath<?>) dba).getPropertyPath();
|
||||
return propertyPath == null ? null : propertyPath.toDotPath();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-112
|
||||
public void deleteDeletesTheEntityAndReferencedEntities() {
|
||||
|
||||
@@ -60,11 +50,11 @@ public class RelationalEntityDeleteWriterUnitTests {
|
||||
converter.write(entity.id, aggregateChange);
|
||||
|
||||
Assertions.assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, RelationalEntityDeleteWriterUnitTests::dotPath) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
|
||||
.containsExactly( //
|
||||
Tuple.tuple(Delete.class, YetAnother.class, "other.yetAnother"), //
|
||||
Tuple.tuple(Delete.class, OtherEntity.class, "other"), //
|
||||
Tuple.tuple(DeleteRoot.class, SomeEntity.class, null) //
|
||||
Tuple.tuple(DeleteRoot.class, SomeEntity.class, "") //
|
||||
);
|
||||
}
|
||||
|
||||
@@ -76,11 +66,11 @@ public class RelationalEntityDeleteWriterUnitTests {
|
||||
converter.write(null, aggregateChange);
|
||||
|
||||
Assertions.assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, RelationalEntityDeleteWriterUnitTests::dotPath) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
|
||||
.containsExactly( //
|
||||
Tuple.tuple(DeleteAll.class, YetAnother.class, "other.yetAnother"), //
|
||||
Tuple.tuple(DeleteAll.class, OtherEntity.class, "other"), //
|
||||
Tuple.tuple(DeleteAllRoot.class, SomeEntity.class, null) //
|
||||
Tuple.tuple(DeleteAllRoot.class, SomeEntity.class, "") //
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
@@ -24,15 +27,13 @@ import org.springframework.data.relational.core.conversion.AggregateChange.Kind;
|
||||
import org.springframework.data.relational.core.conversion.DbAction.InsertRoot;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link RelationalEntityInsertWriter}
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Thomas Lang
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class) public class RelationalEntityInsertWriterUnitTests {
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RelationalEntityInsertWriterUnitTests {
|
||||
|
||||
public static final long SOME_ENTITY_ID = 23L;
|
||||
RelationalEntityInsertWriter converter = new RelationalEntityInsertWriter(new RelationalMappingContext());
|
||||
@@ -47,8 +48,8 @@ import static org.assertj.core.api.Assertions.*;
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
|
||||
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
|
||||
);
|
||||
@@ -65,39 +66,16 @@ import static org.assertj.core.api.Assertions.*;
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath,
|
||||
DbActionTestSupport::actualEntityType, DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
|
||||
);
|
||||
|
||||
assertThat(aggregateChange.getEntity()).isNotNull();
|
||||
// the new id should not be the same as the origin one - should do insert, not update
|
||||
// assertThat(aggregateChange.getEntity().id).isNotEqualTo(SOME_ENTITY_ID);
|
||||
}
|
||||
|
||||
private String extractPath(DbAction action) {
|
||||
|
||||
if (action instanceof DbAction.WithPropertyPath) {
|
||||
return ((DbAction.WithPropertyPath<?>) action).getPropertyPath().toDotPath();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean isWithDependsOn(DbAction dbAction) {
|
||||
return dbAction instanceof DbAction.WithDependingOn;
|
||||
}
|
||||
|
||||
private Class<?> actualEntityType(DbAction a) {
|
||||
|
||||
if (a instanceof DbAction.WithEntity) {
|
||||
return ((DbAction.WithEntity) a).getEntity().getClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor static class SingleReferenceEntity {
|
||||
@RequiredArgsConstructor
|
||||
static class SingleReferenceEntity {
|
||||
|
||||
@Id final Long id;
|
||||
Element other;
|
||||
@@ -105,7 +83,8 @@ import static org.assertj.core.api.Assertions.*;
|
||||
String name;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor private static class Element {
|
||||
@RequiredArgsConstructor
|
||||
private static class Element {
|
||||
@Id final Long id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.conversion;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
@@ -23,74 +26,48 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.relational.core.conversion.AggregateChange.Kind;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link RelationalEntityUpdateWriter}
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Thomas Lang
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RelationalEntityUpdateWriterUnitTests {
|
||||
|
||||
public static final long SOME_ENTITY_ID = 23L;
|
||||
RelationalEntityUpdateWriter converter = new RelationalEntityUpdateWriter(new RelationalMappingContext());
|
||||
public static final long SOME_ENTITY_ID = 23L;
|
||||
RelationalEntityUpdateWriter converter = new RelationalEntityUpdateWriter(new RelationalMappingContext());
|
||||
|
||||
@Test // DATAJDBC-112
|
||||
public void existingEntityGetsConvertedToDeletePlusUpdate() {
|
||||
@Test // DATAJDBC-112
|
||||
public void existingEntityGetsConvertedToDeletePlusUpdate() {
|
||||
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
|
||||
AggregateChange<RelationalEntityWriterUnitTests.SingleReferenceEntity> aggregateChange = //
|
||||
new AggregateChange(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
AggregateChange<RelationalEntityWriterUnitTests.SingleReferenceEntity> aggregateChange = //
|
||||
new AggregateChange(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(DbAction.Delete.class, Element.class, "other", null, false), //
|
||||
tuple(DbAction.UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
|
||||
);
|
||||
}
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(DbAction.Delete.class, Element.class, "other", null, false), //
|
||||
tuple(DbAction.UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
|
||||
);
|
||||
}
|
||||
|
||||
private String extractPath(DbAction action) {
|
||||
@RequiredArgsConstructor
|
||||
static class SingleReferenceEntity {
|
||||
|
||||
if (action instanceof DbAction.WithPropertyPath) {
|
||||
return ((DbAction.WithPropertyPath<?>) action).getPropertyPath().toDotPath();
|
||||
}
|
||||
@Id final Long id;
|
||||
Element other;
|
||||
// should not trigger own Dbaction
|
||||
String name;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean isWithDependsOn(DbAction dbAction) {
|
||||
return dbAction instanceof DbAction.WithDependingOn;
|
||||
}
|
||||
|
||||
private Class<?> actualEntityType(DbAction a) {
|
||||
|
||||
if (a instanceof DbAction.WithEntity) {
|
||||
return ((DbAction.WithEntity) a).getEntity().getClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class SingleReferenceEntity {
|
||||
|
||||
@Id
|
||||
final Long id;
|
||||
Element other;
|
||||
// should not trigger own Dbaction
|
||||
String name;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class Element {
|
||||
@Id
|
||||
final Long id;
|
||||
}
|
||||
@RequiredArgsConstructor
|
||||
private static class Element {
|
||||
@Id final Long id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,13 +53,13 @@ public class RelationalEntityWriterUnitTests {
|
||||
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(null);
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
new AggregateChange(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
new AggregateChange<>(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
|
||||
);
|
||||
@@ -72,13 +72,13 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.other = new Element(null);
|
||||
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
new AggregateChange(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
new AggregateChange<>(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false), //
|
||||
tuple(Insert.class, Element.class, "other", Element.class, true) //
|
||||
@@ -91,13 +91,13 @@ public class RelationalEntityWriterUnitTests {
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = //
|
||||
new AggregateChange(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
new AggregateChange<>(Kind.SAVE, SingleReferenceEntity.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(Delete.class, Element.class, "other", null, false), //
|
||||
tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false) //
|
||||
@@ -110,14 +110,14 @@ public class RelationalEntityWriterUnitTests {
|
||||
SingleReferenceEntity entity = new SingleReferenceEntity(SOME_ENTITY_ID);
|
||||
entity.other = new Element(null);
|
||||
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange(Kind.SAVE, SingleReferenceEntity.class,
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange<>(Kind.SAVE, SingleReferenceEntity.class,
|
||||
entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(Delete.class, Element.class, "other", null, false), //
|
||||
tuple(UpdateRoot.class, SingleReferenceEntity.class, "", SingleReferenceEntity.class, false), //
|
||||
@@ -129,14 +129,14 @@ public class RelationalEntityWriterUnitTests {
|
||||
public void newEntityWithEmptySetResultsInSingleInsert() {
|
||||
|
||||
SetContainer entity = new SetContainer(null);
|
||||
AggregateChange<RelationalEntityWriterUnitTests.SingleReferenceEntity> aggregateChange = new AggregateChange(
|
||||
AggregateChange<RelationalEntityWriterUnitTests.SetContainer> aggregateChange = new AggregateChange<>(
|
||||
Kind.SAVE, SetContainer.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, SetContainer.class, "", SetContainer.class, false));
|
||||
}
|
||||
@@ -148,12 +148,12 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.add(new Element(null));
|
||||
entity.elements.add(new Element(null));
|
||||
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange(Kind.SAVE, SetContainer.class, entity);
|
||||
AggregateChange<SetContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, SetContainer.class, entity);
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, SetContainer.class, "", SetContainer.class, false), //
|
||||
tuple(Insert.class, Element.class, "elements", Element.class, true), //
|
||||
@@ -176,14 +176,14 @@ public class RelationalEntityWriterUnitTests {
|
||||
new Element(null)) //
|
||||
);
|
||||
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange(Kind.SAVE,
|
||||
AggregateChange<CascadingReferenceEntity> aggregateChange = new AggregateChange<>(Kind.SAVE,
|
||||
CascadingReferenceEntity.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, CascadingReferenceEntity.class, "", CascadingReferenceEntity.class, false), //
|
||||
tuple(Insert.class, CascadingReferenceMiddleElement.class, "other", CascadingReferenceMiddleElement.class,
|
||||
@@ -212,14 +212,14 @@ public class RelationalEntityWriterUnitTests {
|
||||
new Element(null)) //
|
||||
);
|
||||
|
||||
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange(Kind.SAVE,
|
||||
AggregateChange<CascadingReferenceEntity> aggregateChange = new AggregateChange<>(Kind.SAVE,
|
||||
CascadingReferenceEntity.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath, this::actualEntityType,
|
||||
this::isWithDependsOn) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath, DbActionTestSupport::actualEntityType,
|
||||
DbActionTestSupport::isWithDependsOn) //
|
||||
.containsExactly( //
|
||||
tuple(Delete.class, Element.class, "other.element", null, false),
|
||||
tuple(Delete.class, CascadingReferenceMiddleElement.class, "other", null, false),
|
||||
@@ -239,11 +239,11 @@ public class RelationalEntityWriterUnitTests {
|
||||
public void newEntityWithEmptyMapResultsInSingleInsert() {
|
||||
|
||||
MapContainer entity = new MapContainer(null);
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange(Kind.SAVE, MapContainer.class, entity);
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, MapContainer.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
|
||||
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, MapContainer.class, ""));
|
||||
}
|
||||
@@ -255,11 +255,11 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.put("one", new Element(null));
|
||||
entity.elements.put("two", new Element(null));
|
||||
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange(Kind.SAVE, MapContainer.class, entity);
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, MapContainer.class, entity);
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, this::extractPath) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, DbActionTestSupport::extractPath) //
|
||||
.containsExactlyInAnyOrder( //
|
||||
tuple(InsertRoot.class, MapContainer.class, null, ""), //
|
||||
tuple(Insert.class, Element.class, "one", "elements"), //
|
||||
@@ -290,11 +290,11 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.put("a", new Element(null));
|
||||
entity.elements.put("b", new Element(null));
|
||||
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange(Kind.SAVE, MapContainer.class, entity);
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, MapContainer.class, entity);
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, this::extractPath) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, DbActionTestSupport::extractPath) //
|
||||
.containsExactlyInAnyOrder( //
|
||||
tuple(InsertRoot.class, MapContainer.class, null, ""), //
|
||||
tuple(Insert.class, Element.class, "1", "elements"), //
|
||||
@@ -316,11 +316,11 @@ public class RelationalEntityWriterUnitTests {
|
||||
public void newEntityWithEmptyListResultsInSingleInsert() {
|
||||
|
||||
ListContainer entity = new ListContainer(null);
|
||||
AggregateChange<ListContainer> aggregateChange = new AggregateChange(Kind.SAVE, ListContainer.class, entity);
|
||||
AggregateChange<ListContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, ListContainer.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
|
||||
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, DbActionTestSupport::extractPath) //
|
||||
.containsExactly( //
|
||||
tuple(InsertRoot.class, ListContainer.class, ""));
|
||||
}
|
||||
@@ -332,11 +332,11 @@ public class RelationalEntityWriterUnitTests {
|
||||
entity.elements.add(new Element(null));
|
||||
entity.elements.add(new Element(null));
|
||||
|
||||
AggregateChange<ListContainer> aggregateChange = new AggregateChange(Kind.SAVE, ListContainer.class, entity);
|
||||
AggregateChange<ListContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, ListContainer.class, entity);
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions())
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getListKey, this::extractPath) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getListKey, DbActionTestSupport::extractPath) //
|
||||
.containsExactlyInAnyOrder( //
|
||||
tuple(InsertRoot.class, ListContainer.class, null, ""), //
|
||||
tuple(Insert.class, Element.class, 0, "elements"), //
|
||||
@@ -356,12 +356,12 @@ public class RelationalEntityWriterUnitTests {
|
||||
MapContainer entity = new MapContainer(SOME_ENTITY_ID);
|
||||
entity.elements.put("one", new Element(null));
|
||||
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange(Kind.SAVE, MapContainer.class, entity);
|
||||
AggregateChange<MapContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, MapContainer.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, this::extractPath) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, DbActionTestSupport::extractPath) //
|
||||
.containsExactly( //
|
||||
tuple(Delete.class, Element.class, null, "elements"), //
|
||||
tuple(UpdateRoot.class, MapContainer.class, null, ""), //
|
||||
@@ -375,12 +375,12 @@ public class RelationalEntityWriterUnitTests {
|
||||
ListContainer entity = new ListContainer(SOME_ENTITY_ID);
|
||||
entity.elements.add(new Element(null));
|
||||
|
||||
AggregateChange<ListContainer> aggregateChange = new AggregateChange(Kind.SAVE, ListContainer.class, entity);
|
||||
AggregateChange<ListContainer> aggregateChange = new AggregateChange<>(Kind.SAVE, ListContainer.class, entity);
|
||||
|
||||
converter.write(entity, aggregateChange);
|
||||
|
||||
assertThat(aggregateChange.getActions()) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getListKey, this::extractPath) //
|
||||
.extracting(DbAction::getClass, DbAction::getEntityType, this::getListKey, DbActionTestSupport::extractPath) //
|
||||
.containsExactly( //
|
||||
tuple(Delete.class, Element.class, null, "elements"), //
|
||||
tuple(UpdateRoot.class, ListContainer.class, null, ""), //
|
||||
@@ -408,27 +408,6 @@ public class RelationalEntityWriterUnitTests {
|
||||
: null;
|
||||
}
|
||||
|
||||
private String extractPath(DbAction action) {
|
||||
|
||||
if (action instanceof DbAction.WithPropertyPath) {
|
||||
return ((DbAction.WithPropertyPath<?>) action).getPropertyPath().toDotPath();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean isWithDependsOn(DbAction dbAction) {
|
||||
return dbAction instanceof DbAction.WithDependingOn;
|
||||
}
|
||||
|
||||
private Class<?> actualEntityType(DbAction a) {
|
||||
|
||||
if (a instanceof DbAction.WithEntity) {
|
||||
return ((DbAction.WithEntity) a).getEntity().getClass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class SingleReferenceEntity {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user