Browse Source
Formatting. Removed AbstractRelationalEntityWriter. Fixed integration test for MS-SQL. Extracted some common test infrastructure. Original pull request: #107.pull/108/head
22 changed files with 917 additions and 774 deletions
@ -0,0 +1,102 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
CREATE TABLE dummy_entity ( id_Prop BIGINT PRIMARY KEY, NAME VARCHAR(100)) |
||||
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
CREATE TABLE dummy_entity (id_Prop BIGINT PRIMARY KEY, NAME VARCHAR(100)); |
||||
@ -0,0 +1,2 @@
@@ -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 @@
@@ -0,0 +1 @@
|
||||
CREATE TABLE dummy_entity (id_Prop BIGINT PRIMARY KEY, NAME VARCHAR(100)); |
||||
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
DROP TABLE dummy_entity; |
||||
CREATE TABLE dummy_entity (id_Prop INTEGER PRIMARY KEY, NAME VARCHAR(100)); |
||||
@ -1,274 +0,0 @@
@@ -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 @@
@@ -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; |
||||
} |
||||
@ -0,0 +1,252 @@
@@ -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 @@
@@ -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; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue