Browse Source

DATAJDBC-145 - Back-reference in insert now honours NamingStrategy.

Added a property path to DbActions in order to make them aware of the property it is related to.
Then used the JdbcPersistentProperty based on that get the name of the back-reference name of that property back to the root entity of that property.
This gets the NamingStrategy properly involved, as it should.

Instead of using the vanilla PropertyPath a new JdbcPropertyPath is introduced.
It allows for an empty path avoiding various null-checks.

Upgraded SD-Commons dependency to 2.1.x in order to utilise DATACMNS-1199.
Removed the now superfluous PropertyPaths and used PropertyPath methods instead.

Related issues:
DATACMNS-1204
DATACMSN-1199
pull/22/merge
Jens Schauder 8 years ago committed by Greg Turnquist
parent
commit
207278c891
No known key found for this signature in database
GPG Key ID: CB2FA4D512B5C413
  1. 4
      pom.xml
  2. 38
      src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java
  3. 5
      src/main/java/org/springframework/data/jdbc/core/SqlGenerator.java
  4. 58
      src/main/java/org/springframework/data/jdbc/core/conversion/DbAction.java
  5. 4
      src/main/java/org/springframework/data/jdbc/core/conversion/JdbcEntityDeleteWriter.java
  6. 51
      src/main/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriter.java
  7. 3
      src/main/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterSupport.java
  8. 66
      src/main/java/org/springframework/data/jdbc/core/conversion/JdbcPropertyPath.java
  9. 4
      src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java
  10. 46
      src/main/java/org/springframework/data/jdbc/mapping/model/PropertyPaths.java
  11. 81
      src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java
  12. 81
      src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterUnitTests.java
  13. 4
      src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryManipulateDbActionsIntegrationTests.java

4
pom.xml

@ -15,14 +15,14 @@ @@ -15,14 +15,14 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<version>2.1.0.BUILD-SNAPSHOT</version>
</parent>
<properties>
<dist.key>DATAJDBC</dist.key>
<springdata.commons>2.0.0.BUILD-SNAPSHOT</springdata.commons>
<springdata.commons>2.1.0.BUILD-SNAPSHOT</springdata.commons>
<java-module-name>spring.data.jdbc</java-module-name>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>

38
src/main/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreter.java

@ -26,6 +26,8 @@ import org.springframework.data.jdbc.core.conversion.DbAction.Update; @@ -26,6 +26,8 @@ import org.springframework.data.jdbc.core.conversion.DbAction.Update;
import org.springframework.data.jdbc.core.conversion.Interpreter;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.Assert;
/**
* {@link Interpreter} for {@link DbAction}s using a {@link DataAccessStrategy} for performing actual database
@ -60,15 +62,15 @@ class DefaultJdbcInterpreter implements Interpreter { @@ -60,15 +62,15 @@ class DefaultJdbcInterpreter implements Interpreter {
if (delete.getPropertyPath() == null) {
accessStrategy.delete(delete.getRootId(), delete.getEntityType());
} else {
accessStrategy.delete(delete.getRootId(), delete.getPropertyPath());
accessStrategy.delete(delete.getRootId(), delete.getPropertyPath().getPath());
}
}
@Override
public <T> void interpret(DeleteAll<T> delete) {
if (delete.getEntityType() == null) {
accessStrategy.deleteAll(delete.getPropertyPath());
accessStrategy.deleteAll(delete.getPropertyPath().getPath());
} else {
accessStrategy.deleteAll(delete.getEntityType());
}
@ -84,16 +86,32 @@ class DefaultJdbcInterpreter implements Interpreter { @@ -84,16 +86,32 @@ class DefaultJdbcInterpreter implements Interpreter {
}
private <T> void addDependingOnInformation(Insert<T> insert, Map<String, Object> additionalColumnValues) {
DbAction dependingOn = insert.getDependingOn();
if (dependingOn != null) {
if (dependingOn == null) {
return;
}
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(dependingOn.getEntityType());
String columnName = persistentEntity.getTableName();
Object entity = dependingOn.getEntity();
Object identifier = persistentEntity.getIdentifierAccessor(entity).getIdentifier();
JdbcPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(dependingOn.getEntityType());
additionalColumnValues.put(columnName, identifier);
}
String columnName = getColumnNameForReverseColumn(insert, persistentEntity);
Object identifier = getIdFromEntityDependingOn(dependingOn, persistentEntity);
additionalColumnValues.put(columnName, identifier);
}
private Object getIdFromEntityDependingOn(DbAction dependingOn, JdbcPersistentEntity<?> persistentEntity) {
return persistentEntity.getIdentifierAccessor(dependingOn.getEntity()).getIdentifier();
}
private <T> String getColumnNameForReverseColumn(Insert<T> insert, JdbcPersistentEntity<?> persistentEntity) {
PropertyPath path = insert.getPropertyPath().getPath();
Assert.notNull(path, "There shouldn't be an insert depending on another insert without having a PropertyPath.");
return persistentEntity.getRequiredPersistentProperty(path.getSegment()).getReverseColumnName();
}
}

5
src/main/java/org/springframework/data/jdbc/core/SqlGenerator.java

@ -26,7 +26,6 @@ import java.util.stream.Stream; @@ -26,7 +26,6 @@ import java.util.stream.Stream;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import org.springframework.data.jdbc.mapping.model.PropertyPaths;
import org.springframework.data.jdbc.repository.SimpleJdbcRepository;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.PropertyPath;
@ -267,7 +266,7 @@ class SqlGenerator { @@ -267,7 +266,7 @@ class SqlGenerator {
return String.format("DELETE FROM %s", entity.getTableName());
}
JdbcPersistentEntity<?> entityToDelete = context.getRequiredPersistentEntity(PropertyPaths.getLeafType(path));
JdbcPersistentEntity<?> entityToDelete = context.getRequiredPersistentEntity(path.getLeafType());
JdbcPersistentEntity<?> owningEntity = context.getRequiredPersistentEntity(path.getOwningType());
JdbcPersistentProperty property = owningEntity.getRequiredPersistentProperty(path.getSegment());
@ -285,7 +284,7 @@ class SqlGenerator { @@ -285,7 +284,7 @@ class SqlGenerator {
String createDeleteByPath(PropertyPath path) {
JdbcPersistentEntity<?> entityToDelete = context.getRequiredPersistentEntity(PropertyPaths.getLeafType(path));
JdbcPersistentEntity<?> entityToDelete = context.getRequiredPersistentEntity(path.getLeafType());
JdbcPersistentEntity<?> owningEntity = context.getRequiredPersistentEntity(path.getOwningType());
JdbcPersistentProperty property = owningEntity.getRequiredPersistentProperty(path.getSegment());

58
src/main/java/org/springframework/data/jdbc/core/conversion/DbAction.java

@ -21,7 +21,6 @@ import lombok.ToString; @@ -21,7 +21,6 @@ import lombok.ToString;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.Assert;
/**
@ -43,6 +42,11 @@ public abstract class DbAction<T> { @@ -43,6 +42,11 @@ public abstract class DbAction<T> {
*/
private final T entity;
/**
* The path from the Aggregate Root to the entity affected by this {@link DbAction}.
*/
private final JdbcPropertyPath propertyPath;
/**
* Key-value-pairs to specify additional values to be used with the statement which can't be obtained from the entity,
* nor from {@link DbAction}s {@literal this} depends on. A used case are map keys, which need to be persisted with
@ -57,27 +61,28 @@ public abstract class DbAction<T> { @@ -57,27 +61,28 @@ public abstract class DbAction<T> {
*/
private final DbAction dependingOn;
private DbAction(Class<T> entityType, T entity, DbAction dependingOn) {
private DbAction(Class<T> entityType, T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
this.entityType = entityType;
this.entity = entity;
this.propertyPath = propertyPath;
this.dependingOn = dependingOn;
}
public static <T> Insert<T> insert(T entity, DbAction dependingOn) {
return new Insert<>(entity, dependingOn);
public static <T> Insert<T> insert(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
return new Insert<>(entity, propertyPath, dependingOn);
}
public static <T> Update<T> update(T entity, DbAction dependingOn) {
return new Update<>(entity, dependingOn);
public static <T> Update<T> update(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
return new Update<>(entity, propertyPath, dependingOn);
}
public static <T> Delete<T> delete(Object id, Class<T> type, T entity, PropertyPath propertyPath,
public static <T> Delete<T> delete(Object id, Class<T> type, T entity, JdbcPropertyPath propertyPath,
DbAction dependingOn) {
return new Delete<>(id, type, entity, propertyPath, dependingOn);
}
public static <T> DeleteAll<T> deleteAll(Class<T> type, PropertyPath propertyPath, DbAction dependingOn) {
public static <T> DeleteAll<T> deleteAll(Class<T> type, JdbcPropertyPath propertyPath, DbAction dependingOn) {
return new DeleteAll<>(type, propertyPath, dependingOn);
}
@ -90,8 +95,9 @@ public abstract class DbAction<T> { @@ -90,8 +95,9 @@ public abstract class DbAction<T> {
*/
abstract static class InsertOrUpdate<T> extends DbAction<T> {
InsertOrUpdate(T entity, DbAction dependingOn) {
super((Class<T>) entity.getClass(), entity, dependingOn);
@SuppressWarnings("unchecked")
InsertOrUpdate(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
super((Class<T>) entity.getClass(), entity, propertyPath, dependingOn);
}
}
@ -102,8 +108,8 @@ public abstract class DbAction<T> { @@ -102,8 +108,8 @@ public abstract class DbAction<T> {
*/
public static class Insert<T> extends InsertOrUpdate<T> {
private Insert(T entity, DbAction dependingOn) {
super(entity, dependingOn);
private Insert(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
super(entity, propertyPath, dependingOn);
}
@Override
@ -119,8 +125,8 @@ public abstract class DbAction<T> { @@ -119,8 +125,8 @@ public abstract class DbAction<T> {
*/
public static class Update<T> extends InsertOrUpdate<T> {
private Update(T entity, DbAction dependingOn) {
super(entity, dependingOn);
private Update(T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
super(entity, propertyPath, dependingOn);
}
@Override
@ -143,20 +149,13 @@ public abstract class DbAction<T> { @@ -143,20 +149,13 @@ public abstract class DbAction<T> {
*/
private final Object rootId;
/**
* {@link PropertyPath} which connects the aggregate root with the entities to be deleted. If this is the action to
* delete the root enity itself, this is {@literal null}.
*/
private final PropertyPath propertyPath;
private Delete(Object rootId, Class<T> type, T entity, PropertyPath propertyPath, DbAction dependingOn) {
private Delete(Object rootId, Class<T> type, T entity, JdbcPropertyPath propertyPath, DbAction dependingOn) {
super(type, entity, dependingOn);
super(type, entity, propertyPath, dependingOn);
Assert.notNull(rootId, "rootId must not be null.");
this.rootId = rootId;
this.propertyPath = propertyPath;
}
@Override
@ -170,19 +169,10 @@ public abstract class DbAction<T> { @@ -170,19 +169,10 @@ public abstract class DbAction<T> {
*
* @param <T> type o the entity for which this represents a database interaction
*/
@Getter
public static class DeleteAll<T> extends DbAction<T> {
/**
*
*/
private final PropertyPath propertyPath;
private DeleteAll(Class<T> entityType, PropertyPath propertyPath, DbAction dependingOn) {
super(entityType, null, dependingOn);
this.propertyPath = propertyPath;
private DeleteAll(Class<T> entityType, JdbcPropertyPath propertyPath, DbAction dependingOn) {
super(entityType, null, propertyPath, dependingOn);
}
@Override

4
src/main/java/org/springframework/data/jdbc/core/conversion/JdbcEntityDeleteWriter.java

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
package org.springframework.data.jdbc.core.conversion;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.PropertyPaths;
/**
* Converts an entity that is about to be deleted into {@link DbAction}s inside a {@link AggregateChange} that need to be
@ -43,7 +42,7 @@ public class JdbcEntityDeleteWriter extends JdbcEntityWriterSupport { @@ -43,7 +42,7 @@ public class JdbcEntityDeleteWriter extends JdbcEntityWriterSupport {
private void deleteAll(AggregateChange aggregateChange) {
context.referencedEntities(aggregateChange.getEntityType(), null)
.forEach(p -> aggregateChange.addAction(DbAction.deleteAll(PropertyPaths.getLeafType(p), p, null)));
.forEach(p -> aggregateChange.addAction(DbAction.deleteAll(p.getLeafType(), new JdbcPropertyPath(p), null)));
aggregateChange.addAction(DbAction.deleteAll(aggregateChange.getEntityType(), null, null));
}
@ -54,5 +53,4 @@ public class JdbcEntityDeleteWriter extends JdbcEntityWriterSupport { @@ -54,5 +53,4 @@ public class JdbcEntityDeleteWriter extends JdbcEntityWriterSupport {
aggregateChange.addAction(DbAction.delete(id, aggregateChange.getEntityType(), aggregateChange.getEntity(), null, null));
}
}

51
src/main/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriter.java

@ -52,76 +52,85 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport { @@ -52,76 +52,85 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
private void write(Object o, AggregateChange aggregateChange, DbAction dependingOn) {
JdbcPersistentEntityInformation<Object, ?> entityInformation = context
.getRequiredPersistentEntityInformation((Class<Object>) o.getClass());
Class<Object> type = (Class<Object>) o.getClass();
JdbcPersistentEntityInformation<Object, ?> entityInformation = context.getRequiredPersistentEntityInformation(type);
JdbcPropertyPath propertyPath = JdbcPropertyPath.from("", type);
if (entityInformation.isNew(o)) {
Insert<Object> insert = DbAction.insert(o, dependingOn);
Insert<Object> insert = DbAction.insert(o, propertyPath, dependingOn);
aggregateChange.addAction(insert);
referencedEntities(o).forEach(propertyAndValue -> saveReferencedEntities(propertyAndValue, aggregateChange, insert));
referencedEntities(o).forEach(propertyAndValue -> saveReferencedEntities(propertyAndValue, aggregateChange,
propertyPath.nested(propertyAndValue.property.getName()), insert));
} else {
deleteReferencedEntities(entityInformation.getRequiredId(o), aggregateChange);
Update<Object> update = DbAction.update(o, dependingOn);
Update<Object> update = DbAction.update(o, propertyPath, dependingOn);
aggregateChange.addAction(update);
referencedEntities(o).forEach(propertyAndValue -> insertReferencedEntities(propertyAndValue, aggregateChange, update));
referencedEntities(o).forEach(
propertyAndValue -> insertReferencedEntities(propertyAndValue, aggregateChange, propertyPath.nested(propertyAndValue.property.getName()), update));
}
}
private void saveReferencedEntities(PropertyAndValue propertyAndValue, AggregateChange aggregateChange, DbAction dependingOn) {
private void saveReferencedEntities(PropertyAndValue propertyAndValue, AggregateChange aggregateChange,
JdbcPropertyPath propertyPath, DbAction dependingOn) {
saveActions(propertyAndValue, dependingOn).forEach(a -> {
saveActions(propertyAndValue, propertyPath, dependingOn).forEach(a -> {
aggregateChange.addAction(a);
referencedEntities(propertyAndValue.value).forEach(pav -> saveReferencedEntities(pav, aggregateChange, a));
referencedEntities(propertyAndValue.value)
.forEach(pav -> saveReferencedEntities(pav, aggregateChange, propertyPath.nested(pav.property.getName()), a));
});
}
private Stream<DbAction> saveActions(PropertyAndValue propertyAndValue, DbAction dependingOn) {
private Stream<DbAction> saveActions(PropertyAndValue propertyAndValue, JdbcPropertyPath propertyPath,
DbAction dependingOn) {
if (Map.Entry.class.isAssignableFrom(ClassUtils.getUserClass(propertyAndValue.value))) {
return mapEntrySaveAction(propertyAndValue, dependingOn);
return mapEntrySaveAction(propertyAndValue, propertyPath, dependingOn);
}
return Stream.of(singleSaveAction(propertyAndValue.value, dependingOn));
return Stream.of(singleSaveAction(propertyAndValue.value, propertyPath, dependingOn));
}
private Stream<DbAction> mapEntrySaveAction(PropertyAndValue propertyAndValue, DbAction dependingOn) {
private Stream<DbAction> mapEntrySaveAction(PropertyAndValue propertyAndValue, JdbcPropertyPath propertyPath,
DbAction dependingOn) {
Map.Entry<Object, Object> entry = (Map.Entry) propertyAndValue.value;
DbAction action = singleSaveAction(entry.getValue(), dependingOn);
DbAction action = singleSaveAction(entry.getValue(), propertyPath, dependingOn);
action.getAdditionalValues().put(propertyAndValue.property.getKeyColumn(), entry.getKey());
return Stream.of(action);
}
private <T> DbAction singleSaveAction(T t, DbAction dependingOn) {
private <T> DbAction singleSaveAction(T t, JdbcPropertyPath propertyPath, DbAction dependingOn) {
JdbcPersistentEntityInformation<T, ?> entityInformation = context
.getRequiredPersistentEntityInformation((Class<T>) ClassUtils.getUserClass(t));
return entityInformation.isNew(t) ? DbAction.insert(t, dependingOn) : DbAction.update(t, dependingOn);
return entityInformation.isNew(t) ? DbAction.insert(t, propertyPath, dependingOn)
: DbAction.update(t, propertyPath, dependingOn);
}
private void insertReferencedEntities(PropertyAndValue propertyAndValue, AggregateChange aggregateChange, DbAction dependingOn) {
private void insertReferencedEntities(PropertyAndValue propertyAndValue, AggregateChange aggregateChange,
JdbcPropertyPath propertyPath, DbAction dependingOn) {
Insert<Object> insert;
if (propertyAndValue.property.isQualified()) {
Entry<Object, Object> valueAsEntry = (Entry<Object, Object>) propertyAndValue.value;
insert = DbAction.insert(valueAsEntry.getValue(), dependingOn);
insert = DbAction.insert(valueAsEntry.getValue(), propertyPath, dependingOn);
insert.getAdditionalValues().put(propertyAndValue.property.getKeyColumn(), valueAsEntry.getKey());
} else {
insert = DbAction.insert(propertyAndValue.value, dependingOn);
insert = DbAction.insert(propertyAndValue.value, propertyPath, dependingOn);
}
aggregateChange.addAction(insert);
referencedEntities(insert.getEntity())
.forEach(pav -> insertReferencedEntities(pav, aggregateChange, dependingOn));
.forEach(pav -> insertReferencedEntities(pav, aggregateChange, propertyPath.nested(pav.property.getName()), dependingOn));
}
private Stream<PropertyAndValue> referencedEntities(Object o) {
@ -133,7 +142,7 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport { @@ -133,7 +142,7 @@ public class JdbcEntityWriter extends JdbcEntityWriterSupport {
.flatMap( //
p -> referencedEntity(p, persistentEntity.getPropertyAccessor(o)) //
.map(e -> new PropertyAndValue(p, e)) //
);
);
}
private Stream<Object> referencedEntity(JdbcPersistentProperty p, PersistentPropertyAccessor propertyAccessor) {

3
src/main/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterSupport.java

@ -17,7 +17,6 @@ package org.springframework.data.jdbc.core.conversion; @@ -17,7 +17,6 @@ package org.springframework.data.jdbc.core.conversion;
import org.springframework.data.convert.EntityWriter;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.PropertyPaths;
/**
* Common infrastructure needed by different implementations of {@link EntityWriter}<Object, AggregateChange>.
@ -41,6 +40,6 @@ abstract class JdbcEntityWriterSupport implements EntityWriter<Object, Aggregate @@ -41,6 +40,6 @@ abstract class JdbcEntityWriterSupport implements EntityWriter<Object, Aggregate
void deleteReferencedEntities(Object id, AggregateChange aggregateChange) {
context.referencedEntities(aggregateChange.getEntityType(), null)
.forEach(p -> aggregateChange.addAction(DbAction.delete(id, PropertyPaths.getLeafType(p), null, p, null)));
.forEach(p -> aggregateChange.addAction(DbAction.delete(id, p.getLeafType(), null, new JdbcPropertyPath(p), null)));
}
}

66
src/main/java/org/springframework/data/jdbc/core/conversion/JdbcPropertyPath.java

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.conversion;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.StringUtils;
/**
* A replacement for {@link org.springframework.data.mapping.PropertyPath} as long as it doesn't support objects with
* empty path.
*
* See https://jira.spring.io/browse/DATACMNS-1204.
*
* @author Jens Schauder
*/
public class JdbcPropertyPath {
private final PropertyPath path;
private final Class<?> rootType;
JdbcPropertyPath(PropertyPath path) {
this.path = path;
this.rootType = null;
}
private JdbcPropertyPath(Class<?> type) {
this.path = null;
this.rootType = type;
}
public static JdbcPropertyPath from(String source, Class<?> type) {
if (StringUtils.isEmpty(source)) {
return new JdbcPropertyPath(type);
} else {
return new JdbcPropertyPath(PropertyPath.from(source, type));
}
}
public JdbcPropertyPath nested(String name) {
return path == null ? new JdbcPropertyPath(PropertyPath.from(name, rootType)) : new JdbcPropertyPath(path.nested(name));
}
public PropertyPath getPath() {
return path;
}
public String toDotPath() {
return path == null ? "" : path.toDotPath();
}
}

4
src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java

@ -61,14 +61,14 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt @@ -61,14 +61,14 @@ public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEnt
List<PropertyPath> paths = new ArrayList<>();
Class<?> currentType = path == null ? rootType : PropertyPaths.getLeafType(path);
Class<?> currentType = path == null ? rootType : path.getLeafType();
JdbcPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(currentType);
for (JdbcPersistentProperty property : persistentEntity) {
if (property.isEntity()) {
PropertyPath nextPath = path == null ? PropertyPath.from(property.getName(), rootType)
: PropertyPaths.extendBy(path, property.getColumnName());
: path.nested(property.getColumnName());
paths.add(nextPath);
paths.addAll(referencedEntities(rootType, nextPath));
}

46
src/main/java/org/springframework/data/jdbc/mapping/model/PropertyPaths.java

@ -1,46 +0,0 @@ @@ -1,46 +0,0 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.mapping.model;
import lombok.experimental.UtilityClass;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.util.Assert;
/**
* Utilities for working with {@link PropertyPath}s.
*
* @author Jens Schauder
*/
@UtilityClass
public class PropertyPaths {
public static Class<?> getLeafType(PropertyPath path) {
if (path.hasNext()) {
return getLeafType(path.next());
}
return path.getType();
}
public static PropertyPath extendBy(PropertyPath path, String name) {
Assert.notNull(path, "Path must not be null.");
Assert.hasText(name, "Name must not be empty");
return PropertyPath.from(path.toDotPath() + "." + name, path.getOwningType());
}
}

81
src/test/java/org/springframework/data/jdbc/core/DefaultJdbcInterpreterUnitTests.java

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.conversion.DbAction;
import org.springframework.data.jdbc.core.conversion.DbAction.Insert;
import org.springframework.data.jdbc.core.conversion.JdbcPropertyPath;
import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
/**
* Unit tests for {@link DefaultJdbcInterpreter}
*
* @author Jens Schauder
*/
public class DefaultJdbcInterpreterUnitTests {
static final long CONTAINER_ID = 23L;
static final String BACK_REFERENCE = "back-reference";
JdbcMappingContext context = new JdbcMappingContext(new DefaultNamingStrategy() {
@Override
public String getReverseColumnName(JdbcPersistentProperty property) {
return BACK_REFERENCE;
}
});
DataAccessStrategy dataAccessStrategy = mock(DataAccessStrategy.class);
DefaultJdbcInterpreter interpreter = new DefaultJdbcInterpreter(context, dataAccessStrategy);
@Test // DATAJDBC-145
public void insertDoesHonourNamingStrategyForBackReference() {
Container container = new Container();
container.id = CONTAINER_ID;
Element element = new Element();
Insert<?> containerInsert = DbAction.insert(container, JdbcPropertyPath.from("", Container.class), null);
Insert<?> insert = DbAction.insert(element, JdbcPropertyPath.from("element", Container.class), containerInsert);
interpreter.interpret(insert);
ArgumentCaptor<Map<String, Object>> argumentCaptor = ArgumentCaptor.forClass(Map.class);
verify(dataAccessStrategy).insert(eq(element), eq(Element.class), argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).containsExactly(new SimpleEntry(BACK_REFERENCE, CONTAINER_ID));
}
static class Container {
@Id Long id;
Element element;
}
static class Element {}
}

81
src/test/java/org/springframework/data/jdbc/core/conversion/JdbcEntityWriterUnitTests.java

@ -56,9 +56,9 @@ public class JdbcEntityWriterUnitTests { @@ -56,9 +56,9 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
.containsExactly( //
tuple(Insert.class, SingleReferenceEntity.class) //
tuple(Insert.class, SingleReferenceEntity.class, "") //
);
}
@ -72,10 +72,10 @@ public class JdbcEntityWriterUnitTests { @@ -72,10 +72,10 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
.containsExactly( //
tuple(Delete.class, Element.class), //
tuple(Update.class, SingleReferenceEntity.class) //
tuple(Delete.class, Element.class, "other"), //
tuple(Update.class, SingleReferenceEntity.class, "") //
);
}
@ -91,11 +91,11 @@ public class JdbcEntityWriterUnitTests { @@ -91,11 +91,11 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
.containsExactly( //
tuple(Delete.class, Element.class), //
tuple(Update.class, SingleReferenceEntity.class), //
tuple(Insert.class, Element.class) //
tuple(Delete.class, Element.class, "other"), //
tuple(Update.class, SingleReferenceEntity.class, ""), //
tuple(Insert.class, Element.class, "other") //
);
}
@ -107,9 +107,10 @@ public class JdbcEntityWriterUnitTests { @@ -107,9 +107,10 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType) //
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
.containsExactly( //
tuple(Insert.class, SetContainer.class));
tuple(Insert.class, SetContainer.class, ""));
}
@Test // DATAJDBC-113
@ -122,11 +123,11 @@ public class JdbcEntityWriterUnitTests { @@ -122,11 +123,11 @@ public class JdbcEntityWriterUnitTests {
AggregateChange<SingleReferenceEntity> aggregateChange = new AggregateChange(Kind.SAVE, SetContainer.class, entity);
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType) //
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
.containsExactly( //
tuple(Insert.class, SetContainer.class), //
tuple(Insert.class, Element.class), //
tuple(Insert.class, Element.class) //
tuple(Insert.class, SetContainer.class, ""), //
tuple(Insert.class, Element.class, "elements"), //
tuple(Insert.class, Element.class, "elements") //
);
}
@ -149,15 +150,15 @@ public class JdbcEntityWriterUnitTests { @@ -149,15 +150,15 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType) //
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
.containsExactly( //
tuple(Insert.class, CascadingReferenceEntity.class), //
tuple(Insert.class, CascadingReferenceMiddleElement.class), //
tuple(Insert.class, Element.class), //
tuple(Insert.class, Element.class), //
tuple(Insert.class, CascadingReferenceMiddleElement.class), //
tuple(Insert.class, Element.class), //
tuple(Insert.class, Element.class) //
tuple(Insert.class, CascadingReferenceEntity.class, ""), //
tuple(Insert.class, CascadingReferenceMiddleElement.class, "other"), //
tuple(Insert.class, Element.class, "other.element"), //
tuple(Insert.class, Element.class, "other.element"), //
tuple(Insert.class, CascadingReferenceMiddleElement.class, "other"), //
tuple(Insert.class, Element.class, "other.element"), //
tuple(Insert.class, Element.class, "other.element") //
);
}
@ -169,9 +170,9 @@ public class JdbcEntityWriterUnitTests { @@ -169,9 +170,9 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType) //
assertThat(aggregateChange.getActions()).extracting(DbAction::getClass, DbAction::getEntityType, this::extractPath) //
.containsExactly( //
tuple(Insert.class, MapContainer.class));
tuple(Insert.class, MapContainer.class, ""));
}
@Test // DATAJDBC-131
@ -185,17 +186,17 @@ public class JdbcEntityWriterUnitTests { @@ -185,17 +186,17 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions())
.extracting(DbAction::getClass, DbAction::getEntityType, JdbcEntityWriterUnitTests::getMapKey) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, this::extractPath) //
.containsExactlyInAnyOrder( //
tuple(Insert.class, MapContainer.class, null), //
tuple(Insert.class, Element.class, "one"), //
tuple(Insert.class, Element.class, "two") //
tuple(Insert.class, MapContainer.class, null, ""), //
tuple(Insert.class, Element.class, "one", "elements"), //
tuple(Insert.class, Element.class, "two", "elements") //
).containsSubsequence( // container comes before the elements
tuple(Insert.class, MapContainer.class, null), //
tuple(Insert.class, Element.class, "two") //
tuple(Insert.class, MapContainer.class, null, ""), //
tuple(Insert.class, Element.class, "two", "elements") //
).containsSubsequence( // container comes before the elements
tuple(Insert.class, MapContainer.class, null), //
tuple(Insert.class, Element.class, "one") //
tuple(Insert.class, MapContainer.class, null, ""), //
tuple(Insert.class, Element.class, "one", "elements") //
);
}
@ -210,11 +211,11 @@ public class JdbcEntityWriterUnitTests { @@ -210,11 +211,11 @@ public class JdbcEntityWriterUnitTests {
converter.write(entity, aggregateChange);
assertThat(aggregateChange.getActions()) //
.extracting(DbAction::getClass, DbAction::getEntityType, JdbcEntityWriterUnitTests::getMapKey) //
.extracting(DbAction::getClass, DbAction::getEntityType, this::getMapKey, this::extractPath) //
.containsExactly( //
tuple(Delete.class, Element.class, null), //
tuple(Update.class, MapContainer.class, null), //
tuple(Insert.class, Element.class, "one") //
tuple(Delete.class, Element.class, null, "elements"), //
tuple(Update.class, MapContainer.class, null, ""), //
tuple(Insert.class, Element.class, "one", "elements") //
);
}
@ -226,10 +227,14 @@ public class JdbcEntityWriterUnitTests { @@ -226,10 +227,14 @@ public class JdbcEntityWriterUnitTests {
return middleElement1;
}
private static Object getMapKey(DbAction a) {
private Object getMapKey(DbAction a) {
return a.getAdditionalValues().get("MapContainer_key");
}
private String extractPath(DbAction action) {
return action.getPropertyPath().toDotPath();
}
@RequiredArgsConstructor
static class SingleReferenceEntity {

4
src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryManipulateDbActionsIntegrationTests.java

@ -204,7 +204,7 @@ public class JdbcRepositoryManipulateDbActionsIntegrationTests { @@ -204,7 +204,7 @@ public class JdbcRepositoryManipulateDbActionsIntegrationTests {
List<DbAction> actions = event.getChange().getActions();
actions.clear();
actions.add(DbAction.update(entity, null));
actions.add(DbAction.update(entity, null, null));
};
}
@ -223,7 +223,7 @@ public class JdbcRepositoryManipulateDbActionsIntegrationTests { @@ -223,7 +223,7 @@ public class JdbcRepositoryManipulateDbActionsIntegrationTests {
List<DbAction> actions = event.getChange().getActions();
actions.add(DbAction.insert(log, null));
actions.add(DbAction.insert(log, null, null));
};
}

Loading…
Cancel
Save