mapping policy is provided through all method calls
This commit is contained in:
@@ -245,7 +245,7 @@ public privileged aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMix
|
||||
*/
|
||||
Object around(NodeBacked entity): entityFieldGet(entity) {
|
||||
if (entity.entityState==null) return proceed(entity);
|
||||
Object result=entity.entityState.getValue(field(thisJoinPoint));
|
||||
Object result=entity.entityState.getValue(field(thisJoinPoint),null);
|
||||
if (result instanceof DoReturn) return unwrap(result);
|
||||
return proceed(entity);
|
||||
}
|
||||
@@ -255,7 +255,7 @@ public privileged aspect Neo4jNodeBacking { // extends AbstractTypeAnnotatingMix
|
||||
*/
|
||||
Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
|
||||
if (entity.entityState==null) return proceed(entity,newVal);
|
||||
Object result=entity.entityState.setValue(field(thisJoinPoint),newVal);
|
||||
Object result=entity.entityState.setValue(field(thisJoinPoint),newVal,null);
|
||||
if (result instanceof DoReturn) return unwrap(result);
|
||||
return proceed(entity,result);
|
||||
}
|
||||
|
||||
@@ -160,14 +160,14 @@ public aspect Neo4jRelationshipBacking {
|
||||
|
||||
Object around(RelationshipBacked entity): entityFieldGet(entity) {
|
||||
if (entity.entityState == null) return proceed(entity);
|
||||
Object result = entity.entityState.getValue(field(thisJoinPoint));
|
||||
Object result = entity.entityState.getValue(field(thisJoinPoint),null);
|
||||
if (result instanceof DoReturn) return unwrap(result);
|
||||
return proceed(entity);
|
||||
}
|
||||
|
||||
Object around(RelationshipBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
|
||||
if (entity.entityState == null) return proceed(entity,newVal);
|
||||
Object result=entity.entityState.setValue(field(thisJoinPoint),newVal);
|
||||
Object result=entity.entityState.setValue(field(thisJoinPoint),newVal,null);
|
||||
if (result instanceof DoReturn) return unwrap(result);
|
||||
return proceed(entity,result);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class IndexTest extends EntityTestBase {
|
||||
group.setIndexLevelName("indexLevelNameValue");
|
||||
Index<Node> subGroupIndex = neo4jTemplate.getIndex(SubGroup.class);
|
||||
final Node found = subGroupIndex.get("indexLevelName", "indexLevelNameValue").getSingle();
|
||||
final SubGroup foundEntity = neo4jTemplate.createEntityFromState(found, SubGroup.class);
|
||||
final SubGroup foundEntity = neo4jTemplate.createEntityFromState(found, SubGroup.class, neo4jTemplate.getMappingPolicy(SubGroup.class));
|
||||
assertEquals(group, foundEntity);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.springframework.data.neo4j.aspects.Person;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.repository.GraphRepository;
|
||||
import org.springframework.test.context.CleanContextCacheTestExecutionListener;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -47,9 +48,10 @@ public class NodeEntityInstantiationTest extends EntityTestBase {
|
||||
long nodeId = getNodeId(p);
|
||||
|
||||
Node node = neo4jTemplate.getNode(nodeId);
|
||||
Person person1 = (Person) neo4jTemplate.createEntityFromStoredType(node);
|
||||
final MappingPolicy mappingPolicy = neo4jTemplate.getMappingPolicy(Person.class);
|
||||
Person person1 = (Person) neo4jTemplate.createEntityFromStoredType(node, mappingPolicy);
|
||||
assertEquals("Rod", person1.getName());
|
||||
Person person2 = neo4jTemplate.createEntityFromState(node,Person.class);
|
||||
Person person2 = neo4jTemplate.createEntityFromState(node,Person.class, mappingPolicy);
|
||||
assertEquals("Rod", person2.getName());
|
||||
|
||||
GraphRepository<Person> finder = neo4jTemplate.repositoryFor(Person.class);
|
||||
|
||||
@@ -48,7 +48,7 @@ public class NodeEntityTest extends EntityTestBase {
|
||||
Person p = persistedPerson("Rod", 39);
|
||||
assertEquals(p.getName(), getNodeState(p).getProperty("name"));
|
||||
assertEquals(p.getAge(), getNodeState(p).getProperty("age"));
|
||||
Person found = neo4jTemplate.createEntityFromState(neo4jTemplate.getNode(getNodeId(p)), Person.class);
|
||||
Person found = neo4jTemplate.createEntityFromState(neo4jTemplate.getNode(getNodeId(p)), Person.class, neo4jTemplate.getMappingPolicy(p));
|
||||
assertEquals("Rod", getNodeState(found).getProperty("name"));
|
||||
assertEquals(39, getNodeState(found).getProperty("age"));
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class QueryEngineTest extends EntityTestBase {
|
||||
@Test
|
||||
public void testQueryListWithCustomConverter() throws Exception {
|
||||
final String queryString = "start person=node:name_index(name={name}) match (person) <-[:boss]- (boss) return boss";
|
||||
final Collection<String> result = IteratorUtil.asCollection(queryEngine.query(queryString, michaelsName()).to(String.class, new ResultConverter<Map<String, Object>, String>() {
|
||||
final Collection<String> result = IteratorUtil.asCollection(queryEngine.query(queryString, michaelsName()).to(String.class, new ResultConverter.ResultConverterAdapter<Map<String, Object>, String>() {
|
||||
@Override
|
||||
public String convert(Map<String, Object> row, Class<String> target) {
|
||||
return (String) ((Node) row.get("boss")).getProperty("name");
|
||||
|
||||
@@ -148,14 +148,14 @@ public class IndexingNodeTypeRepresentationStrategyTest extends EntityTestBase {
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCreateEntityAndInferType() throws Exception {
|
||||
Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing));
|
||||
Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing));
|
||||
assertEquals(thing, newThing);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCreateEntityAndSpecifyType() throws Exception {
|
||||
Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class);
|
||||
Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing));
|
||||
assertEquals(subThing, newThing);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,14 +129,14 @@ public class IndexingRelationshipTypeRepresentationStrategyTest extends EntityTe
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCreateEntityAndInferType() throws Exception {
|
||||
Link newLink = neo4jTemplate.createEntityFromStoredType(rel(link));
|
||||
Link newLink = neo4jTemplate.createEntityFromStoredType(rel(link), neo4jTemplate.getMappingPolicy(link));
|
||||
assertEquals(link, newLink);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCreateEntityAndSpecifyType() throws Exception {
|
||||
Link newLink = neo4jTemplate.createEntityFromState(rel(link), Link.class);
|
||||
Link newLink = neo4jTemplate.createEntityFromState(rel(link), Link.class, neo4jTemplate.getMappingPolicy(link));
|
||||
assertEquals(link, newLink);
|
||||
}
|
||||
|
||||
|
||||
@@ -202,14 +202,14 @@ public class SubReferenceNodeTypeRepresentationStrategyTest extends EntityTestBa
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCreateEntityAndInferType() throws Exception {
|
||||
Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing));
|
||||
Thing newThing = neo4jTemplate.createEntityFromStoredType(node(thing), neo4jTemplate.getMappingPolicy(thing));
|
||||
assertEquals(thing, newThing);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCreateEntityAndSpecifyType() throws Exception {
|
||||
Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class);
|
||||
Thing newThing = neo4jTemplate.createEntityFromState(node(subThing), Thing.class, neo4jTemplate.getMappingPolicy(subThing));
|
||||
assertEquals(subThing, newThing);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.neo4j.cross_store.support.node;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.springframework.data.neo4j.aspects.core.NodeBacked;
|
||||
import org.springframework.data.neo4j.mapping.EntityInstantiator;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.support.node.NodeEntityInstantiator;
|
||||
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
|
||||
|
||||
@@ -48,17 +49,18 @@ public class CrossStoreNodeEntityInstantiator implements EntityInstantiator<Node
|
||||
* this node is created by the original {@link org.springframework.data.neo4j.mapping.EntityInstantiator}.
|
||||
* @param n Node to instantiate an entity for
|
||||
* @param entityClass type of the entity
|
||||
* @param mappingPolicy
|
||||
* @param <T> generic type of the entity
|
||||
* @return
|
||||
*/
|
||||
public <T> T createEntityFromState(Node n, Class<T> entityClass) {
|
||||
public <T> T createEntityFromState(Node n, Class<T> entityClass, final MappingPolicy mappingPolicy) {
|
||||
if (n.hasProperty(CrossStoreNodeEntityState.FOREIGN_ID)) {
|
||||
final Object foreignId = n.getProperty(CrossStoreNodeEntityState.FOREIGN_ID);
|
||||
final T result = entityManager().find(entityClass, foreignId);
|
||||
((NodeBacked)result).setPersistentState(n);
|
||||
return result;
|
||||
}
|
||||
return delegate.createEntityFromState(n, entityClass);
|
||||
return delegate.createEntityFromState(n, entityClass, mappingPolicy);
|
||||
}
|
||||
|
||||
private EntityManager entityManager() {
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.index.IndexType;
|
||||
|
||||
import javax.persistence.PersistenceUnitUtil;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -90,8 +89,8 @@ public class CrossStoreNodeEntityState<ENTITY extends NodeBacked> extends Defaul
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable(Field field) {
|
||||
final FieldAccessor accessor = accessorFor(property(field));
|
||||
public boolean isWritable(Neo4jPersistentProperty property) {
|
||||
final FieldAccessor accessor = accessorFor(property);
|
||||
if (accessor == null) return false; // difference to default behaviour, we don't care for non-managed fields here
|
||||
return accessor.isWriteable(entity);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -10,6 +11,7 @@ import static org.junit.Assert.assertEquals;
|
||||
* @author mh
|
||||
* @since 13.03.11
|
||||
*/
|
||||
@Ignore
|
||||
public class MovieDbApiClientTest {
|
||||
private static final String API_KEY = "926d2a79e82920b62f03b1cb57e532e6";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.neo4j.cineasts.movieimport;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -10,6 +11,7 @@ import static org.junit.Assert.assertEquals;
|
||||
* @author mh
|
||||
* @since 13.03.11
|
||||
*/
|
||||
@Ignore
|
||||
public class MovieDbApiClientTest {
|
||||
private static final String API_KEY = "926d2a79e82920b62f03b1cb57e532e6";
|
||||
|
||||
|
||||
@@ -81,9 +81,9 @@ class ImdbServiceImpl implements ImdbService {
|
||||
int mod = 0;
|
||||
for (Node node : list.nodes()) {
|
||||
if (mod++ % 2 == 0) {
|
||||
actorAndMovieList.add(template.createEntityFromState(node, Actor.class));
|
||||
actorAndMovieList.add(template.load(node, Actor.class));
|
||||
} else {
|
||||
actorAndMovieList.add(template.createEntityFromState(node, Movie.class));
|
||||
actorAndMovieList.add(template.load(node, Movie.class));
|
||||
}
|
||||
}
|
||||
return actorAndMovieList;
|
||||
|
||||
@@ -64,9 +64,9 @@ public class TopRatedRestaurantFinder {
|
||||
}
|
||||
|
||||
private RatedRestaurant toRatedRestaurant(final CalculateRatingPredicate calculateRatingPredicate) {
|
||||
final RatedRestaurant ratedRestaurant = new RatedRestaurant(template.createEntityFromState(restaurant, Restaurant.class));
|
||||
final RatedRestaurant ratedRestaurant = new RatedRestaurant(template.load(restaurant, Restaurant.class));
|
||||
for (final Relationship recommendation : recommendations) {
|
||||
ratedRestaurant.add(template.createEntityFromState(recommendation, Recommendation.class));
|
||||
ratedRestaurant.add(template.load(recommendation, Recommendation.class));
|
||||
}
|
||||
return ratedRestaurant;
|
||||
}
|
||||
|
||||
@@ -47,5 +47,4 @@ class SpringEndResult<R> implements EndResult<R> {
|
||||
public Iterator<R> iterator() {
|
||||
return result.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ import org.neo4j.rest.graphdb.util.ConvertedResult;
|
||||
import org.neo4j.rest.graphdb.util.ResultConverter;
|
||||
import org.springframework.data.neo4j.conversion.EndResult;
|
||||
import org.springframework.data.neo4j.conversion.Result;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
|
||||
class SpringRestResult<T> implements Result<T> {
|
||||
org.neo4j.rest.graphdb.util.QueryResult<T> queryResult;
|
||||
private MappingPolicy mappingPolicy;
|
||||
|
||||
SpringRestResult(org.neo4j.rest.graphdb.util.QueryResult<T> queryResult) {
|
||||
this.queryResult = queryResult;
|
||||
@@ -39,7 +41,7 @@ class SpringRestResult<T> implements Result<T> {
|
||||
ConvertedResult<R> result = queryResult.to(type, new ResultConverter<T, R>() {
|
||||
@Override
|
||||
public R convert(T value, Class<R> type) {
|
||||
return converter.convert(value,type);
|
||||
return converter.convert(value,type,mappingPolicy);
|
||||
}
|
||||
});
|
||||
return new SpringEndResult<R>(result);
|
||||
@@ -66,4 +68,10 @@ class SpringRestResult<T> implements Result<T> {
|
||||
public T single() {
|
||||
return (T) to(Object.class).single();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<T> with(MappingPolicy mappingPolicy) {
|
||||
this.mappingPolicy = mappingPolicy;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class TestServerPlugin extends ServerPlugin {
|
||||
@PluginTarget(Node.class)
|
||||
public Iterable<Node> allFriendsOf(@Source Node target) {
|
||||
context(target.getGraphDatabase());
|
||||
final Person person = template.createEntityFromState(target, Person.class);
|
||||
final Person person = template.load(target, Person.class);
|
||||
return new IterableWrapper<Node, Friendship>(person.getFriendships()) {
|
||||
@Override
|
||||
protected Node underlyingObjectToObject(Friendship friendship) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.neo4j.conversion;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Path;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.support.path.NodePath;
|
||||
import org.springframework.data.neo4j.support.path.RelationshipPath;
|
||||
|
||||
@@ -32,11 +33,14 @@ import java.util.Map;
|
||||
public class DefaultConverter<T,R> implements ResultConverter<T,R> {
|
||||
@SuppressWarnings("unchecked")
|
||||
public R convert(Object value, Class type) {
|
||||
return convert(value,type, null);
|
||||
}
|
||||
public R convert(Object value, Class type, MappingPolicy mappingPolicy) {
|
||||
if (value == null || type.isInstance(value)) return (R) value;
|
||||
Object singleValue = extractValue(value);
|
||||
if (singleValue == null || type.isInstance(singleValue)) return (R) singleValue;
|
||||
final Class<?> sourceType = singleValue.getClass();
|
||||
Object result = doConvert(singleValue, sourceType, type);
|
||||
Object result = doConvert(singleValue, sourceType, type,mappingPolicy);
|
||||
if (result == null)
|
||||
throw new RuntimeException("Cannot automatically convert " + sourceType + " to " + type + " please use a custom converter");
|
||||
return (R) result;
|
||||
@@ -58,7 +62,7 @@ public class DefaultConverter<T,R> implements ResultConverter<T,R> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object doConvert(Object value, Class<?> sourceType, Class type) {
|
||||
protected Object doConvert(Object value, Class<?> sourceType, Class type, MappingPolicy mappingPolicy) {
|
||||
if (Node.class.isAssignableFrom(type)) {
|
||||
return toNode(value, sourceType);
|
||||
}
|
||||
|
||||
@@ -24,5 +24,4 @@ public interface EndResult<R> extends Iterable<R> {
|
||||
R single();
|
||||
R singleOrNull();
|
||||
void handle(Handler<R> handler);
|
||||
|
||||
}
|
||||
|
||||
@@ -15,14 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.conversion;
|
||||
|
||||
import org.springframework.data.neo4j.annotation.ResultColumn;
|
||||
import org.springframework.data.neo4j.support.conversion.NoSuchColumnFoundException;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.support.conversion.QueryResultProxy;
|
||||
import org.springframework.data.neo4j.template.Neo4jOperations;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -33,35 +29,15 @@ public class QueryMapResulConverter<T> implements ResultConverter<Map<String, Ob
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T convert(Map<String, Object> value, Class<T> type, MappingPolicy mappingPolicy) {
|
||||
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{type}, new QueryResultProxy(value,mappingPolicy,template.getDefaultConverter()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T convert(Map<String, Object> value, Class<T> type) {
|
||||
final Map<String, Object> valueCopy = value;
|
||||
|
||||
|
||||
T resultProxy = (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{type}, new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
|
||||
ResultColumn column = method.getAnnotation(ResultColumn.class);
|
||||
TypeInformation<Object> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
||||
|
||||
String columnName = column.value();
|
||||
if(!valueCopy.containsKey( columnName )) {
|
||||
throw new NoSuchColumnFoundException( columnName );
|
||||
}
|
||||
|
||||
Object columnValue = valueCopy.get( columnName );
|
||||
|
||||
|
||||
Object result;
|
||||
if (returnType.isCollectionLike())
|
||||
result = template.convert((Iterable) columnValue).to(returnType.getActualType().getType());
|
||||
else
|
||||
result = template.convert(columnValue, returnType.getType());
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
});
|
||||
return resultProxy;
|
||||
return convert(value,type,null);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import org.neo4j.graphdb.index.IndexHits;
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.neo4j.helpers.collection.IteratorWrapper;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@@ -32,6 +33,7 @@ public class QueryResultBuilder<T> implements Result<T> {
|
||||
private final ResultConverter defaultConverter;
|
||||
private final boolean isClosableIterable;
|
||||
private boolean isClosed;
|
||||
private MappingPolicy mappingPolicy;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public QueryResultBuilder(Iterable<T> result) {
|
||||
@@ -74,7 +76,7 @@ public class QueryResultBuilder<T> implements Result<T> {
|
||||
public R single() {
|
||||
try {
|
||||
final T value = IteratorUtil.single(result);
|
||||
return resultConverter.convert(value, type);
|
||||
return convert(value);
|
||||
} finally {
|
||||
closeIfNeeded();
|
||||
}
|
||||
@@ -83,17 +85,21 @@ public class QueryResultBuilder<T> implements Result<T> {
|
||||
public R singleOrNull() {
|
||||
try {
|
||||
final T value = IteratorUtil.singleOrNull(result);
|
||||
return resultConverter.convert(value, type);
|
||||
return convert(value);
|
||||
} finally {
|
||||
closeIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
private R convert(T value) {
|
||||
return resultConverter.convert(value, type, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Handler<R> handler) {
|
||||
try {
|
||||
for (T value : result) {
|
||||
handler.handle(resultConverter.convert(value, type));
|
||||
handler.handle(convert(value));
|
||||
}
|
||||
} finally {
|
||||
closeIfNeeded();
|
||||
@@ -104,7 +110,7 @@ public class QueryResultBuilder<T> implements Result<T> {
|
||||
public Iterator<R> iterator() {
|
||||
return new IteratorWrapper<R, T>(result.iterator()) {
|
||||
protected R underlyingObjectToObject(T value) {
|
||||
return resultConverter.convert(value, type);
|
||||
return convert(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -138,4 +144,9 @@ public class QueryResultBuilder<T> implements Result<T> {
|
||||
public Iterator<T> iterator() {
|
||||
return result.iterator();
|
||||
}
|
||||
|
||||
public Result<T> with(MappingPolicy mappingPolicy) {
|
||||
this.mappingPolicy = mappingPolicy;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.data.neo4j.conversion;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 28.06.11
|
||||
@@ -23,4 +25,6 @@ package org.springframework.data.neo4j.conversion;
|
||||
public interface Result<T> extends EndResult<T> {
|
||||
<R> EndResult<R> to(Class<R> type);
|
||||
<R> EndResult<R> to(Class<R> type, ResultConverter<T, R> resultConverter);
|
||||
Result<T> with(MappingPolicy mappingPolicy);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,18 +16,26 @@
|
||||
|
||||
package org.springframework.data.neo4j.conversion;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 28.06.11
|
||||
*/
|
||||
public interface ResultConverter<T, R> {
|
||||
R convert(T value, Class<R> type);
|
||||
R convert(T value, Class<R> type, MappingPolicy mappingPolicy);
|
||||
|
||||
ResultConverter NO_OP_RESULT_CONVERTER = new ResultConverter() {
|
||||
public class ResultConverterAdapter<T,R> implements ResultConverter<T,R> {
|
||||
@Override
|
||||
public Object convert(Object value, Class type) {
|
||||
public R convert(T value, Class<R> type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R convert(T value, Class<R> type, MappingPolicy mappingPolicy) {
|
||||
return convert(value,type);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.data.neo4j.core;
|
||||
|
||||
import org.springframework.data.neo4j.fieldaccess.FieldAccessor;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
|
||||
@@ -34,35 +35,39 @@ public interface EntityState<STATE> {
|
||||
void setPersistentState(STATE state);
|
||||
|
||||
/**
|
||||
* @param field field of the entity class
|
||||
* @return a default value for the given field by its {@link FieldAccessor} or {@code null} if none is provided.
|
||||
*
|
||||
* @param property@return a default value for the given field by its {@link FieldAccessor} or {@code null} if none is provided.
|
||||
*/
|
||||
Object getDefaultImplementation(Field field);
|
||||
Object getDefaultValue(Neo4jPersistentProperty property);
|
||||
|
||||
/**
|
||||
* @param field
|
||||
* @return value of the field either from the state and/or the entity
|
||||
*/
|
||||
Object getValue(Field field);
|
||||
/**
|
||||
* @return value of the property either from the state and/or the entity
|
||||
*/
|
||||
Object getValue(Neo4jPersistentProperty property);
|
||||
|
||||
/**
|
||||
* @param field
|
||||
* @return true if the field can be written
|
||||
*/
|
||||
boolean isWritable(Field field);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param field
|
||||
* @param mappingPolicy
|
||||
* @return value of the field either from the state and/or the entity
|
||||
*/
|
||||
Object getValue(Field field, MappingPolicy mappingPolicy);
|
||||
/**
|
||||
* @return value of the property either from the state and/or the entity
|
||||
*/
|
||||
Object getValue(Neo4jPersistentProperty property, MappingPolicy mappingPolicy);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param property@return true if the field can be written
|
||||
*/
|
||||
boolean isWritable(Neo4jPersistentProperty property);
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param field
|
||||
* @param newVal
|
||||
* @param mappingPolicy
|
||||
* @return sets the value in the entity and/or the state
|
||||
*/
|
||||
Object setValue(Field field, Object newVal);
|
||||
Object setValue(Neo4jPersistentProperty property, Object newVal);
|
||||
Object setValue(Field field, Object newVal, MappingPolicy mappingPolicy);
|
||||
Object setValue(Neo4jPersistentProperty property, Object newVal, MappingPolicy mappingPolicy);
|
||||
|
||||
/**
|
||||
* callback for creating and initializing an initial state
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.neo4j.fieldaccess;
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -45,6 +46,11 @@ public abstract class AbstractNodeRelationshipFieldAccessor<STATE extends Proper
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
protected MappingPolicy updateMappingPolicy(MappingPolicy mappingPolicy) {
|
||||
if (mappingPolicy !=null) return mappingPolicy;
|
||||
return property.getMappingPolicy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWriteable(Object entity) {
|
||||
return true;
|
||||
@@ -94,15 +100,15 @@ public abstract class AbstractNodeRelationshipFieldAccessor<STATE extends Proper
|
||||
return newState;
|
||||
}
|
||||
|
||||
protected <T> ManagedFieldAccessorSet<T> createManagedSet(Object entity, Set<T> result) {
|
||||
return new ManagedFieldAccessorSet<T>(entity, result, property, template,this);
|
||||
protected <T> ManagedFieldAccessorSet<T> createManagedSet(Object entity, Set<T> result, MappingPolicy mappingPolicy) {
|
||||
return new ManagedFieldAccessorSet<T>(entity, result, property, template,this, mappingPolicy);
|
||||
}
|
||||
|
||||
protected Set<Object> createEntitySetFromRelationshipEndNodes(Object entity) {
|
||||
protected Set<Object> createEntitySetFromRelationshipEndNodes(Object entity, final MappingPolicy mappingPolicy) {
|
||||
final Iterable<TSTATE> nodes = getStatesFromEntity(entity);
|
||||
final Set<Object> result = new HashSet<Object>();
|
||||
for (final TSTATE otherNode : nodes) {
|
||||
Object target= template.createEntityFromState(otherNode, relatedType);
|
||||
Object target= template.createEntityFromState(otherNode, relatedType, mappingPolicy);
|
||||
result.add(target);
|
||||
}
|
||||
return result;
|
||||
@@ -125,7 +131,7 @@ public abstract class AbstractNodeRelationshipFieldAccessor<STATE extends Proper
|
||||
}
|
||||
}
|
||||
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Configurable;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
@@ -62,8 +63,8 @@ public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessor
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
super.setValue(entity, propertyConverter.serializePropertyValue(newVal,targetType));
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
super.setValue(entity, propertyConverter.serializePropertyValue(newVal,targetType), mappingPolicy);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.neo4j.fieldaccess;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.neo4j.core.EntityState;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
|
||||
@@ -33,7 +34,7 @@ import java.util.Map;
|
||||
*/
|
||||
public abstract class DefaultEntityState<STATE> implements EntityState<STATE> {
|
||||
protected final Object entity;
|
||||
protected final Class<? extends Object> type;
|
||||
protected final Class<?> type;
|
||||
private final Map<Neo4jPersistentProperty, FieldAccessor> fieldAccessors = new HashMap<Neo4jPersistentProperty, FieldAccessor>();
|
||||
private final Map<Neo4jPersistentProperty,List<FieldAccessListener>> fieldAccessorListeners = new HashMap<Neo4jPersistentProperty, List<FieldAccessListener>>();
|
||||
private STATE state;
|
||||
@@ -41,7 +42,7 @@ public abstract class DefaultEntityState<STATE> implements EntityState<STATE> {
|
||||
private final FieldAccessorFactoryProviders<Object> fieldAccessorFactoryProviders;
|
||||
protected final Neo4jPersistentEntity<?> persistentEntity;
|
||||
|
||||
public DefaultEntityState(final STATE underlyingState, final Object entity, final Class<? extends Object> type, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4jPersistentEntity<?> persistentEntity) {
|
||||
public DefaultEntityState(final STATE underlyingState, final Object entity, final Class<?> type, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4jPersistentEntity<?> persistentEntity) {
|
||||
this.state = underlyingState;
|
||||
this.entity = entity;
|
||||
this.type = type;
|
||||
@@ -83,43 +84,43 @@ public abstract class DefaultEntityState<STATE> implements EntityState<STATE> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable(Field field) {
|
||||
final FieldAccessor accessor = accessorFor(property(field));
|
||||
public boolean isWritable(Neo4jPersistentProperty property) {
|
||||
final FieldAccessor accessor = accessorFor(property);
|
||||
if (accessor == null) return true;
|
||||
return accessor.isWriteable(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Neo4jPersistentProperty property) {
|
||||
public Object getValue(final Neo4jPersistentProperty property, MappingPolicy mappingPolicy) {
|
||||
final FieldAccessor accessor = accessorFor(property);
|
||||
if (accessor == null) return null;
|
||||
else return accessor.getValue(entity);
|
||||
else return accessor.getValue(entity, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Field field) {
|
||||
return getValue(property(field));
|
||||
public Object getValue(final Field field, MappingPolicy mappingPolicy) {
|
||||
return getValue(property(field), mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Field field, final Object newVal) {
|
||||
return setValue(property(field),newVal);
|
||||
public Object setValue(final Field field, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
return setValue(property(field),newVal, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Neo4jPersistentProperty property, final Object newVal) {
|
||||
public Object setValue(final Neo4jPersistentProperty property, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
final FieldAccessor accessor = accessorFor(property);
|
||||
final Object result=accessor!=null ? accessor.setValue(entity, newVal) : newVal;
|
||||
final Object result=accessor!=null ? accessor.setValue(entity, newVal, mappingPolicy) : newVal;
|
||||
notifyListeners(property, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation(Field field) {
|
||||
final FieldAccessor accessor = accessorFor(property(field));
|
||||
public Object getDefaultValue(Neo4jPersistentProperty property) {
|
||||
final FieldAccessor accessor = accessorFor(property);
|
||||
if (accessor == null) return null;
|
||||
else return accessor.getDefaultImplementation();
|
||||
else return accessor.getDefaultValue();
|
||||
}
|
||||
|
||||
protected Neo4jPersistentProperty property(Field field) {
|
||||
@@ -140,6 +141,6 @@ public abstract class DefaultEntityState<STATE> implements EntityState<STATE> {
|
||||
protected Object getIdFromEntity() {
|
||||
final Neo4jPersistentProperty idProperty = fieldAccessorFactoryProviders.getIdProperty();
|
||||
if (idProperty==null) return null;
|
||||
return idProperty.getValue(entity);
|
||||
return idProperty.getValue(entity, idProperty.getMappingPolicy());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.neo4j.graphdb.Transaction;
|
||||
import org.springframework.data.neo4j.core.EntityState;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
@@ -37,7 +38,7 @@ import static org.springframework.data.neo4j.support.DoReturn.unwrap;
|
||||
* @since 15.09.2010
|
||||
*/
|
||||
public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
private final Map<Field, ExistingValue> dirty = new HashMap<Field, ExistingValue>();
|
||||
private final Map<Neo4jPersistentProperty, ExistingValue> dirty = new HashMap<Neo4jPersistentProperty, ExistingValue>();
|
||||
protected final EntityState<STATE> delegate;
|
||||
private final static Log log = LogFactory.getLog(DetachedEntityState.class);
|
||||
private Neo4jTemplate template;
|
||||
@@ -50,8 +51,8 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable(final Field field) {
|
||||
return delegate.isWritable(field);
|
||||
public boolean isWritable(Neo4jPersistentProperty property) {
|
||||
return delegate.isWritable(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,37 +76,33 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Neo4jPersistentProperty property) {
|
||||
return getValue(property.getField());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Field field) {
|
||||
public Object getValue(Neo4jPersistentProperty property, MappingPolicy mappingPolicy) {
|
||||
mappingPolicy = mappingPolicy == null ? property.getMappingPolicy() : mappingPolicy;
|
||||
if (isDetached()) {
|
||||
if (template.getPersistentState(getEntity())==null || isDirty(field)) {
|
||||
if (log.isDebugEnabled()) log.debug("Outside of transaction, GET value from field " + field);
|
||||
Object entityValue = getValueFromEntity(field);
|
||||
if (template.getPersistentState(getEntity())==null || isDirty(property)) {
|
||||
if (log.isDebugEnabled()) log.debug("Outside of transaction, GET value from field " + property);
|
||||
Object entityValue = getValueFromEntity(property, MappingPolicy.MAP_FIELD_DIRECT_POLICY);
|
||||
if (entityValue != null) {
|
||||
return entityValue;
|
||||
}
|
||||
|
||||
Object defaultValue = getDefaultImplementation(field);
|
||||
|
||||
Object defaultValue = getDefaultValue(property);
|
||||
if (defaultValue != null) {
|
||||
final Object entity = getEntity();
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
field.set(entity, defaultValue);
|
||||
addDirty(field, defaultValue, false);
|
||||
} catch(IllegalAccessException e) {
|
||||
throw new RuntimeException("Error setting default value for field " + field + " in " + entity.getClass(), e);
|
||||
}
|
||||
property.setValue(entity, defaultValue);
|
||||
addDirty(property, defaultValue, false);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
} else {
|
||||
// flushDirty();
|
||||
}
|
||||
return delegate.getValue(field);
|
||||
return delegate.getValue(property, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Field field, MappingPolicy mappingPolicy) {
|
||||
return getValue(property(field), mappingPolicy);
|
||||
}
|
||||
|
||||
protected boolean isDetached() {
|
||||
@@ -135,8 +132,8 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Object setValue(final Field field, final Object newVal) {
|
||||
return setValue(property(field),newVal);
|
||||
public Object setValue(final Field field, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
return setValue(property(field),newVal, mappingPolicy);
|
||||
}
|
||||
|
||||
private Neo4jPersistentProperty property(Field field) {
|
||||
@@ -144,25 +141,24 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Neo4jPersistentProperty property, final Object newVal) {
|
||||
public Object setValue(final Neo4jPersistentProperty property, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
if (isDetached()) {
|
||||
final Field field = property.getField();
|
||||
if (!isDirty(field) && isWritable(field)) {
|
||||
if (!isDirty(property) && isWritable(property)) {
|
||||
if (hasPersistentState()) {
|
||||
addDirty(field, unwrap(delegate.getValue(field)), true);
|
||||
addDirty(property, unwrap(delegate.getValue(property, MappingPolicy.MAP_FIELD_DIRECT_POLICY)), true);
|
||||
}
|
||||
else {
|
||||
addDirty(field, newVal, false);
|
||||
addDirty(property, newVal, false);
|
||||
}
|
||||
}
|
||||
return newVal;
|
||||
}
|
||||
// flushDirty();
|
||||
return delegate.setValue(property, newVal);
|
||||
return delegate.setValue(property, newVal, mappingPolicy);
|
||||
}
|
||||
@Override
|
||||
public Object getDefaultImplementation(Field field) {
|
||||
return delegate.getDefaultImplementation(field);
|
||||
public Object getDefaultValue(Neo4jPersistentProperty property) {
|
||||
return delegate.getDefaultValue(property);
|
||||
}
|
||||
private Object getDefaultValue(final Class<?> type) {
|
||||
if (type.isPrimitive()) {
|
||||
@@ -193,15 +189,16 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
}
|
||||
|
||||
if (isDirty()) {
|
||||
final Map<Field, ExistingValue> dirtyCopy = new HashMap<Field, ExistingValue>(dirty);
|
||||
final Map<Neo4jPersistentProperty, ExistingValue> dirtyCopy = new HashMap<Neo4jPersistentProperty, ExistingValue>(dirty);
|
||||
clearDirty();
|
||||
for (final Map.Entry<Field, ExistingValue> entry : dirtyCopy.entrySet()) {
|
||||
final Field field = entry.getKey();
|
||||
Object valueFromEntity = getValueFromEntity(field);
|
||||
for (final Map.Entry<Neo4jPersistentProperty, ExistingValue> entry : dirtyCopy.entrySet()) {
|
||||
final Neo4jPersistentProperty property = entry.getKey();
|
||||
final MappingPolicy mappingPolicy = property.getMappingPolicy();
|
||||
Object valueFromEntity = getValueFromEntity(property, MappingPolicy.MAP_FIELD_DIRECT_POLICY);
|
||||
cascadePersist(valueFromEntity);
|
||||
if (log.isDebugEnabled()) log.debug("Flushing dirty Entity new node " + entity + " field " + field+ " with value "+ valueFromEntity);
|
||||
checkConcurrentModification(entity, entry, field);
|
||||
delegate.setValue(field, valueFromEntity);
|
||||
if (log.isDebugEnabled()) log.debug("Flushing dirty Entity new node " + entity + " field " + property+ " with value "+ valueFromEntity);
|
||||
checkConcurrentModification(entity, entry, property, mappingPolicy);
|
||||
delegate.setValue(property, valueFromEntity, mappingPolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,22 +224,17 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
}
|
||||
|
||||
|
||||
private Object getValueFromEntity(final Field field) {
|
||||
private Object getValueFromEntity(final Neo4jPersistentProperty property, MappingPolicy mappingPolicy) {
|
||||
final Object entity = getEntity();
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
return field.get(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Error accessing field " + field + " in " + entity.getClass(), e);
|
||||
}
|
||||
return property.getValue(entity, mappingPolicy);
|
||||
}
|
||||
|
||||
private void checkConcurrentModification(final Object entity, final Map.Entry<Field, ExistingValue> entry, final Field field) {
|
||||
private void checkConcurrentModification(final Object entity, final Map.Entry<Neo4jPersistentProperty, ExistingValue> entry, final Neo4jPersistentProperty property, final MappingPolicy mappingPolicy) {
|
||||
final ExistingValue previousValue = entry.getValue();
|
||||
if (previousValue.mustCheckConcurrentModification()) {
|
||||
final Object nodeValue = unwrap(delegate.getValue(field));
|
||||
final Object nodeValue = unwrap(delegate.getValue(property, mappingPolicy));
|
||||
if (!ObjectUtils.nullSafeEquals(nodeValue, previousValue.value)) {
|
||||
throw new ConcurrentModificationException("Node " + entity + " field " + field + " changed in between previous " + previousValue + " current " + nodeValue); // todo or just overwrite
|
||||
throw new ConcurrentModificationException("Node " + entity + " field " + property + " changed in between previous " + previousValue + " current " + nodeValue); // todo or just overwrite
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,8 +243,8 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
return !this.dirty.isEmpty();
|
||||
}
|
||||
|
||||
private boolean isDirty(final Field f) {
|
||||
return this.dirty.containsKey(f);
|
||||
private boolean isDirty(final Neo4jPersistentProperty property) {
|
||||
return this.dirty.containsKey(property);
|
||||
}
|
||||
|
||||
private void clearDirty() {
|
||||
@@ -263,8 +255,8 @@ public class DetachedEntityState<STATE> implements EntityState<STATE> {
|
||||
this.dirty.remove(f);
|
||||
}
|
||||
|
||||
private void addDirty(final Field f, final Object previousValue, boolean fromGraph) {
|
||||
this.dirty.put(f, new ExistingValue(previousValue,fromGraph));
|
||||
private void addDirty(final Neo4jPersistentProperty property, final Object previousValue, boolean fromGraph) {
|
||||
this.dirty.put(property, new ExistingValue(previousValue,fromGraph));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.DoReturn;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This accessor factory creates {@link DynamicPropertiesFieldAccessor}s for @NodeEntity properties of type
|
||||
* {@link DynamicProperties}.
|
||||
@@ -60,7 +60,7 @@ public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFacto
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
final PropertyContainer propertyContainer = template.getPersistentState(entity);
|
||||
PrefixedDynamicProperties dynamicProperties;
|
||||
if (newVal instanceof ManagedPrefixedDynamicProperties) {
|
||||
@@ -106,9 +106,9 @@ public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFacto
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
PropertyContainer element = template.getPersistentState(entity);
|
||||
ManagedPrefixedDynamicProperties props = ManagedPrefixedDynamicProperties.create(propertyNamePrefix, field, entity, template,this);
|
||||
ManagedPrefixedDynamicProperties props = ManagedPrefixedDynamicProperties.create(propertyNamePrefix, field, entity, template,this, field.getMappingPolicy());
|
||||
for (String key : element.getPropertyKeys()) {
|
||||
props.setPropertyIfPrefixed(key, element.getProperty(key));
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class DynamicPropertiesFieldAccessorFactory implements FieldAccessorFacto
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return new DynamicPropertiesContainer();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
/**
|
||||
* interface for field accessors, encapsulates reading and writing from fields and write support information about the field.
|
||||
* It is used by the {@link org.springframework.data.neo4j.core.EntityState}.
|
||||
@@ -29,23 +31,27 @@ public interface FieldAccessor {
|
||||
* when the value of the field is get.
|
||||
* @return a default implementation for a field or {@code null} if none is provided.
|
||||
*/
|
||||
Object getDefaultImplementation();
|
||||
Object getDefaultValue();
|
||||
|
||||
/**
|
||||
* handles field write modification.
|
||||
*
|
||||
* @param entity
|
||||
* @param newVal
|
||||
* @param mappingPolicy
|
||||
* @return the written value or a DoReturn wrapper with the written value or null.
|
||||
* DoReturn indicates that the aspect should not proceed to the original field access but instead return immediately.
|
||||
*/
|
||||
Object setValue(Object entity, Object newVal);
|
||||
Object setValue(Object entity, Object newVal, MappingPolicy mappingPolicy);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param entity
|
||||
* @param mappingPolicy
|
||||
* @return the value or a DoReturn wrapper with the value for the field.
|
||||
* DoReturn indicates that the aspect should not proceed to the original field access but instead return immediately.
|
||||
*/
|
||||
Object getValue(Object entity);
|
||||
Object getValue(Object entity, MappingPolicy mappingPolicy);
|
||||
|
||||
/**
|
||||
* @param entity
|
||||
|
||||
@@ -35,7 +35,7 @@ public class GraphBackedEntityIterableWrapper<STATE extends PropertyContainer, E
|
||||
|
||||
@Override
|
||||
protected ENTITY underlyingObjectToObject(STATE s) {
|
||||
return template.createEntityFromState(s, targetType);
|
||||
return template.createEntityFromState(s, targetType, template.getMappingPolicy(targetType));
|
||||
}
|
||||
|
||||
public static <S extends PropertyContainer, E> GraphBackedEntityIterableWrapper<S, E> create(
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
@@ -62,12 +63,12 @@ public class IdFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
return newVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
final PropertyContainer state = template.getPersistentState(entity);
|
||||
if (state instanceof Node) {
|
||||
return doReturn(((Node)state).getId());
|
||||
@@ -79,7 +80,7 @@ public class IdFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import org.springframework.data.neo4j.core.EntityState;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.ManagedEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.DoReturn;
|
||||
@@ -36,15 +37,17 @@ public class ManagedFieldAccessorSet<T> extends AbstractSet<T> {
|
||||
private final Neo4jPersistentProperty property;
|
||||
private final Neo4jTemplate ctx;
|
||||
private final FieldAccessor fieldAccessor;
|
||||
private final MappingPolicy mappingPolicy;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ManagedFieldAccessorSet(final Object entity, final Object newVal, final Neo4jPersistentProperty property, Neo4jTemplate ctx, FieldAccessor fieldAccessor) {
|
||||
public ManagedFieldAccessorSet(final Object entity, final Object newVal, final Neo4jPersistentProperty property, Neo4jTemplate ctx, FieldAccessor fieldAccessor, final MappingPolicy mappingPolicy) {
|
||||
this.entity = entity;
|
||||
this.property = property;
|
||||
this.ctx = ctx;
|
||||
this.fieldAccessor = fieldAccessor;
|
||||
delegate = (Set<T>) newVal;
|
||||
}
|
||||
this.mappingPolicy = mappingPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
@@ -77,14 +80,14 @@ public class ManagedFieldAccessorSet<T> extends AbstractSet<T> {
|
||||
}
|
||||
|
||||
private Object updateValueWithState(EntityState entityState) {
|
||||
final Object newValue = entityState.setValue(property, delegate);
|
||||
final Object newValue = entityState.setValue(property, delegate, mappingPolicy);
|
||||
if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue);
|
||||
property.setValue(entity, newValue);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
private Object updateValue() {
|
||||
final Object newValue = fieldAccessor.setValue(entity,delegate);
|
||||
final Object newValue = fieldAccessor.setValue(entity,delegate, mappingPolicy);
|
||||
if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue);
|
||||
property.setValue(entity, newValue);
|
||||
return newValue;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import org.springframework.data.neo4j.core.EntityState;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.ManagedEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.DoReturn;
|
||||
@@ -33,22 +34,24 @@ public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties
|
||||
private final FieldAccessor fieldAccessor;
|
||||
private final Neo4jPersistentProperty property;
|
||||
private boolean isNode;
|
||||
private MappingPolicy mappingPolicy;
|
||||
|
||||
public ManagedPrefixedDynamicProperties(String prefix, final Neo4jPersistentProperty property, final Object entity, Neo4jTemplate template, FieldAccessor fieldAccessor) {
|
||||
this(prefix,10,property,entity, template,fieldAccessor);
|
||||
public ManagedPrefixedDynamicProperties(String prefix, final Neo4jPersistentProperty property, final Object entity, Neo4jTemplate template, FieldAccessor fieldAccessor, final MappingPolicy mappingPolicy) {
|
||||
this(prefix,10,property,entity, template,fieldAccessor, mappingPolicy);
|
||||
}
|
||||
|
||||
public ManagedPrefixedDynamicProperties(String prefix, int initialCapacity, final Neo4jPersistentProperty property, final Object entity, Neo4jTemplate template, FieldAccessor fieldAccessor) {
|
||||
public ManagedPrefixedDynamicProperties(String prefix, int initialCapacity, final Neo4jPersistentProperty property, final Object entity, Neo4jTemplate template, FieldAccessor fieldAccessor, final MappingPolicy mappingPolicy) {
|
||||
super(prefix, initialCapacity);
|
||||
this.property = property;
|
||||
this.entity = entity;
|
||||
this.template = template;
|
||||
this.fieldAccessor = fieldAccessor;
|
||||
this.isNode = property.getOwner().isNodeEntity();
|
||||
this.mappingPolicy = mappingPolicy;
|
||||
}
|
||||
|
||||
public static ManagedPrefixedDynamicProperties create(String prefix, final Neo4jPersistentProperty property, final Object entity, Neo4jTemplate template, FieldAccessor fieldAccessor) {
|
||||
return new ManagedPrefixedDynamicProperties(prefix, property, entity, template,fieldAccessor);
|
||||
public static ManagedPrefixedDynamicProperties create(String prefix, final Neo4jPersistentProperty property, final Object entity, Neo4jTemplate template, FieldAccessor fieldAccessor, final MappingPolicy mappingPolicy) {
|
||||
return new ManagedPrefixedDynamicProperties(prefix, property, entity, template,fieldAccessor, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,13 +75,13 @@ public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties
|
||||
|
||||
@Override
|
||||
public DynamicProperties createFrom(Map<String, Object> map) {
|
||||
DynamicProperties d = new ManagedPrefixedDynamicProperties(prefix, map.size(), property, entity, template,fieldAccessor);
|
||||
DynamicProperties d = new ManagedPrefixedDynamicProperties(prefix, map.size(), property, entity, template,fieldAccessor, property.getMappingPolicy());
|
||||
d.setPropertiesFrom(map);
|
||||
return d;
|
||||
}
|
||||
|
||||
private Object updateValue() {
|
||||
final Object newValue = fieldAccessor.setValue(entity, this);
|
||||
final Object newValue = fieldAccessor.setValue(entity, this, mappingPolicy);
|
||||
if (newValue instanceof DoReturn)
|
||||
return DoReturn.unwrap(newValue);
|
||||
property.setValue(entity, newValue);
|
||||
@@ -94,7 +97,7 @@ public class ManagedPrefixedDynamicProperties extends PrefixedDynamicProperties
|
||||
}
|
||||
|
||||
private Object updateValueWithState(EntityState entityState) {
|
||||
final Object newValue = entityState.setValue(property, this);
|
||||
final Object newValue = entityState.setValue(property, this, mappingPolicy);
|
||||
if (newValue instanceof DoReturn) return DoReturn.unwrap(newValue);
|
||||
property.setValue(entity, newValue);
|
||||
return newValue;
|
||||
|
||||
@@ -22,10 +22,7 @@ import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipInfo;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipProperties;
|
||||
import org.springframework.data.neo4j.mapping.*;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -65,7 +62,7 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
if (!isEditableSet) throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field.");
|
||||
final Node startNode = checkUnderlyingState(entity);
|
||||
if (newVal == null) {
|
||||
@@ -75,7 +72,7 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess
|
||||
removeMissingRelationships(startNode, targetNodes.keySet());
|
||||
//createAddedRelationships(startNode, targetNodes.keySet());
|
||||
persistEntities(targetNodes);
|
||||
return createManagedSet(entity, (Set<?>) newVal);
|
||||
return createManagedSet(entity, (Set<?>) newVal, updateMappingPolicy(mappingPolicy));
|
||||
}
|
||||
|
||||
private void persistEntities(Map<Node, Object> targetNodes) {
|
||||
@@ -95,11 +92,14 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess
|
||||
}
|
||||
Neo4jPersistentEntity relationshipPEntity = property.getRelationshipInfo().getTargetEntity();
|
||||
final RelationshipProperties relationshipProperties = relationshipPEntity.getRelationshipProperties();
|
||||
final Node endNode = getState(relationshipProperties.getEndeNodeProperty().getValue(entry));
|
||||
final Neo4jPersistentProperty endNodeProperty = relationshipProperties.getEndNodeProperty();
|
||||
final Object endNodeEntity = endNodeProperty.getValue(entry, endNodeProperty.getMappingPolicy());
|
||||
final Node endNode = getState(endNodeEntity);
|
||||
if (!endNode.equals(startNode)) {
|
||||
targetNodes.put(endNode, entry);
|
||||
} else {
|
||||
final Node otherNode = getState(relationshipProperties.getStartNodeProperty().getValue(entry));
|
||||
final Neo4jPersistentProperty startNodeProperty = relationshipProperties.getStartNodeProperty();
|
||||
final Node otherNode = getState(startNodeProperty.getValue(entry, startNodeProperty.getMappingPolicy()));
|
||||
targetNodes.put(otherNode, entry);
|
||||
}
|
||||
}
|
||||
@@ -112,11 +112,11 @@ public class OneToNRelationshipEntityFieldAccessorFactory implements FieldAccess
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
checkUnderlyingState(entity);
|
||||
final GraphBackedEntityIterableWrapper<Relationship, ?> result = iterableFrom(entity);
|
||||
if (isEditableSet) {
|
||||
@SuppressWarnings("unchecked") final ManagedFieldAccessorSet managedSet = createManagedSet(entity, IteratorUtil.addToCollection(result, new HashSet()));
|
||||
@SuppressWarnings("unchecked") final ManagedFieldAccessorSet managedSet = createManagedSet(entity, IteratorUtil.addToCollection(result, new HashSet()), updateMappingPolicy(mappingPolicy));
|
||||
return doReturn(managedSet);
|
||||
}
|
||||
return doReturn(result);
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.data.neo4j.fieldaccess;
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipInfo;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
@@ -54,7 +55,7 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
super(elementClass, template, direction, type,property);
|
||||
}
|
||||
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
final Node node = checkUnderlyingState(entity);
|
||||
if (newVal == null) {
|
||||
/* null should not remove existing relationships but leave them alone
|
||||
@@ -65,18 +66,19 @@ public class OneToNRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
final Set<Node> targetNodes = createSetOfTargetNodes(newVal);
|
||||
removeMissingRelationships(node, targetNodes);
|
||||
createAddedRelationships(node, targetNodes);
|
||||
return createManagedSet(entity, (Set<?>) newVal);
|
||||
return createManagedSet(entity, (Set<?>) newVal, updateMappingPolicy(mappingPolicy));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
checkUnderlyingState(entity);
|
||||
final Set<?> result = createEntitySetFromRelationshipEndNodes(entity);
|
||||
return doReturn(createManagedSet(entity, result));
|
||||
final MappingPolicy currentPolicy = updateMappingPolicy(mappingPolicy);
|
||||
final Set<?> result = createEntitySetFromRelationshipEndNodes(entity, currentPolicy);
|
||||
return doReturn(createManagedSet(entity, result, currentPolicy));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return new HashSet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
@@ -64,7 +65,7 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
final PropertyContainer propertyContainer = template.getPersistentState(entity);
|
||||
if (newVal==null) {
|
||||
propertyContainer.removeProperty(propertyName);
|
||||
@@ -75,7 +76,7 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object getValue(final Object entity) {
|
||||
public final Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
return doReturn(doGetValue(entity));
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,13 @@ import org.neo4j.graphdb.Node;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.neo4j.annotation.Query;
|
||||
|
||||
import org.springframework.data.neo4j.annotation.QueryType;
|
||||
import org.springframework.data.neo4j.conversion.Result;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
import org.springframework.data.neo4j.support.query.QueryEngine;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -58,6 +63,7 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory {
|
||||
private Class<?> target;
|
||||
protected String[] annotationParams;
|
||||
private boolean iterableResult;
|
||||
private final QueryEngine<Object> queryEngine;
|
||||
|
||||
public QueryFieldAccessor(final Neo4jPersistentProperty property, Neo4jTemplate template) {
|
||||
this.property = property;
|
||||
@@ -70,6 +76,7 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory {
|
||||
this.query = query.value();
|
||||
this.iterableResult = Iterable.class.isAssignableFrom(property.getType());
|
||||
this.target = resolveTarget(query,property);
|
||||
queryEngine = this.template.queryEngineFor(QueryType.Cypher);
|
||||
}
|
||||
|
||||
private Class<?> resolveTarget(Query query, Neo4jPersistentProperty property) {
|
||||
@@ -83,17 +90,27 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot set readonly query field " + property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
return doReturn(executeQuery(entity, this.query, createPlaceholderParams(entity)));
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
return doReturn(executeQuery(entity, this.query, createPlaceholderParams(entity),mappingPolicy));
|
||||
}
|
||||
|
||||
private Object executeQuery(Object entity, String queryString, Map<String, Object> params) {
|
||||
return template.query(queryString, params, property.getTypeInformation());
|
||||
private Object executeQuery(Object entity, String queryString, Map<String, Object> params, MappingPolicy mappingPolicy) {
|
||||
final TypeInformation<?> typeInformation = property.getTypeInformation();
|
||||
final TypeInformation<?> actualType = typeInformation.getActualType();
|
||||
final Class<?> targetType = actualType.getType();
|
||||
final Result<Object> result = queryEngine.query(queryString, params).with(mappingPolicy);
|
||||
if (actualType.isMap()) {
|
||||
return result;
|
||||
}
|
||||
if (typeInformation.isCollectionLike()) {
|
||||
return result.to(targetType);
|
||||
}
|
||||
return result.to(targetType).single();
|
||||
}
|
||||
|
||||
private Map<String, Object> createPlaceholderParams(Object entity) {
|
||||
@@ -108,7 +125,7 @@ public class QueryFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipInfo;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
@@ -54,7 +55,7 @@ public class ReadOnlyOneToNRelationshipFieldAccessorFactory extends NodeRelation
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot set read-only relationship entity field.");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.data.neo4j.annotation.EndNode;
|
||||
import org.springframework.data.neo4j.annotation.StartNode;
|
||||
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
@@ -87,18 +88,18 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot change start or end node of existing relationship.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
final Relationship relationship = template.getPersistentState(entity);
|
||||
final Node node = getNode(relationship);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
final Object result = template.createEntityFromState(node, (Class<?>) property.getType());
|
||||
final Object result = template.createEntityFromState(node, (Class<?>) property.getType(), mappingPolicy);
|
||||
return doReturn(result);
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ public class RelationshipNodeFieldAccessorFactory implements FieldAccessorFactor
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.RelationshipType;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipInfo;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
@@ -52,7 +53,7 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
final Node node= checkUnderlyingState(entity);
|
||||
if (newVal == null) {
|
||||
removeMissingRelationships(node, Collections.<Node>emptySet());
|
||||
@@ -65,9 +66,9 @@ public class SingleRelationshipFieldAccessorFactory extends NodeRelationshipFiel
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
checkUnderlyingState(entity);
|
||||
final Set<Object> result = createEntitySetFromRelationshipEndNodes(entity);
|
||||
final Set<Object> result = createEntitySetFromRelationshipEndNodes(entity, updateMappingPolicy(mappingPolicy));
|
||||
final Object singleEntity = result.isEmpty() ? null : result.iterator().next();
|
||||
return doReturn(singleEntity);
|
||||
}
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
package org.springframework.data.neo4j.fieldaccess;
|
||||
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
|
||||
public class TransientFieldAccessorFactory implements FieldAccessorFactory {
|
||||
@Override
|
||||
public boolean accept(final Neo4jPersistentProperty property) {
|
||||
return property.isTransient();
|
||||
return property.isReallyTransient();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,7 +42,7 @@ public class TransientFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
return newVal;
|
||||
}
|
||||
|
||||
@@ -53,12 +52,12 @@ public class TransientFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.data.neo4j.annotation.GraphTraversal;
|
||||
import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder;
|
||||
|
||||
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
@@ -97,12 +98,12 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
public Object setValue(final Object entity, final Object newVal, MappingPolicy mappingPolicy) {
|
||||
throw new InvalidDataAccessApiUsageException("Cannot set readonly traversal description field " + property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, MappingPolicy mappingPolicy) {
|
||||
final TraversalDescription traversalDescription = fieldTraversalDescriptionBuilder.build(entity, property,params);
|
||||
return doReturn(template.traverse(entity, target, traversalDescription));
|
||||
}
|
||||
@@ -120,7 +121,7 @@ public class TraversalFieldAccessorFactory implements FieldAccessorFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDefaultImplementation() {
|
||||
public Object getDefaultValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,6 @@ public interface EntityInstantiator<STATE> {
|
||||
* code to instantiate entities without invoking a constructor.
|
||||
*/
|
||||
|
||||
<T> T createEntityFromState(STATE s, Class<T> c);
|
||||
<T> T createEntityFromState(STATE s, Class<T> c, final MappingPolicy mappingPolicy);
|
||||
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@ import org.neo4j.graphdb.PropertyContainer;
|
||||
public interface EntityPersister {
|
||||
|
||||
<T> T projectTo(Object entity, Class<T> targetType);
|
||||
<S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type);
|
||||
<S extends PropertyContainer, T> T createEntityFromStoredType(S state);
|
||||
<T> T projectTo(Object entity, Class<T> targetType, MappingPolicy mappingPolicy);
|
||||
<S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type, MappingPolicy mappingPolicy);
|
||||
<S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy);
|
||||
boolean isNodeEntity(Class<?> targetType);
|
||||
boolean isRelationshipEntity(Class<?> targetType);
|
||||
MappingPolicy getMappingPolicy(Class<?> targetType);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2011 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.neo4j.mapping;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 09.11.11
|
||||
*/
|
||||
public interface MappingPolicy {
|
||||
|
||||
enum Option {
|
||||
FIELD_DIRECT, SHOULD_LOAD
|
||||
}
|
||||
boolean accessField();
|
||||
boolean shouldLoad();
|
||||
MappingPolicy combineWith(MappingPolicy mappingPolicy);
|
||||
|
||||
public class DefaultMappingPolicy implements MappingPolicy {
|
||||
private Set<Option> options;
|
||||
|
||||
public DefaultMappingPolicy(Option... options) {
|
||||
this(asList(options));
|
||||
}
|
||||
|
||||
public DefaultMappingPolicy(final Collection<Option> options) {
|
||||
this.options = options.isEmpty() ? EnumSet.noneOf(Option.class) : EnumSet.copyOf(options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accessField() {
|
||||
return options.contains(Option.FIELD_DIRECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldLoad() {
|
||||
return options.contains(Option.SHOULD_LOAD);
|
||||
}
|
||||
|
||||
public MappingPolicy with(Option...options) {
|
||||
return with(asList(options));
|
||||
}
|
||||
|
||||
private MappingPolicy with(Collection<Option> optionsList) {
|
||||
Collection<Option> combined =new HashSet<Option>(optionsList);
|
||||
combined.addAll(this.options);
|
||||
combined.remove(null);
|
||||
return new DefaultMappingPolicy(combined);
|
||||
}
|
||||
|
||||
public MappingPolicy withOut(Option...options) {
|
||||
Collection<Option> combined =new HashSet<Option>(this.options);
|
||||
combined.removeAll(asList(options));
|
||||
return new DefaultMappingPolicy(combined);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingPolicy combineWith(MappingPolicy mappingPolicy) {
|
||||
if (mappingPolicy instanceof DefaultMappingPolicy) {
|
||||
return with(((DefaultMappingPolicy)mappingPolicy).options);
|
||||
}
|
||||
return with(mappingPolicy.accessField() ? Option.FIELD_DIRECT : null, mappingPolicy.shouldLoad() ? Option.SHOULD_LOAD : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Policy: "+options.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
DefaultMappingPolicy that = (DefaultMappingPolicy) o;
|
||||
|
||||
return options.equals(that.options);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return options.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public MappingPolicy LOAD_POLICY = new DefaultMappingPolicy(Option.SHOULD_LOAD);
|
||||
public MappingPolicy DEFAULT_POLICY = new DefaultMappingPolicy();
|
||||
public MappingPolicy MAP_FIELD_DIRECT_POLICY = new DefaultMappingPolicy(Option.FIELD_DIRECT);
|
||||
}
|
||||
@@ -15,17 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.neo4j.mapping;
|
||||
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.springframework.data.convert.EntityConverter;
|
||||
import org.springframework.data.convert.EntityReader;
|
||||
import org.springframework.data.convert.EntityWriter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 27.09.11
|
||||
*/
|
||||
public interface Neo4jEntityConverter<T, S extends PropertyContainer> extends EntityConverter<Neo4jPersistentEntity<?>, Neo4jPersistentProperty, T, S>, EntityWriter<T,S>,
|
||||
EntityReader<T, S> {
|
||||
public interface Neo4jEntityConverter<T, S extends PropertyContainer> {
|
||||
<R extends T> R read(Class<R> type, S source, MappingPolicy mappingPolicy);
|
||||
void write(T source, S sink, MappingPolicy mappingPolicy);
|
||||
MappingContext<? extends Neo4jPersistentEntity<?>, Neo4jPersistentProperty> getMappingContext();
|
||||
ConversionService getConversionService();
|
||||
|
||||
}
|
||||
|
||||
@@ -37,4 +37,6 @@ public interface Neo4jPersistentEntity<T> extends PersistentEntity<T, Neo4jPersi
|
||||
Object getPersistentId(Object entity);
|
||||
|
||||
RelationshipProperties getRelationshipProperties();
|
||||
|
||||
MappingPolicy getMappingPolicy();
|
||||
}
|
||||
|
||||
@@ -66,9 +66,15 @@ public interface Neo4jPersistentProperty extends PersistentProperty<Neo4jPersist
|
||||
|
||||
void setValue(Object entity, Object newValue);
|
||||
|
||||
Object getValue(final Object entity);
|
||||
Object getValue(final Object entity, final MappingPolicy mappingPolicy);
|
||||
|
||||
Neo4jPersistentEntity<?> getOwner();
|
||||
|
||||
String getIndexKey();
|
||||
|
||||
MappingPolicy getMappingPolicy();
|
||||
|
||||
boolean isReallyTransient();
|
||||
|
||||
Object getValueFromEntity(Object entity, MappingPolicy mappingPolicy);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.springframework.data.neo4j.annotation.RelatedTo;
|
||||
import org.springframework.data.neo4j.annotation.RelatedToVia;
|
||||
import org.springframework.data.neo4j.annotation.RelationshipEntity;
|
||||
import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext;
|
||||
import org.springframework.data.neo4j.support.mapping.Neo4jPersistentEntityImpl;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
@@ -57,7 +56,6 @@ public class RelationshipInfo {
|
||||
public RelationshipInfo(String type, Direction direction, TypeInformation<?> typeInformation, TypeInformation<?> concreteActualType, Neo4jMappingContext ctx) {
|
||||
this.type = type;
|
||||
this.direction = direction;
|
||||
this.targetEntity = targetEntity;
|
||||
isMultiple = typeInformation.isCollectionLike();
|
||||
targetType = concreteActualType!=null ? concreteActualType : typeInformation.getActualType();
|
||||
this.targetEntity = ctx.getPersistentEntity(targetType);
|
||||
@@ -75,18 +73,19 @@ public class RelationshipInfo {
|
||||
annotation.direction(),
|
||||
typeInformation,
|
||||
annotation.elementClass() != Object.class ? ClassTypeInformation.from(annotation.elementClass()) : null,
|
||||
ctx);
|
||||
ctx
|
||||
);
|
||||
}
|
||||
|
||||
public static RelationshipInfo fromField(Field field, RelatedToVia annotation, TypeInformation<?> typeInformation, Neo4jMappingContext ctx) {
|
||||
final TypeInformation<?> elementClass = elementClass(annotation, typeInformation);
|
||||
final Neo4jPersistentEntityImpl<?> targetEntity = ctx.getPersistentEntity(elementClass);
|
||||
return new RelationshipInfo(
|
||||
relationshipType(field,annotation,typeInformation),
|
||||
annotation.direction(),
|
||||
typeInformation,
|
||||
elementClass,
|
||||
ctx);
|
||||
ctx
|
||||
);
|
||||
}
|
||||
|
||||
private static String relationshipType(Field field, RelatedToVia annotation, TypeInformation<?> typeInformation) {
|
||||
|
||||
@@ -21,7 +21,7 @@ package org.springframework.data.neo4j.mapping;
|
||||
*/
|
||||
public interface RelationshipProperties {
|
||||
Neo4jPersistentProperty getStartNodeProperty();
|
||||
Neo4jPersistentProperty getEndeNodeProperty();
|
||||
Neo4jPersistentProperty getEndNodeProperty();
|
||||
Neo4jPersistentProperty getTypeProperty();
|
||||
|
||||
String getRelationshipType();
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
package org.springframework.data.neo4j.repository;
|
||||
|
||||
import org.apache.lucene.search.NumericRangeQuery;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.NotFoundException;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.index.IndexHits;
|
||||
import org.neo4j.graphdb.index.ReadableIndex;
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
@@ -184,7 +182,7 @@ public abstract class AbstractGraphRepository<S extends PropertyContainer, T> im
|
||||
}
|
||||
|
||||
protected T createEntity(S node) {
|
||||
return template.createEntityFromState(node, clazz);
|
||||
return template.createEntityFromState(node, clazz, template.getMappingPolicy(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,10 +29,12 @@ import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.neo4j.annotation.QueryType;
|
||||
import org.springframework.data.neo4j.conversion.QueryResultBuilder;
|
||||
import org.springframework.data.neo4j.conversion.Result;
|
||||
import org.springframework.data.neo4j.conversion.ResultConverter;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.core.TypeRepresentationStrategy;
|
||||
import org.springframework.data.neo4j.core.UncategorizedGraphStoreException;
|
||||
import org.springframework.data.neo4j.mapping.EntityPersister;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipResult;
|
||||
import org.springframework.data.neo4j.support.index.IndexType;
|
||||
import org.springframework.data.neo4j.support.mapping.EntityStateHandler;
|
||||
@@ -46,6 +48,7 @@ import org.springframework.data.neo4j.support.mapping.EntityCreatingClosableIter
|
||||
import org.springframework.data.neo4j.support.query.QueryEngine;
|
||||
import org.springframework.data.neo4j.template.GraphCallback;
|
||||
import org.springframework.data.neo4j.template.Neo4jOperations;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
@@ -134,15 +137,16 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
|
||||
@Override
|
||||
public <T> T findOne(long id, final Class<T> entityClass) {
|
||||
if (isNodeEntity(entityClass)) {
|
||||
final Neo4jPersistentEntityImpl<?> persistentEntity = getPersistentEntity(entityClass);
|
||||
if (persistentEntity.isNodeEntity()) {
|
||||
final Node node = getNode(id);
|
||||
if (node==null) return null;
|
||||
return infrastructure.getEntityPersister().createEntityFromState(node, entityClass);
|
||||
return infrastructure.getEntityPersister().createEntityFromState(node, entityClass, persistentEntity.getMappingPolicy());
|
||||
}
|
||||
if (isRelationshipEntity(entityClass)) {
|
||||
if (persistentEntity.isRelationshipEntity()) {
|
||||
final Relationship relationship = getRelationship(id);
|
||||
if (relationship==null) return null;
|
||||
return infrastructure.getEntityPersister().createEntityFromState(relationship, entityClass);
|
||||
return infrastructure.getEntityPersister().createEntityFromState(relationship, entityClass, persistentEntity.getMappingPolicy());
|
||||
}
|
||||
throw new IllegalArgumentException("provided entity type is not annotated with @NodeEntiy nor @RelationshipEntity");
|
||||
}
|
||||
@@ -161,15 +165,20 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state) {
|
||||
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy) {
|
||||
notNull(state,"node or relationship");
|
||||
return infrastructure.getEntityPersister().createEntityFromStoredType(state);
|
||||
return infrastructure.getEntityPersister().createEntityFromStoredType(state, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type) {
|
||||
public <S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type, MappingPolicy mappingPolicy) {
|
||||
notNull(state,"node or relationship",type,"entity class");
|
||||
return infrastructure.getEntityPersister().createEntityFromState(state, type);
|
||||
return infrastructure.getEntityPersister().createEntityFromState(state, type, mappingPolicy);
|
||||
}
|
||||
@Override
|
||||
public <S extends PropertyContainer, T> T load(S state, Class<T> type) {
|
||||
notNull(state,"node or relationship",type,"entity class");
|
||||
return infrastructure.getEntityPersister().createEntityFromState(state, type, getMappingPolicy(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,6 +186,11 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
notNull(entity,"entity",targetType,"new entity class");
|
||||
return infrastructure.getEntityPersister().projectTo(entity, targetType);
|
||||
}
|
||||
@Override
|
||||
public <T> T projectTo(Object entity, Class<T> targetType, MappingPolicy mappingPolicy) {
|
||||
notNull(entity,"entity",targetType,"new entity class");
|
||||
return infrastructure.getEntityPersister().projectTo(entity, targetType, mappingPolicy);
|
||||
}
|
||||
|
||||
/**
|
||||
* just sets the persistent state (i.e. Node or id) to the entity, doesn't copy any values/properties.
|
||||
@@ -186,12 +200,12 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
*/
|
||||
@Override
|
||||
public <S extends PropertyContainer> S getPersistentState(Object entity) {
|
||||
notNull(entity,"entity");
|
||||
notNull(entity, "entity");
|
||||
return infrastructure.getEntityPersister().getPersistentState(entity);
|
||||
}
|
||||
|
||||
public <S extends PropertyContainer, T> T setPersistentState(T entity, S state) {
|
||||
notNull(entity,"entity",state,"node or relationship");
|
||||
notNull(entity, "entity", state, "node or relationship");
|
||||
infrastructure.getEntityPersister().setPersistentState(entity, state);
|
||||
return entity;
|
||||
}
|
||||
@@ -274,7 +288,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T save(T entity) {
|
||||
return (T) infrastructure.getEntityPersister().persist(entity);
|
||||
return (T) infrastructure.getEntityPersister().persist(entity, getMappingPolicy(entity));
|
||||
}
|
||||
|
||||
public boolean isManaged(Object entity) {
|
||||
@@ -285,12 +299,12 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
final TypeInformation<?> actualType = typeInformation.getActualType();
|
||||
final Class<?> targetType = actualType.getType();
|
||||
if (actualType.isMap()) {
|
||||
return infrastructure.getCypherQueryExecutor().queryForList(statement, params);
|
||||
return queryEngineFor(QueryType.Cypher).query(statement, params);
|
||||
}
|
||||
if (typeInformation.isCollectionLike()) {
|
||||
return infrastructure.getCypherQueryExecutor().query(statement, targetType, params);
|
||||
return queryEngineFor(QueryType.Cypher).query(statement, params).to(targetType);
|
||||
}
|
||||
return infrastructure.getCypherQueryExecutor().queryForObject(statement, targetType, params);
|
||||
return queryEngineFor(QueryType.Cypher).query(statement, params).to(targetType).single();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -299,7 +313,8 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
final Relationship relationship = infrastructure.getEntityStateHandler().getRelationshipBetween(start, end, relationshipType);
|
||||
if (relationship == null) return null;
|
||||
if (Relationship.class.isAssignableFrom(relationshipEntityClass)) return (R)relationship;
|
||||
return infrastructure.getEntityPersister().createEntityFromState(relationship, relationshipEntityClass);
|
||||
final Neo4jPersistentEntityImpl<?> persistentEntity = getPersistentEntity(relationshipEntityClass);
|
||||
return infrastructure.getEntityPersister().createEntityFromState(relationship, relationshipEntityClass, persistentEntity.getMappingPolicy());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -321,7 +336,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
// TODO
|
||||
postEntityCreation(result.relationship, relationshipEntityClass);
|
||||
}
|
||||
return createEntityFromState(result.relationship, relationshipEntityClass);
|
||||
return createEntityFromState(result.relationship, relationshipEntityClass, getMappingPolicy(relationshipEntityClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -415,30 +430,46 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T fetch(T value) {
|
||||
final PropertyContainer state = getPersistentState(value);
|
||||
if (state != null) {
|
||||
return (T) infrastructure.getEntityPersister().createEntityFromState(state, value.getClass());
|
||||
final Class<?> targetType = value.getClass();
|
||||
final TypeInformation<?> targetTypeInformation = ClassTypeInformation.from(targetType);
|
||||
if (targetTypeInformation.isCollectionLike()) {
|
||||
return (T) infrastructure.getEntityPersister().createEntityFromState(state, targetType, getMappingPolicy(targetTypeInformation.getActualType().getType())); // todo handle collections
|
||||
}
|
||||
return (T) infrastructure.getEntityPersister().createEntityFromState(state, targetType, getMappingPolicy(targetType)); // todo handle collections
|
||||
}
|
||||
throw new MappingException("No state information available in "+ value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingPolicy getMappingPolicy(Class<?> targetType) {
|
||||
return getPersistentEntity(targetType).getMappingPolicy();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Result<T> convert(Iterable<T> iterable) {
|
||||
return new QueryResultBuilder<T>(iterable, infrastructure.getResultConverter());
|
||||
return new QueryResultBuilder<T>(iterable, getDefaultConverter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convert(Object value, Class<T> type) {
|
||||
return (T) infrastructure.getResultConverter().convert(value, type);
|
||||
return (T) getDefaultConverter().convert(value, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultConverter getDefaultConverter() {
|
||||
return infrastructure.getResultConverter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> QueryEngine<T> queryEngineFor(QueryType type) {
|
||||
return infrastructure.getGraphDatabase().queryEngineFor(type, infrastructure.getResultConverter());
|
||||
return infrastructure.getGraphDatabase().queryEngineFor(type, getDefaultConverter());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -502,7 +533,7 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
}
|
||||
|
||||
public <T extends PropertyContainer> Index<T> getIndex(String indexName, Class<?> indexedType) {
|
||||
return getIndexProvider().getIndex(indexedType,indexName);
|
||||
return getIndexProvider().getIndex(indexedType, indexName);
|
||||
}
|
||||
|
||||
public <T extends PropertyContainer> Index<T> getIndex(Class<?> indexedType, String propertyName) {
|
||||
@@ -572,4 +603,9 @@ public class Neo4jTemplate implements Neo4jOperations, EntityPersister {
|
||||
return getIndexProvider().getIndex(property, instanceType);
|
||||
}
|
||||
|
||||
|
||||
public MappingPolicy getMappingPolicy(Object entity) {
|
||||
ParameterCheck.notNull(entity,"entity");
|
||||
return getMappingPolicy(entity.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,13 @@ package org.springframework.data.neo4j.support.conversion;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.neo4j.annotation.MapResult;
|
||||
import org.springframework.data.neo4j.annotation.ResultColumn;
|
||||
import org.springframework.data.neo4j.conversion.DefaultConverter;
|
||||
import org.springframework.data.neo4j.conversion.QueryResultBuilder;
|
||||
import org.springframework.data.neo4j.core.EntityPath;
|
||||
import org.springframework.data.neo4j.mapping.EntityPersister;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.support.path.ConvertingEntityPath;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -48,17 +43,17 @@ public class EntityResultConverter<T, R> extends DefaultConverter<T, R> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected Object doConvert(Object value, Class<?> sourceType, Class targetType) {
|
||||
protected Object doConvert(Object value, Class<?> sourceType, Class targetType, MappingPolicy mappingPolicy) {
|
||||
if (EntityPath.class.isAssignableFrom(targetType)) {
|
||||
return new ConvertingEntityPath(entityPersister, toPath(value, sourceType));
|
||||
}
|
||||
if (entityPersister.isNodeEntity(targetType)) {
|
||||
return entityPersister.projectTo(toNode(value, sourceType), targetType);
|
||||
return entityPersister.projectTo(toNode(value, sourceType), targetType, mappingPolicy);
|
||||
}
|
||||
if (entityPersister.isRelationshipEntity(targetType)) {
|
||||
return entityPersister.projectTo(toRelationship(value, sourceType), targetType);
|
||||
return entityPersister.projectTo(toRelationship(value, sourceType), targetType, mappingPolicy);
|
||||
}
|
||||
final Object result = super.doConvert(value, sourceType, targetType);
|
||||
final Object result = super.doConvert(value, sourceType, targetType, mappingPolicy);
|
||||
|
||||
if (result != null) return result;
|
||||
|
||||
@@ -69,83 +64,22 @@ public class EntityResultConverter<T, R> extends DefaultConverter<T, R> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public R extractMapResult(Object value, Class returnType) {
|
||||
public R extractMapResult(Object value, Class returnType, MappingPolicy mappingPolicy) {
|
||||
if (!Map.class.isAssignableFrom(value.getClass())) {
|
||||
throw new RuntimeException("MapResult can only be extracted from Map<String,Object>.");
|
||||
}
|
||||
|
||||
InvocationHandler handler = new QueryResultProxy((Map<String, Object>) value);
|
||||
InvocationHandler handler = new QueryResultProxy((Map<String, Object>) value,mappingPolicy,this);
|
||||
|
||||
return (R) Proxy.newProxyInstance(returnType.getClassLoader(), new Class[]{returnType}, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R convert(Object value, Class type) {
|
||||
public R convert(Object value, Class type, MappingPolicy mappingPolicy) {
|
||||
if (type.isAnnotationPresent(MapResult.class)) {
|
||||
return extractMapResult(value, type);
|
||||
return extractMapResult(value, type,mappingPolicy);
|
||||
} else
|
||||
return super.convert(value, type);
|
||||
return super.convert(value, type,mappingPolicy);
|
||||
}
|
||||
|
||||
private class QueryResultProxy implements InvocationHandler {
|
||||
private final Map<String, Object> map;
|
||||
|
||||
private QueryResultProxy(Map<String,Object> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
|
||||
ResultColumn column = method.getAnnotation(ResultColumn.class);
|
||||
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
||||
|
||||
String columnName = column.value();
|
||||
if(!map.containsKey( columnName )) {
|
||||
throw new NoSuchColumnFoundException( columnName );
|
||||
}
|
||||
|
||||
Object columnValue = map.get( columnName );
|
||||
if(columnValue==null) return null;
|
||||
|
||||
// If the returned value is a Scala iterable, transform it to a Java iterable first
|
||||
Class iterableLikeInterface = implementsInterface("scala.collection.Iterable", columnValue.getClass());
|
||||
if (iterableLikeInterface!=null) {
|
||||
columnValue = transformScalaIterableToJavaIterable(columnValue, iterableLikeInterface);
|
||||
}
|
||||
|
||||
if (returnType.isCollectionLike())
|
||||
return new QueryResultBuilder((Iterable)columnValue, EntityResultConverter.this).to(returnType.getActualType().getType());
|
||||
else
|
||||
return convert(columnValue, returnType.getType());
|
||||
}
|
||||
}
|
||||
|
||||
public Object transformScalaIterableToJavaIterable(Object scalaIterable, Class iterableLikeIface) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
// This is equivalent to doing this:
|
||||
// JavaConversions.asJavaIterable(((IterableLike) columnValue).toIterable());
|
||||
|
||||
Class<?> javaConversions = iterableLikeIface.getClassLoader().loadClass("scala.collection.JavaConversions");
|
||||
Method asJavaIterable = javaConversions.getMethod("asJavaIterable", iterableLikeIface);
|
||||
Iterable<?> javaIterable = (Iterable<?>) asJavaIterable.invoke(null, scalaIterable);
|
||||
return javaIterable;
|
||||
}
|
||||
|
||||
private Class implementsInterface(String interfaceName, Class clazz) {
|
||||
if(clazz.getCanonicalName().equals(interfaceName)) return clazz;
|
||||
|
||||
Class superclass = clazz.getSuperclass();
|
||||
if(superclass != null) {
|
||||
Class iface = implementsInterface(interfaceName, superclass);
|
||||
if (iface!= null) return iface;
|
||||
}
|
||||
|
||||
for(Class iface : clazz.getInterfaces()) {
|
||||
Class superIface = implementsInterface(interfaceName, iface);
|
||||
if(superIface!=null)
|
||||
return superIface;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright 2011 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.neo4j.support.conversion;
|
||||
|
||||
import org.springframework.data.neo4j.annotation.ResultColumn;
|
||||
import org.springframework.data.neo4j.conversion.QueryResultBuilder;
|
||||
import org.springframework.data.neo4j.conversion.ResultConverter;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.util.ClassTypeInformation;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 10.11.11
|
||||
*/
|
||||
public class QueryResultProxy implements InvocationHandler {
|
||||
private final Map<String, Object> map;
|
||||
private final MappingPolicy mappingPolicy;
|
||||
private final ResultConverter converter;
|
||||
|
||||
public QueryResultProxy(Map<String, Object> map, MappingPolicy mappingPolicy, ResultConverter converter) {
|
||||
this.map = map;
|
||||
this.mappingPolicy = mappingPolicy;
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
|
||||
ResultColumn column = method.getAnnotation(ResultColumn.class);
|
||||
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
|
||||
|
||||
String columnName = column.value();
|
||||
if(!map.containsKey( columnName )) {
|
||||
throw new NoSuchColumnFoundException( columnName );
|
||||
}
|
||||
|
||||
Object columnValue = map.get( columnName );
|
||||
if(columnValue==null) return null;
|
||||
|
||||
// If the returned value is a Scala iterable, transform it to a Java iterable first
|
||||
Class iterableLikeInterface = implementsInterface("scala.collection.Iterable", columnValue.getClass());
|
||||
if (iterableLikeInterface!=null) {
|
||||
columnValue = transformScalaIterableToJavaIterable(columnValue, iterableLikeInterface);
|
||||
}
|
||||
|
||||
if (returnType.isCollectionLike())
|
||||
return new QueryResultBuilder((Iterable)columnValue, converter).to(returnType.getActualType().getType());
|
||||
else
|
||||
return converter.convert(columnValue, returnType.getType(), mappingPolicy);
|
||||
}
|
||||
public Object transformScalaIterableToJavaIterable(Object scalaIterable, Class iterableLikeIface) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
// This is equivalent to doing this:
|
||||
// JavaConversions.asJavaIterable(((IterableLike) columnValue).toIterable());
|
||||
|
||||
Class<?> javaConversions = iterableLikeIface.getClassLoader().loadClass("scala.collection.JavaConversions");
|
||||
Method asJavaIterable = javaConversions.getMethod("asJavaIterable", iterableLikeIface);
|
||||
Iterable<?> javaIterable = (Iterable<?>) asJavaIterable.invoke(null, scalaIterable);
|
||||
return javaIterable;
|
||||
}
|
||||
private Class implementsInterface(String interfaceName, Class clazz) {
|
||||
if(clazz.getCanonicalName().equals(interfaceName)) return clazz;
|
||||
|
||||
Class superclass = clazz.getSuperclass();
|
||||
if(superclass != null) {
|
||||
Class iface = implementsInterface(interfaceName, superclass);
|
||||
if (iface!= null) return iface;
|
||||
}
|
||||
|
||||
for(Class iface : clazz.getInterfaces()) {
|
||||
Class superIface = implementsInterface(interfaceName, iface);
|
||||
if(superIface!=null)
|
||||
return superIface;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.support.mapping;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.data.neo4j.mapping.EntityInstantiator;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.persistence.StateBackedCreator;
|
||||
import org.springframework.data.persistence.StateProvider;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -39,7 +40,7 @@ public abstract class AbstractConstructorEntityInstantiator<STATE> implements En
|
||||
private final Map<Class<?>, StateBackedCreator<?, STATE>> cache = new HashMap<Class<?>, StateBackedCreator<?, STATE>>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T createEntityFromState(STATE n, Class<T> c) {
|
||||
public <T> T createEntityFromState(STATE n, Class<T> c, final MappingPolicy mappingPolicy) {
|
||||
try {
|
||||
StateBackedCreator<T, STATE> creator = (StateBackedCreator<T, STATE>) cache.get(c);
|
||||
if (creator != null)
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.support.mapping;
|
||||
import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
import org.neo4j.helpers.collection.IterableWrapper;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -27,17 +28,19 @@ public class EntityCreatingClosableIterable<T> extends IterableWrapper<T,Propert
|
||||
private final ClosableIterable<PropertyContainer> iterable;
|
||||
private final Class<T> entityClass;
|
||||
private final Neo4jEntityPersister entityPersister;
|
||||
private final MappingPolicy mappingPolicy;
|
||||
|
||||
public EntityCreatingClosableIterable(ClosableIterable<PropertyContainer> iterable, Class<T> entityClass, Neo4jEntityPersister entityPersister) {
|
||||
super(iterable);
|
||||
this.iterable = iterable;
|
||||
this.entityClass = entityClass;
|
||||
this.entityPersister = entityPersister;
|
||||
mappingPolicy = this.entityPersister.getMappingPolicy(this.entityClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T underlyingObjectToObject(PropertyContainer state) {
|
||||
return entityPersister.createEntityFromState(state, entityClass);
|
||||
return entityPersister.createEntityFromState(state, entityClass, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,9 +17,7 @@ package org.springframework.data.neo4j.support.mapping;
|
||||
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.mapping.ManagedEntity;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipResult;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipProperties;
|
||||
import org.springframework.data.neo4j.mapping.*;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -110,6 +108,8 @@ public class EntityStateHandler {
|
||||
if (containedState != null) return containedState;
|
||||
final Class<?> type = entity.getClass();
|
||||
final Neo4jPersistentEntityImpl<?> persistentEntity = mappingContext.getPersistentEntity(type);
|
||||
final MappingPolicy mappingPolicy = persistentEntity.getMappingPolicy();
|
||||
// todo observer load policy
|
||||
if (persistentEntity.isNodeEntity()) {
|
||||
return (S) graphDatabase.createNode(null);
|
||||
}
|
||||
@@ -122,9 +122,12 @@ public class EntityStateHandler {
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S extends PropertyContainer> S createRelationship(Object entity, Neo4jPersistentEntityImpl<?> persistentEntity) {
|
||||
final RelationshipProperties relationshipProperties = persistentEntity.getRelationshipProperties();
|
||||
Node startNode = (Node) getPersistentState(relationshipProperties.getStartNodeProperty().getValue(entity));
|
||||
Node endNode = (Node) getPersistentState(relationshipProperties.getEndeNodeProperty().getValue(entity));
|
||||
Object relType = relationshipProperties.getTypeProperty().getValue(entity);
|
||||
final Neo4jPersistentProperty startNodeProperty = relationshipProperties.getStartNodeProperty();
|
||||
Node startNode = (Node) getPersistentState(startNodeProperty.getValue(entity, startNodeProperty.getMappingPolicy()));
|
||||
final Neo4jPersistentProperty endNodeProperty = relationshipProperties.getEndNodeProperty();
|
||||
Node endNode = (Node) getPersistentState(endNodeProperty.getValue(entity, endNodeProperty.getMappingPolicy()));
|
||||
final Neo4jPersistentProperty typeProperty = relationshipProperties.getTypeProperty();
|
||||
Object relType = typeProperty.getValue(entity, typeProperty.getMappingPolicy());
|
||||
if (relType instanceof RelationshipType) {
|
||||
return (S) startNode.createRelationshipTo(endNode, (RelationshipType) relType);
|
||||
}
|
||||
|
||||
@@ -17,23 +17,21 @@
|
||||
package org.springframework.data.neo4j.support.mapping;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.AbstractPersistentProperty;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.neo4j.annotation.*;
|
||||
|
||||
|
||||
import org.springframework.data.neo4j.mapping.IndexInfo;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipInfo;
|
||||
import org.springframework.data.neo4j.mapping.*;
|
||||
import org.springframework.data.neo4j.support.DoReturn;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
@@ -50,6 +48,7 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
|
||||
private final boolean isIdProperty;
|
||||
private IndexInfo indexInfo;
|
||||
private Map<Class<? extends Annotation>, ? extends Annotation> annotations;
|
||||
private Association<Neo4jPersistentProperty> myAssociation;
|
||||
|
||||
public Neo4jPersistentPropertyImpl(Field field, PropertyDescriptor propertyDescriptor,
|
||||
PersistentEntity<?, Neo4jPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder, Neo4jMappingContext ctx) {
|
||||
@@ -58,6 +57,12 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
|
||||
this.relationshipInfo = extractRelationshipInfo(field,ctx);
|
||||
this.indexInfo = extractIndexInfo();
|
||||
this.isIdProperty = annotations.containsKey(GraphId.class);
|
||||
this.myAssociation = isAssociation() ? super.getAssociation() == null ? createAssociation() : super.getAssociation() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Association<Neo4jPersistentProperty> getAssociation() {
|
||||
return myAssociation;
|
||||
}
|
||||
|
||||
private Map<Class<? extends Annotation>,? extends Annotation> extractAnnotations(Field field) {
|
||||
@@ -120,6 +125,11 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
|
||||
return new Association<Neo4jPersistentProperty>(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssociation() {
|
||||
return super.isAssociation() || isRelationship();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRelationship() {
|
||||
return this.relationshipInfo != null;
|
||||
@@ -151,7 +161,9 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
|
||||
if (isRelationship()) return false;
|
||||
final Class<?> type = getType();
|
||||
final Class<String> targetType = String.class;
|
||||
if (getTypeInformation().isCollectionLike()) return isConvertibleBetween(conversionService, getComponentType(), targetType);
|
||||
if (getTypeInformation().isCollectionLike()) {
|
||||
return isConvertibleBetween(conversionService, getComponentType(), targetType);
|
||||
}
|
||||
return isConvertibleBetween(conversionService, type, targetType);
|
||||
}
|
||||
|
||||
@@ -183,7 +195,15 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
|
||||
return annotations.values();
|
||||
}
|
||||
|
||||
public Object getValue(final Object entity) {
|
||||
public Object getValue(final Object entity, final MappingPolicy mappingPolicy) {
|
||||
if (entity instanceof ManagedEntity && !mappingPolicy.accessField()) {
|
||||
return DoReturn.unwrap(((ManagedEntity) entity).getEntityState().getValue(this, mappingPolicy));
|
||||
}
|
||||
return getValueFromEntity(entity, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueFromEntity(Object entity, final MappingPolicy mappingPolicy) {
|
||||
try {
|
||||
final Field field = getField();
|
||||
if (!field.isAccessible()) field.setAccessible(true);
|
||||
@@ -218,4 +238,31 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
|
||||
public String getIndexKey() {
|
||||
return getIndexInfo().getIndexKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingPolicy getMappingPolicy() {
|
||||
if (isAnnotationPresent(Fetch.class))
|
||||
return MappingPolicy.LOAD_POLICY;
|
||||
else
|
||||
return MappingPolicy.DEFAULT_POLICY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated todo remove when SD-Commons handles transient properties differently
|
||||
*/
|
||||
public boolean isReallyTransient() {
|
||||
return Modifier.isTransient(field.getModifiers()) || isAnnotationPresent(Transient.class) || isAnnotationPresent("javax.persistence.Transient");
|
||||
}
|
||||
|
||||
private boolean isAnnotationPresent(String className) {
|
||||
for (Class<? extends Annotation> annotationType : annotations.keySet()) {
|
||||
if (annotationType.getName().equals(className)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransient() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class Neo4jEntityConverterImpl<T,S extends PropertyContainer> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends T> R read(Class<R> requestedType, S source) {
|
||||
public <R extends T> R read(Class<R> requestedType, S source, MappingPolicy mappingPolicy) {
|
||||
// 1) source -> type alias
|
||||
// 2) type alias -> type
|
||||
// 3) check for subtype matching / enforcement
|
||||
@@ -77,31 +77,33 @@ public class Neo4jEntityConverterImpl<T,S extends PropertyContainer> implements
|
||||
@SuppressWarnings("unchecked") final Neo4jPersistentEntityImpl<R> persistentEntity = (Neo4jPersistentEntityImpl<R>) mappingContext.getPersistentEntity(targetType);
|
||||
|
||||
// 4) create object instance
|
||||
final R createdEntity = entityInstantiator.createEntityFromState(source, targetType.getType());
|
||||
final R createdEntity = entityInstantiator.createEntityFromState(source, targetType.getType(), mappingPolicy);
|
||||
|
||||
// 5) connect state
|
||||
entityStateHandler.setPersistentState(createdEntity,source);
|
||||
|
||||
if (!persistentEntity.isManaged()) {
|
||||
// 5a) depending on mode -> copy data
|
||||
if (persistentEntity.isManaged()) return createdEntity;
|
||||
if (mappingPolicy.shouldLoad()) {
|
||||
final BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper = BeanWrapper.create(createdEntity, conversionService);
|
||||
sourceStateTransmitter.copyPropertiesFrom(wrapper, source, persistentEntity);
|
||||
sourceStateTransmitter.copyPropertiesFrom(wrapper, source, persistentEntity,mappingPolicy);
|
||||
// 6) handle cascading fetches
|
||||
cascadeFetch(persistentEntity, wrapper);
|
||||
cascadeFetch(persistentEntity, wrapper, mappingPolicy);
|
||||
}
|
||||
return createdEntity;
|
||||
}
|
||||
|
||||
private <R extends T> void cascadeFetch(Neo4jPersistentEntityImpl<R> persistentEntity, final BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper) {
|
||||
private <R extends T> void cascadeFetch(Neo4jPersistentEntityImpl<R> persistentEntity, final BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, final MappingPolicy policy) {
|
||||
persistentEntity.doWithAssociations(new AssociationHandler<Neo4jPersistentProperty>() {
|
||||
@Override
|
||||
public void doWithAssociation(Association<Neo4jPersistentProperty> association) {
|
||||
final Neo4jPersistentProperty property = association.getInverse();
|
||||
if (property.isRelationship()) {
|
||||
// MappingPolicy mappingPolicy = policy.combineWith(property.getMappingPolicy());
|
||||
final MappingPolicy mappingPolicy = property.getMappingPolicy();
|
||||
if (mappingPolicy.shouldLoad() && property.isRelationship()) {
|
||||
final Object value = getProperty(wrapper, property);
|
||||
@SuppressWarnings("unchecked") final Neo4jPersistentEntityImpl<Object> persistentEntity =
|
||||
(Neo4jPersistentEntityImpl<Object>) mappingContext.getPersistentEntity(property.getTypeInformation().getActualType());
|
||||
final Object fetchedValue = entityFetchHandler.fetch(value, persistentEntity, property);
|
||||
final Object fetchedValue = entityFetchHandler.fetch(value, persistentEntity, property, mappingPolicy);
|
||||
// replace fetched one-time iterables and similiar managed values
|
||||
sourceStateTransmitter.setProperty(wrapper, property, fetchedValue);
|
||||
}
|
||||
@@ -120,7 +122,7 @@ public class Neo4jEntityConverterImpl<T,S extends PropertyContainer> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(T source, S sink) {
|
||||
public void write(T source, S sink, MappingPolicy mappingPolicy) {
|
||||
final Class<?> sourceType = source.getClass();
|
||||
@SuppressWarnings("unchecked") final Neo4jPersistentEntityImpl<T> persistentEntity = (Neo4jPersistentEntityImpl<T>) mappingContext.getPersistentEntity(sourceType);
|
||||
if (persistentEntity.isManaged()) { // todo check if typerepreentationstragegy is called ??
|
||||
@@ -134,7 +136,7 @@ public class Neo4jEntityConverterImpl<T,S extends PropertyContainer> implements
|
||||
entityStateHandler.setPersistentState(source, sink);
|
||||
typeMapper.writeType(sourceType, sink);
|
||||
}
|
||||
sourceStateTransmitter.copyPropertiesTo(wrapper, sink, persistentEntity);
|
||||
sourceStateTransmitter.copyPropertiesTo(wrapper, sink, persistentEntity,mappingPolicy);
|
||||
}
|
||||
/*
|
||||
private Node useGetOrCreateNode(S node, Neo4jPersistentEntity<?> persistentEntity, BeanWrapper<Neo4jPersistentEntity<Object>, Object> wrapper) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.neo4j.graphdb.PropertyContainer;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.neo4j.annotation.Fetch;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
|
||||
@@ -46,14 +46,17 @@ public class Neo4jEntityFetchHandler {
|
||||
|
||||
|
||||
// todo actually cascade !!
|
||||
public Object fetch(final Object value, Neo4jPersistentEntity<Object> persistentEntity, Neo4jPersistentProperty property) {
|
||||
if (value == null || !property.isAnnotationPresent(Fetch.class)) return value;
|
||||
public Object fetch(final Object value, Neo4jPersistentEntity<Object> persistentEntity, Neo4jPersistentProperty property, final MappingPolicy policy) {
|
||||
if (value == null) return value;
|
||||
//MappingPolicy mappingPolicy = mappingPolicy.combineWith(property.getMappingPolicy());
|
||||
final MappingPolicy mappingPolicy = property.getMappingPolicy();
|
||||
if (!mappingPolicy.shouldLoad()) return value;
|
||||
if (property.getTypeInformation().isCollectionLike()) {
|
||||
List<Object> replacement = new ArrayList<Object>();
|
||||
for (Object inner : ((Iterable) value)) {
|
||||
final BeanWrapper<Neo4jPersistentEntity<Object>, Object> innerWrapper = BeanWrapper.create(inner, conversionService);
|
||||
final PropertyContainer state = entityStateHandler.getPersistentState(inner);
|
||||
fetchValue(innerWrapper, state, persistentEntity);
|
||||
fetchValue(innerWrapper, state, persistentEntity, mappingPolicy);
|
||||
replacement.add(inner);
|
||||
//sourceStateTransmitter.copyPropertiesFrom(innerWrapper, entityStateHandler.<S>getPersistentState(inner), persistentEntity);
|
||||
}
|
||||
@@ -61,17 +64,17 @@ public class Neo4jEntityFetchHandler {
|
||||
} else {
|
||||
final BeanWrapper<Neo4jPersistentEntity<Object>, Object> innerWrapper = BeanWrapper.create(value, conversionService);
|
||||
final PropertyContainer state = entityStateHandler.getPersistentState(value);
|
||||
fetchValue(innerWrapper, state, persistentEntity);
|
||||
fetchValue(innerWrapper, state, persistentEntity, mappingPolicy);
|
||||
// sourceStateTransmitter.copyPropertiesFrom(innerWrapper, entityStateHandler.<S>getPersistentState(value), persistentEntity);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
public void fetchValue(final BeanWrapper<Neo4jPersistentEntity<Object>, Object> wrapper, PropertyContainer source, Neo4jPersistentEntity<Object> persistentEntity) {
|
||||
public void fetchValue(final BeanWrapper<Neo4jPersistentEntity<Object>, Object> wrapper, PropertyContainer source, Neo4jPersistentEntity<Object> persistentEntity, final MappingPolicy mappingPolicy) {
|
||||
if (persistentEntity.isNodeEntity()) {
|
||||
nodeStateTransmitter.copyPropertiesFrom(wrapper, (Node) source,persistentEntity);
|
||||
nodeStateTransmitter.copyPropertiesFrom(wrapper, (Node) source,persistentEntity, mappingPolicy);
|
||||
}
|
||||
if (persistentEntity.isRelationshipEntity()) {
|
||||
relationshipStateTransmitter.copyPropertiesFrom(wrapper, (Relationship) source, persistentEntity);
|
||||
relationshipStateTransmitter.copyPropertiesFrom(wrapper, (Relationship) source, persistentEntity, mappingPolicy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.neo4j.graphdb.Relationship;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.neo4j.mapping.*;
|
||||
import org.springframework.data.neo4j.mapping.ManagedEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -48,14 +47,40 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
|
||||
}
|
||||
|
||||
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state) {
|
||||
return createEntityFromState(state,null);
|
||||
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy) {
|
||||
return createEntityFromState(state,null, mappingPolicy);
|
||||
}
|
||||
|
||||
|
||||
static class StackedEntityCache {
|
||||
private static class Entry {
|
||||
PropertyContainer state;
|
||||
//MappingPolicy mappingPolicy;
|
||||
|
||||
Entry(PropertyContainer state, MappingPolicy mappingPolicy) {
|
||||
// ParameterCheck.notNull(state, "state",mappingPolicy,"mappingPolicy");
|
||||
// this.mappingPolicy = mappingPolicy;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Entry entry = (Entry) o;
|
||||
// mappingPolicy.equals(entry.mappingPolicy) &&
|
||||
return state.equals(entry.state);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * state.hashCode(); //+ mappingPolicy.hashCode();
|
||||
}
|
||||
}
|
||||
private long depth;
|
||||
private final Map<PropertyContainer,Object> objects =new HashMap<PropertyContainer, Object>();
|
||||
private final Map<Entry,Object> objects =new HashMap<Entry,Object>();
|
||||
private static ThreadLocal<StackedEntityCache> stackedEntityCache = new ThreadLocal<StackedEntityCache>() {
|
||||
@Override
|
||||
protected StackedEntityCache initialValue() {
|
||||
@@ -72,11 +97,11 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T get(PropertyContainer state) {
|
||||
return (T) cache().objects.get(state);
|
||||
public static <T> T get(PropertyContainer state, MappingPolicy mappingPolicy) {
|
||||
return (T) cache().objects.get(new Entry(state, mappingPolicy));
|
||||
}
|
||||
public static <T> T add(PropertyContainer state, T value) {
|
||||
cache().objects.put(state, value);
|
||||
public static <T> T add(PropertyContainer state, T value, MappingPolicy mappingPolicy) {
|
||||
cache().objects.put(new Entry(state, mappingPolicy), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -84,8 +109,8 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
return stackedEntityCache.get();
|
||||
}
|
||||
|
||||
public static boolean contains(PropertyContainer state) {
|
||||
return cache().objects.containsKey(state);
|
||||
public static boolean contains(PropertyContainer state, MappingPolicy mappingPolicy) {
|
||||
return cache().objects.containsKey(new Entry(state,mappingPolicy));
|
||||
}
|
||||
}
|
||||
public static class CachedInstantiator<S extends PropertyContainer> implements EntityInstantiator<S> {
|
||||
@@ -96,12 +121,13 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T createEntityFromState(S state, Class<T> type) {
|
||||
public <T> T createEntityFromState(S state, Class<T> type, final MappingPolicy mappingPolicy) {
|
||||
try {
|
||||
if (state==null) throw new IllegalArgumentException("State must not be null");
|
||||
StackedEntityCache.push();
|
||||
if (StackedEntityCache.contains(state)) return StackedEntityCache.get(state);
|
||||
return StackedEntityCache.add(state, delegate.createEntityFromState(state, type));
|
||||
if (StackedEntityCache.contains(state, mappingPolicy)) return StackedEntityCache.get(state, mappingPolicy);
|
||||
final T newInstance = delegate.createEntityFromState(state, type, mappingPolicy);
|
||||
return StackedEntityCache.add(state, newInstance, mappingPolicy);
|
||||
} finally {
|
||||
StackedEntityCache.pop();
|
||||
}
|
||||
@@ -125,31 +151,33 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R read(Class<R> type, S state) {
|
||||
public <R> R read(Class<R> type, S state, MappingPolicy mappingPolicy) {
|
||||
try {
|
||||
if (state==null) throw new IllegalArgumentException("State must not be null");
|
||||
StackedEntityCache.push();
|
||||
if (StackedEntityCache.contains(state)) return StackedEntityCache.get(state);
|
||||
return StackedEntityCache.add(state, delegate.read(type, state));
|
||||
if (StackedEntityCache.contains(state, mappingPolicy)) return StackedEntityCache.get(state,mappingPolicy);
|
||||
return StackedEntityCache.add(state, delegate.read(type, state,mappingPolicy),mappingPolicy);
|
||||
} finally {
|
||||
StackedEntityCache.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Object source, S sink) {
|
||||
delegate.write(source,sink);
|
||||
public void write(Object source, S sink,MappingPolicy mappingPolicy) {
|
||||
delegate.write(source,sink,mappingPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type) {
|
||||
if (state == null) throw new IllegalArgumentException("state has to be either a Node or Relationship");
|
||||
public <S extends PropertyContainer, T> T createEntityFromState(S state, Class<T> type, MappingPolicy mappingPolicy) {
|
||||
if (state == null) {
|
||||
throw new IllegalArgumentException("state has to be either a Node or Relationship, but is null");
|
||||
}
|
||||
if (isNode(state)) {
|
||||
return nodeConverter.read(type, (Node) state);
|
||||
return nodeConverter.read(type, (Node) state,mappingPolicy);
|
||||
}
|
||||
if (isRelationship(state)) {
|
||||
return relationshipConverter.read(type, (Relationship) state);
|
||||
return relationshipConverter.read(type, (Relationship) state,mappingPolicy);
|
||||
}
|
||||
throw new IllegalArgumentException("state has to be either a Node or Relationship");
|
||||
}
|
||||
@@ -163,8 +191,13 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
}
|
||||
|
||||
public <T> T projectTo(Object entity, Class<T> targetType) {
|
||||
return projectTo(entity,targetType,getMappingPolicy(targetType));
|
||||
}
|
||||
|
||||
public <T> T projectTo(Object entity, Class<T> targetType, MappingPolicy mappingPolicy) {
|
||||
PropertyContainer state = getPersistentState(entity);
|
||||
return createEntityFromState(state, targetType);
|
||||
final MappingPolicy newPolicy = mappingPolicy == null ? getMappingPolicy(targetType) : mappingPolicy;
|
||||
return createEntityFromState(state, targetType, newPolicy);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -173,12 +206,12 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
}
|
||||
|
||||
|
||||
public Object persist(Object entity) {
|
||||
public Object persist(Object entity, final MappingPolicy mappingPolicy) {
|
||||
final Class<?> type = entity.getClass();
|
||||
if (isManaged(entity)) {
|
||||
return ((ManagedEntity)entity).persist();
|
||||
} else {
|
||||
return persist(entity, type);
|
||||
return persist(entity, type, mappingPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,16 +219,18 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
return entityStateHandler.isManaged(entity);
|
||||
}
|
||||
|
||||
private Object persist(Object entity, Class<?> type) {
|
||||
private Object persist(Object entity, Class<?> type,MappingPolicy mappingPolicy) {
|
||||
if (isNodeEntity(type)) {
|
||||
final Node node = this.<Node>getPersistentState(entity);
|
||||
this.nodeConverter.write(entity, node);
|
||||
return entity; // TODO ?
|
||||
this.nodeConverter.write(entity, node,mappingPolicy);
|
||||
return createEntityFromState(getPersistentState(entity),type, getMappingPolicy(type));
|
||||
//return entity; // TODO ?
|
||||
}
|
||||
if (isRelationshipEntity(type)) {
|
||||
final Relationship relationship = this.<Relationship>getPersistentState(entity);
|
||||
this.relationshipConverter.write(entity, relationship);
|
||||
return entity; // TODO ?
|
||||
this.relationshipConverter.write(entity, relationship,mappingPolicy);
|
||||
return createEntityFromState(getPersistentState(entity),type, getMappingPolicy(type));
|
||||
// return entity; // TODO ?
|
||||
}
|
||||
throw new IllegalArgumentException("@NodeEntity or @RelationshipEntity annotation required on domain class"+type);
|
||||
}
|
||||
@@ -204,6 +239,15 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
return mappingContext.isNodeEntity(targetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingPolicy getMappingPolicy(Class<?> targetType) {
|
||||
return getPersistentEntity(targetType).getMappingPolicy();
|
||||
}
|
||||
|
||||
private Neo4jPersistentEntityImpl<?> getPersistentEntity(Class<?> targetType) {
|
||||
return mappingContext.getPersistentEntity(targetType);
|
||||
}
|
||||
|
||||
public boolean isRelationshipEntity(Class targetType) {
|
||||
return mappingContext.isRelationshipEntity(targetType);
|
||||
}
|
||||
@@ -223,13 +267,13 @@ public class Neo4jEntityPersister implements EntityPersister, Neo4jEntityConvert
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R read(Class<R> type, Node source) {
|
||||
return createEntityFromState(source, type);
|
||||
public <R> R read(Class<R> type, Node source, MappingPolicy mappingPolicy) {
|
||||
return createEntityFromState(source, type, mappingPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Object source, Node sink) {
|
||||
nodeConverter.write(source,sink);
|
||||
public void write(Object source, Node sink, MappingPolicy mappingPolicy) {
|
||||
nodeConverter.write(source,sink,mappingPolicy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,7 @@ import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.neo4j.annotation.*;
|
||||
import org.springframework.data.neo4j.mapping.ManagedEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipProperties;
|
||||
import org.springframework.data.neo4j.mapping.*;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -115,7 +112,7 @@ public class Neo4jPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4j
|
||||
public Object getPersistentId(Object entity) {
|
||||
final Neo4jPersistentProperty idProperty = getIdProperty();
|
||||
if (idProperty==null) throw new MappingException("No field annotated with @GraphId found in "+ getEntityName());
|
||||
return idProperty.getValue(entity);
|
||||
return idProperty.getValue(entity, idProperty.getMappingPolicy());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -157,7 +154,7 @@ public class Neo4jPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4j
|
||||
}
|
||||
|
||||
@Override
|
||||
public Neo4jPersistentProperty getEndeNodeProperty() {
|
||||
public Neo4jPersistentProperty getEndNodeProperty() {
|
||||
return endNodeProperty;
|
||||
}
|
||||
|
||||
@@ -177,4 +174,9 @@ public class Neo4jPersistentEntityImpl<T> extends BasicPersistentEntity<T, Neo4j
|
||||
public String toString() {
|
||||
return String.format("%s %smanaged @%sEntity Annotations: %s", getType(), isManaged() ? "" : "un", isNodeEntity() ? "Node" : "Relationship", annotations.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingPolicy getMappingPolicy() {
|
||||
return MappingPolicy.LOAD_POLICY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.BeanWrapper;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.neo4j.core.EntityState;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.DoReturn;
|
||||
@@ -42,37 +43,39 @@ public class SourceStateTransmitter<S extends PropertyContainer> {
|
||||
this.entityStateFactory = entityStateFactory;
|
||||
}
|
||||
|
||||
public <R> R copyPropertiesFrom(final BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, S source, Neo4jPersistentEntity<R> persistentEntity) {
|
||||
public <R> R copyPropertiesFrom(final BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, S source, Neo4jPersistentEntity<R> persistentEntity, final MappingPolicy mappingPolicy) {
|
||||
final R entity = wrapper.getBean();
|
||||
final Transaction tx = getTemplate().beginTx();
|
||||
/* final Transaction tx = getTemplate().beginTx();
|
||||
try {
|
||||
*/
|
||||
final EntityState<S> entityState = entityStateFactory.getEntityState(entity, false);
|
||||
entityState.setPersistentState(source);
|
||||
entityState.persist();
|
||||
// entityState.persist();
|
||||
persistentEntity.doWithProperties(new PropertyHandler<Neo4jPersistentProperty>() {
|
||||
@Override
|
||||
public void doWithPersistentProperty(Neo4jPersistentProperty property) {
|
||||
copyEntityStatePropertyValue(property, entityState, wrapper);
|
||||
copyEntityStatePropertyValue(property, entityState, wrapper, property.getMappingPolicy()); // TODO intelligent mappingPolicy.combineWith(property.getMappingPolicy())
|
||||
}
|
||||
});
|
||||
persistentEntity.doWithAssociations(new AssociationHandler<Neo4jPersistentProperty>() {
|
||||
@Override
|
||||
public void doWithAssociation(Association<Neo4jPersistentProperty> association) {
|
||||
final Neo4jPersistentProperty property = association.getInverse();
|
||||
copyEntityStatePropertyValue(property, entityState, wrapper);
|
||||
copyEntityStatePropertyValue(property, entityState, wrapper, property.getMappingPolicy()); // TODO intelligent mappingPolicy.combineWith(property.getMappingPolicy())
|
||||
}
|
||||
});
|
||||
tx.success();
|
||||
// tx.success();
|
||||
return entity;
|
||||
} finally {
|
||||
/* } finally {
|
||||
tx.finish();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private <R> void setEntityStateValue(Neo4jPersistentProperty property, EntityState<S> entityState, BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper) {
|
||||
if (!entityState.isWritable(property.getField())) return;
|
||||
private <R> void setEntityStateValue(Neo4jPersistentProperty property, EntityState<S> entityState, BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, final MappingPolicy mappingPolicy) {
|
||||
if (!entityState.isWritable(property)) return;
|
||||
final Object value = getProperty(wrapper, property);
|
||||
entityState.setValue(property, value);
|
||||
entityState.setValue(property, value, mappingPolicy);
|
||||
}
|
||||
|
||||
private Neo4jTemplate getTemplate() {
|
||||
@@ -109,30 +112,32 @@ public class SourceStateTransmitter<S extends PropertyContainer> {
|
||||
}
|
||||
}
|
||||
|
||||
private <R> Object copyEntityStatePropertyValue(Neo4jPersistentProperty property, EntityState<S> nodeState, BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper) {
|
||||
final Object value = DoReturn.unwrap(nodeState.getValue(property.getField()));
|
||||
private <R> Object copyEntityStatePropertyValue(Neo4jPersistentProperty property, EntityState<S> nodeState, BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, final MappingPolicy mappingPolicy) {
|
||||
final Object value = DoReturn.unwrap(nodeState.getValue(property, mappingPolicy));
|
||||
setProperty(wrapper, property, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public <R> void copyPropertiesTo(final BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, S target, Neo4jPersistentEntity<R> persistentEntity) {
|
||||
public <R> void copyPropertiesTo(final BeanWrapper<Neo4jPersistentEntity<R>, R> wrapper, S target, Neo4jPersistentEntity<R> persistentEntity, MappingPolicy mappingPolicy) {
|
||||
final Transaction tx = getTemplate().beginTx();
|
||||
try {
|
||||
//final Node targetNode = useGetOrCreateNode(node, persistentEntity, wrapper);
|
||||
final EntityState<S> entityState = entityStateFactory.getEntityState(wrapper.getBean(), false);
|
||||
entityState.setPersistentState(target);
|
||||
entityState.persist();
|
||||
// todo take mapping policies for attributes into account
|
||||
persistentEntity.doWithProperties(new PropertyHandler<Neo4jPersistentProperty>() {
|
||||
@Override
|
||||
public void doWithPersistentProperty(Neo4jPersistentProperty property) {
|
||||
setEntityStateValue(property, entityState, wrapper);
|
||||
setEntityStateValue(property, entityState, wrapper, property.getMappingPolicy());
|
||||
}
|
||||
});
|
||||
// todo take mapping policies for relationships into account
|
||||
persistentEntity.doWithAssociations(new AssociationHandler<Neo4jPersistentProperty>() {
|
||||
@Override
|
||||
public void doWithAssociation(Association<Neo4jPersistentProperty> association) {
|
||||
final Neo4jPersistentProperty property = association.getInverse();
|
||||
setEntityStateValue(property, entityState, wrapper);
|
||||
setEntityStateValue(property, entityState, wrapper, property.getMappingPolicy());
|
||||
}
|
||||
});
|
||||
tx.success();
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.helpers.collection.IterableWrapper;
|
||||
import org.springframework.data.neo4j.core.EntityPath;
|
||||
import org.springframework.data.neo4j.mapping.EntityPersister;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@@ -40,7 +41,7 @@ public class ConvertingEntityPath<S,E> implements EntityPath<S,E> {
|
||||
|
||||
private <T> T projectEntityToFirstParameterOrCreateFromStoredType(Node node, Class<T>... types) {
|
||||
if (node==null) return null;
|
||||
if (types==null || types.length==0) return persister.createEntityFromStoredType(node);
|
||||
if (types==null || types.length==0) return persister.createEntityFromStoredType(node, MappingPolicy.LOAD_POLICY);
|
||||
return persister.projectTo(node, types[0]);
|
||||
}
|
||||
|
||||
@@ -65,7 +66,7 @@ public class ConvertingEntityPath<S,E> implements EntityPath<S,E> {
|
||||
return new IterableWrapper<T,Node>(nodes()) {
|
||||
@Override
|
||||
protected T underlyingObjectToObject(Node node) {
|
||||
return persister.createEntityFromStoredType(node);
|
||||
return persister.createEntityFromStoredType(node, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.support.query;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.neo4j.conversion.DefaultConverter;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -32,7 +33,7 @@ public class ConversionServiceQueryResultConverter<R> extends DefaultConverter<O
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected Object doConvert(Object value, Class<?> sourceType, Class type) {
|
||||
protected Object doConvert(Object value, Class<?> sourceType, Class type, MappingPolicy mappingPolicy) {
|
||||
if (conversionService.canConvert(sourceType, type)) {
|
||||
return conversionService.convert(value, type);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.neo4j.graphdb.RelationshipType;
|
||||
import org.springframework.dao.InvalidDataAccessResourceUsageException;
|
||||
import org.springframework.data.neo4j.fieldaccess.DefaultEntityState;
|
||||
import org.springframework.data.neo4j.fieldaccess.DelegatingFieldAccessorFactory;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentEntity;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.mapping.RelationshipProperties;
|
||||
@@ -38,10 +39,12 @@ import org.springframework.data.neo4j.support.ParameterCheck;
|
||||
public class RelationshipEntityState extends DefaultEntityState<Relationship> {
|
||||
|
||||
private final Neo4jTemplate template;
|
||||
|
||||
private MappingPolicy mappingPolicy;
|
||||
|
||||
public RelationshipEntityState(final Relationship underlyingState, final Object entity, final Class<? extends Object> type, final Neo4jTemplate template, final DelegatingFieldAccessorFactory delegatingFieldAccessorFactory, Neo4jPersistentEntity<Object> persistentEntity) {
|
||||
super(underlyingState, entity, type, delegatingFieldAccessorFactory, persistentEntity);
|
||||
this.template = template;
|
||||
this.mappingPolicy = persistentEntity.getMappingPolicy();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -68,8 +71,8 @@ public class RelationshipEntityState extends DefaultEntityState<Relationship> {
|
||||
|
||||
private Relationship createRelationshipFromEntity() {
|
||||
final RelationshipProperties relationshipProperties = getPersistentEntity().getRelationshipProperties();
|
||||
final Node startNode = template.getPersistentState(relationshipProperties.getStartNodeProperty().getValue(entity));
|
||||
final Node endNode = template.getPersistentState(relationshipProperties.getEndeNodeProperty().getValue(entity));
|
||||
final Node startNode = template.getPersistentState(relationshipProperties.getStartNodeProperty().getValue(entity, mappingPolicy));
|
||||
final Node endNode = template.getPersistentState(relationshipProperties.getEndNodeProperty().getValue(entity, mappingPolicy));
|
||||
final String type = getRelationshipTypeFromEntity(getPersistentEntity());
|
||||
ParameterCheck.notNull(startNode,"start node property",endNode,"end node property",type, "relationship type property from field or annotation");
|
||||
return template.createRelationshipBetween(startNode, endNode,type,null);
|
||||
@@ -78,7 +81,7 @@ public class RelationshipEntityState extends DefaultEntityState<Relationship> {
|
||||
private String getRelationshipTypeFromEntity(Neo4jPersistentEntity<?> persistentEntity) {
|
||||
final RelationshipProperties relationshipProperties = persistentEntity.getRelationshipProperties();
|
||||
final Neo4jPersistentProperty typeProperty = relationshipProperties.getTypeProperty();
|
||||
final Object value = typeProperty!=null ? typeProperty.getValue(entity) : null;
|
||||
final Object value = typeProperty!=null ? typeProperty.getValue(entity, mappingPolicy) : null;
|
||||
if (value==null) {
|
||||
return relationshipProperties.getRelationshipType();
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.helpers.collection.ClosableIterable;
|
||||
import org.springframework.data.neo4j.annotation.QueryType;
|
||||
import org.springframework.data.neo4j.conversion.Result;
|
||||
import org.springframework.data.neo4j.conversion.ResultConverter;
|
||||
import org.springframework.data.neo4j.core.GraphDatabase;
|
||||
import org.springframework.data.neo4j.mapping.MappingPolicy;
|
||||
import org.springframework.data.neo4j.repository.GraphRepository;
|
||||
import org.springframework.data.neo4j.support.query.QueryEngine;
|
||||
|
||||
@@ -256,4 +258,13 @@ public interface Neo4jOperations {
|
||||
* @return the graph database used by the template
|
||||
*/
|
||||
GraphDatabase getGraphDatabase();
|
||||
|
||||
<T> T fetch(T value);
|
||||
|
||||
<S extends PropertyContainer, T> T load(S state, Class<T> type);
|
||||
|
||||
|
||||
MappingPolicy getMappingPolicy(Class<?> targetType);
|
||||
|
||||
ResultConverter getDefaultConverter();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
package org.springframework.data.neo4j.mapping;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.neo4j.graphdb.*;
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.neo4j.graphdb.Node;
|
||||
import org.neo4j.graphdb.Relationship;
|
||||
import org.neo4j.graphdb.index.Index;
|
||||
import org.neo4j.helpers.collection.IteratorUtil;
|
||||
import org.springframework.data.neo4j.model.Friendship;
|
||||
@@ -24,11 +27,15 @@ import org.springframework.data.neo4j.model.Group;
|
||||
import org.springframework.data.neo4j.model.Person;
|
||||
import org.springframework.data.neo4j.model.Personality;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.neo4j.helpers.collection.IteratorUtil.first;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
@@ -71,6 +78,25 @@ public class Neo4jEntityConverterTest extends Neo4jPersistentTestBase {
|
||||
assertEquals("Michael", michaelNode().getProperty("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadPolicy() {
|
||||
storeInGraph(emil);
|
||||
michael.setHeight((short)182);
|
||||
michael.setPersonality(Personality.EXTROVERT);
|
||||
michael.setBoss(emil);
|
||||
storeInGraph(michael);
|
||||
final Person loaded = template.findOne(michael.getId(), Person.class);
|
||||
assertEquals("loaded id", michael.getId(),loaded.getId());
|
||||
assertEquals("loaded simple property", michael.getName(),loaded.getName());
|
||||
assertEquals("loaded simple short property", michael.getHeight(),loaded.getHeight());
|
||||
assertEquals("loaded simple converted property", michael.getPersonality(),loaded.getPersonality());
|
||||
final Person boss = loaded.getBoss();
|
||||
assertNotNull("instance non-fetch relationship", boss);
|
||||
assertEquals("id of non-fetch relationship", emil.getId(), boss.getId());
|
||||
assertNull("no properties of non-fetch relationship", boss.getName());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindNewlyWrittenNodeInIndex() {
|
||||
storeInGraph(michael);
|
||||
@@ -78,6 +104,7 @@ public class Neo4jEntityConverterTest extends Neo4jPersistentTestBase {
|
||||
final Index<Node> index = template.getIndex(Person.NAME_INDEX, Person.class);
|
||||
final Node found = index.get("name", "Michael").getSingle();
|
||||
assertEquals("node found in index", createdNode, found);
|
||||
assertEquals("node property loaded", michael.getName() , found.getProperty("name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -282,15 +309,26 @@ public class Neo4jEntityConverterTest extends Neo4jPersistentTestBase {
|
||||
|
||||
|
||||
@Test
|
||||
public void testCascadingReadWithProperties() {
|
||||
public void testCascadingReadWithOutProperties() {
|
||||
Node groupNode = createNewNode();
|
||||
Node julianNode = createNewNode();
|
||||
julianNode.setProperty("name", "Julian");
|
||||
groupNode.createRelationshipTo(julianNode, PERSONS);
|
||||
|
||||
Group g = readGroup(groupNode);
|
||||
Person julian = IteratorUtil.first(g.getPersons());
|
||||
assertEquals("Julian", julian.getName());
|
||||
|
||||
assertNull(first(g.getPersons()).getName());
|
||||
}
|
||||
@Test
|
||||
public void testCascadingReadWithProperties() {
|
||||
Node groupNode = createNewNode();
|
||||
Node julianNode = createNewNode();
|
||||
julianNode.setProperty("name", "Julian");
|
||||
groupNode.createRelationshipTo(julianNode, DynamicRelationshipType.withName("fetchedPersons"));
|
||||
|
||||
Group g = readGroup(groupNode);
|
||||
|
||||
assertEquals("Julian", first(g.getFetchedPersons()).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -301,7 +339,7 @@ public class Neo4jEntityConverterTest extends Neo4jPersistentTestBase {
|
||||
Relationship friendshipRelationship = makeFriends(michaelNode(), andresNode(), 19);
|
||||
|
||||
Person m = readPerson(michaelNode());
|
||||
Friendship friendship = IteratorUtil.first(m.getFriendships());
|
||||
Friendship friendship = first(m.getFriendships());
|
||||
|
||||
assertEquals((Long) friendshipRelationship.getId(), friendship.getId());
|
||||
assertEquals(19, friendship.getYears());
|
||||
|
||||
@@ -33,7 +33,7 @@ public class Neo4jEntityPersisterTest extends Neo4jPersistentTestBase {
|
||||
public void testCreateEntityFromStoredType() throws Exception {
|
||||
final Node personNode = template.createNode();
|
||||
personNode.setProperty("name","Michael");
|
||||
final Person person = entityPersister.createEntityFromState(personNode, Person.class);
|
||||
final Person person = entityPersister.createEntityFromState(personNode, Person.class, template.getMappingPolicy(Person.class));
|
||||
assertEquals("Michael",person.getName());
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class Neo4jEntityPersisterTest extends Neo4jPersistentTestBase {
|
||||
|
||||
@Test
|
||||
public void testPersist() throws Exception {
|
||||
entityPersister.persist(michael);
|
||||
entityPersister.persist(michael, template.getMappingPolicy(michael));
|
||||
assertEquals((Long) michaelNode().getId(), michael.getId());
|
||||
assertEquals(michaelNode(), entityPersister.getPersistentState(michael));
|
||||
assertEquals(michaelNode().getProperty("name"), michael.getName());
|
||||
|
||||
@@ -195,7 +195,7 @@ public class Neo4jPersistentTestBase {
|
||||
}
|
||||
|
||||
protected Object write(Object entity, Node node) {
|
||||
entityPersister.write(entity, node);
|
||||
entityPersister.write(entity, node, template.getMappingPolicy(entity));
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ public class Neo4jPersistentTestBase {
|
||||
}
|
||||
|
||||
public Person readPerson(Node node) {
|
||||
return entityPersister.read(Person.class, node);
|
||||
return entityPersister.read(Person.class, node, template.getMappingPolicy(Person.class));
|
||||
}
|
||||
|
||||
protected Relationship makeFriends(Node from, Node to, int years) {
|
||||
@@ -235,7 +235,7 @@ public class Neo4jPersistentTestBase {
|
||||
}
|
||||
|
||||
public Group readGroup(Node node) {
|
||||
return entityPersister.read(Group.class, node);
|
||||
return entityPersister.read(Group.class, node,template.getMappingPolicy(Group.class));
|
||||
}
|
||||
|
||||
protected List<Node> getRelatedNodes(Node startNode, String type, Direction direction) {
|
||||
|
||||
@@ -16,26 +16,21 @@
|
||||
|
||||
package org.springframework.data.neo4j.model;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.neo4j.graphdb.Direction;
|
||||
import org.neo4j.graphdb.DynamicRelationshipType;
|
||||
import org.neo4j.graphdb.traversal.TraversalDescription;
|
||||
import org.neo4j.kernel.Traversal;
|
||||
import org.neo4j.kernel.impl.traversal.TraversalDescriptionImpl;
|
||||
import org.springframework.data.neo4j.annotation.GraphId;
|
||||
import org.springframework.data.neo4j.annotation.GraphProperty;
|
||||
import org.springframework.data.neo4j.annotation.GraphTraversal;
|
||||
import org.springframework.data.neo4j.annotation.Indexed;
|
||||
import org.springframework.data.neo4j.annotation.NodeEntity;
|
||||
import org.springframework.data.neo4j.annotation.RelatedTo;
|
||||
import org.springframework.data.neo4j.annotation.*;
|
||||
import org.springframework.data.neo4j.core.FieldTraversalDescriptionBuilder;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.index.IndexType;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@NodeEntity
|
||||
public class Group {
|
||||
|
||||
@@ -44,12 +39,15 @@ public class Group {
|
||||
public static final String SEARCH_GROUPS_INDEX_BUG = "search-groups";
|
||||
|
||||
@RelatedTo(direction = Direction.OUTGOING)
|
||||
private Collection<Person> persons = new HashSet<Person>();
|
||||
private Collection<Person> persons;
|
||||
|
||||
@Fetch @RelatedTo
|
||||
private Collection<Person> fetchedPersons;
|
||||
|
||||
@RelatedTo(type = "persons", elementClass = Person.class)
|
||||
private Iterable<Person> readOnlyPersons;
|
||||
|
||||
@GraphTraversal(traversal = PeopleTraversalBuilder.class, elementClass = Person.class, params = "persons")
|
||||
@GraphTraversal(traversal = PeopleTraversalBuilder.class, params = "persons")
|
||||
private Iterable<Person> people;
|
||||
|
||||
@GraphProperty
|
||||
@@ -117,6 +115,7 @@ public class Group {
|
||||
}
|
||||
|
||||
public void addPerson(Person person) {
|
||||
if (persons==null) persons=new HashSet<Person>();
|
||||
persons.add(person);
|
||||
}
|
||||
|
||||
@@ -215,4 +214,8 @@ public class Group {
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.id);
|
||||
}
|
||||
|
||||
public Collection<Person> getFetchedPersons() {
|
||||
return fetchedPersons;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
createTeam();
|
||||
neo4jOperations = neo4jTemplate;
|
||||
neo4jOperations = template;
|
||||
}
|
||||
|
||||
@Test @Transactional
|
||||
@@ -96,14 +96,14 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
@Test @Transactional
|
||||
public void testGetIndexForType() throws Exception {
|
||||
|
||||
final Index<PropertyContainer> personIndex = neo4jTemplate.getIndex(Person.class);
|
||||
final Index<PropertyContainer> personIndex = template.getIndex(Person.class);
|
||||
assertEquals("Person",personIndex.getName());
|
||||
}
|
||||
|
||||
@Test @Transactional
|
||||
public void testGetIndexForName() throws Exception {
|
||||
|
||||
final Index<PropertyContainer> nameIndex = neo4jTemplate.getIndex(Person.NAME_INDEX);
|
||||
final Index<PropertyContainer> nameIndex = template.getIndex(Person.NAME_INDEX);
|
||||
assertEquals(Person.NAME_INDEX, nameIndex.getName());
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
public void testCreateRelationshipEntityFromStoredType() throws Exception {
|
||||
|
||||
final Relationship friendshipRelationship = getRelationshipState(testTeam.friendShip);
|
||||
Friendship found = neo4jTemplate.createEntityFromStoredType(friendshipRelationship);
|
||||
Friendship found = template.createEntityFromStoredType(friendshipRelationship, template.getMappingPolicy(testTeam.michael));
|
||||
assertEquals(testTeam.friendShip.getId(),found.getId());
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
public void testCreateNodeEntityFromStoredType() throws Exception {
|
||||
|
||||
final Node michaelNode = getNodeState(testTeam.michael);
|
||||
Person found = neo4jTemplate.createEntityFromStoredType(michaelNode);
|
||||
Person found = template.createEntityFromStoredType(michaelNode, template.getMappingPolicy(testTeam.michael));
|
||||
assertEquals(testTeam.michael.getId(),found.getId());
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
public void testCreateEntityFromState() throws Exception {
|
||||
|
||||
final PropertyContainer michaelNode = getNodeState(testTeam.michael);
|
||||
Person found = neo4jTemplate.createEntityFromStoredType(michaelNode);
|
||||
Person found = template.createEntityFromStoredType(michaelNode, template.getMappingPolicy(testTeam.michael));
|
||||
assertEquals(testTeam.michael.getId(),found.getId());
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
@Test @Transactional
|
||||
public void testSetPersistentState() throws Exception {
|
||||
final Person clone = new Person();
|
||||
neo4jTemplate.setPersistentState(clone, neo4jOperations.getPersistentState(testTeam.david));
|
||||
template.setPersistentState(clone, neo4jOperations.getPersistentState(testTeam.david));
|
||||
assertEquals(testTeam.david.getId(), clone.getId());
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
final Long id = testTeam.michael.getId();
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
neo4jTemplate.removeNodeEntity(testTeam.michael);
|
||||
template.removeNodeEntity(testTeam.michael);
|
||||
}
|
||||
});
|
||||
assertNull(neo4jOperations.getNode(id));
|
||||
@@ -217,7 +217,7 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
final Long id = testTeam.friendShip.getId();
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
|
||||
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
||||
neo4jTemplate.removeRelationshipEntity(testTeam.friendShip);
|
||||
template.removeRelationshipEntity(testTeam.friendShip);
|
||||
}
|
||||
});
|
||||
assertNull(neo4jOperations.getRelationship(id));
|
||||
@@ -229,22 +229,22 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
public void testCreateNodeAs() throws Exception {
|
||||
final Person thomas = neo4jOperations.createNodeAs(Person.class, map("name", "Thomas"));
|
||||
assertEquals("Thomas",neo4jOperations.getNode(thomas.getId()).getProperty("name"));
|
||||
final Person found = neo4jTemplate.createEntityFromStoredType(getNodeState(thomas));
|
||||
final Person found = template.createEntityFromStoredType(getNodeState(thomas), template.getMappingPolicy(Person.class));
|
||||
assertEquals("Thomas",found.getName());
|
||||
}
|
||||
|
||||
@Test @Transactional
|
||||
public void testIsNodeEntity() throws Exception {
|
||||
assertEquals(true,neo4jTemplate.isNodeEntity(Person.class));
|
||||
assertEquals(false,neo4jTemplate.isNodeEntity(Friendship.class));
|
||||
assertEquals(false,neo4jTemplate.isNodeEntity(Object.class));
|
||||
assertEquals(true, template.isNodeEntity(Person.class));
|
||||
assertEquals(false, template.isNodeEntity(Friendship.class));
|
||||
assertEquals(false, template.isNodeEntity(Object.class));
|
||||
}
|
||||
|
||||
@Test @Transactional
|
||||
public void testIsRelationshipEntity() throws Exception {
|
||||
assertEquals(true,neo4jTemplate.isRelationshipEntity(Friendship.class));
|
||||
assertEquals(false,neo4jTemplate.isRelationshipEntity(Person.class));
|
||||
assertEquals(false,neo4jTemplate.isRelationshipEntity(Object.class));
|
||||
assertEquals(true, template.isRelationshipEntity(Friendship.class));
|
||||
assertEquals(false, template.isRelationshipEntity(Person.class));
|
||||
assertEquals(false, template.isRelationshipEntity(Object.class));
|
||||
}
|
||||
|
||||
@Test @Transactional
|
||||
@@ -260,11 +260,11 @@ public class EntityNeo4jTemplateTest extends EntityTestBase {
|
||||
static abstract class ManagedTestEntity implements ManagedEntity {}
|
||||
@Test @Transactional
|
||||
public void testIsManaged() throws Exception {
|
||||
assertEquals(true,neo4jTemplate.isManaged(Mockito.mock(ManagedEntity.class)));
|
||||
assertEquals(true,neo4jTemplate.isManaged(Mockito.mock(ManagedTestEntity.class)));
|
||||
assertEquals(false,neo4jTemplate.isManaged(testTeam.michael));
|
||||
assertEquals(false,neo4jTemplate.isManaged(testTeam.friendShip));
|
||||
assertEquals(false,neo4jTemplate.isManaged(new Object()));
|
||||
assertEquals(true, template.isManaged(Mockito.mock(ManagedEntity.class)));
|
||||
assertEquals(true, template.isManaged(Mockito.mock(ManagedTestEntity.class)));
|
||||
assertEquals(false, template.isManaged(testTeam.michael));
|
||||
assertEquals(false, template.isManaged(testTeam.friendShip));
|
||||
assertEquals(false, template.isManaged(new Object()));
|
||||
}
|
||||
|
||||
@Test @Transactional
|
||||
|
||||
@@ -42,7 +42,7 @@ import java.util.Set;
|
||||
public class EntityTestBase {
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
@Autowired protected Neo4jTemplate neo4jTemplate;
|
||||
@Autowired protected Neo4jTemplate template;
|
||||
@Autowired protected ConversionService conversionService;
|
||||
|
||||
@Autowired protected GraphDatabaseService graphDatabaseService;
|
||||
@@ -57,28 +57,28 @@ public class EntityTestBase {
|
||||
}
|
||||
|
||||
protected Node getNodeState(Object entity) {
|
||||
return neo4jTemplate.getPersistentState(entity);
|
||||
return template.getPersistentState(entity);
|
||||
}
|
||||
protected Long getNodeId(Object entity) {
|
||||
final Node node = neo4jTemplate.getPersistentState(entity);
|
||||
final Node node = template.getPersistentState(entity);
|
||||
return node == null ? null : node.getId();
|
||||
}
|
||||
protected Long getRelationshipId(Object entity) {
|
||||
final Relationship rel = neo4jTemplate.getPersistentState(entity);
|
||||
final Relationship rel = template.getPersistentState(entity);
|
||||
return rel == null ? null : rel.getId();
|
||||
}
|
||||
|
||||
protected boolean hasPersistentState(Object entity) {
|
||||
return neo4jTemplate.getPersistentState(entity)!=null;
|
||||
return template.getPersistentState(entity)!=null;
|
||||
}
|
||||
|
||||
protected Relationship getRelationshipState(Object entity) {
|
||||
return neo4jTemplate.getPersistentState(entity);
|
||||
return template.getPersistentState(entity);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T persist(T entity) {
|
||||
return (T) neo4jTemplate.save(entity);
|
||||
return (T) template.save(entity);
|
||||
}
|
||||
|
||||
protected <T> Set<T> set(T... values) {
|
||||
@@ -97,11 +97,11 @@ public class EntityTestBase {
|
||||
|
||||
@Before
|
||||
public void cleanDbBeforeTest() {
|
||||
Neo4jHelper.cleanDb(neo4jTemplate);
|
||||
Neo4jHelper.cleanDb(template);
|
||||
}
|
||||
|
||||
@BeforeTransaction
|
||||
public void cleanDb() {
|
||||
Neo4jHelper.cleanDb(neo4jTemplate);
|
||||
Neo4jHelper.cleanDb(template);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,28 +325,28 @@ public class FullNeo4jTemplateTest {
|
||||
assertEquals("rel2", relationship.getProperty("name", "not set"));
|
||||
}
|
||||
|
||||
private static class PathRelationshipNameMapper implements ResultConverter<Path, String> {
|
||||
private static class PathRelationshipNameMapper extends ResultConverter.ResultConverterAdapter<Path, String> {
|
||||
@Override
|
||||
public String convert(Path path, Class<String> type) {
|
||||
return (String) path.lastRelationship().getProperty("name", "not set");
|
||||
}
|
||||
}
|
||||
|
||||
private static class PathNodeNameMapper implements ResultConverter<Path, String> {
|
||||
private static class PathNodeNameMapper extends ResultConverter.ResultConverterAdapter<Path, String> {
|
||||
@Override
|
||||
public String convert(Path path, Class<String> type) {
|
||||
return (String) path.endNode().getProperty("name", "not set");
|
||||
}
|
||||
}
|
||||
|
||||
private static class RelationshipNameConverter implements ResultConverter<Relationship, String> {
|
||||
private static class RelationshipNameConverter extends ResultConverter.ResultConverterAdapter<Relationship, String> {
|
||||
@Override
|
||||
public String convert(Relationship value, Class<String> type) {
|
||||
return (String) value.getProperty("name");
|
||||
}
|
||||
}
|
||||
|
||||
private static class PropertyContainerNameConverter implements ResultConverter<PropertyContainer, String> {
|
||||
private static class PropertyContainerNameConverter extends ResultConverter.ResultConverterAdapter<PropertyContainer, String> {
|
||||
@Override
|
||||
public String convert(PropertyContainer value, Class<String> type) {
|
||||
return (String) value.getProperty("name");
|
||||
|
||||
@@ -327,27 +327,27 @@ public class Neo4jTemplateApiTest {
|
||||
assertEquals("rel2",relationship.getProperty("name","not set"));
|
||||
}
|
||||
|
||||
private static class PathRelationshipNameMapper implements ResultConverter<Path,String> {
|
||||
private static class PathRelationshipNameMapper extends ResultConverter.ResultConverterAdapter<Path,String> {
|
||||
@Override
|
||||
public String convert(Path path, Class<String> type) {
|
||||
return (String) path.lastRelationship().getProperty("name","not set");
|
||||
}
|
||||
}
|
||||
private static class PathNodeNameMapper implements ResultConverter<Path,String> {
|
||||
private static class PathNodeNameMapper extends ResultConverter.ResultConverterAdapter<Path,String> {
|
||||
@Override
|
||||
public String convert(Path path, Class<String> type) {
|
||||
return (String) path.endNode().getProperty("name","not set");
|
||||
}
|
||||
}
|
||||
|
||||
private static class RelationshipNameConverter implements ResultConverter<Relationship,String> {
|
||||
private static class RelationshipNameConverter extends ResultConverter.ResultConverterAdapter<Relationship,String> {
|
||||
@Override
|
||||
public String convert(Relationship value, Class<String> type) {
|
||||
return (String) value.getProperty("name");
|
||||
}
|
||||
}
|
||||
|
||||
private static class PropertyContainerNameConverter implements ResultConverter<PropertyContainer, String> {
|
||||
private static class PropertyContainerNameConverter extends ResultConverter.ResultConverterAdapter<PropertyContainer, String> {
|
||||
@Override
|
||||
public String convert(PropertyContainer value, Class<String> type) {
|
||||
return (String) value.getProperty("name");
|
||||
|
||||
@@ -83,7 +83,7 @@ public class SnippetNeo4jTemplateMethodsTest extends DocumentingTestBase {
|
||||
assertEquals(thomas, neo.lookup("devs", "name", "Thomas").to(Node.class).single());
|
||||
|
||||
// Index lookup with Result Converter
|
||||
assertEquals("Thomas", neo.lookup("devs", "name", "Thomas").to(String.class, new ResultConverter<PropertyContainer, String>() {
|
||||
assertEquals("Thomas", neo.lookup("devs", "name", "Thomas").to(String.class, new ResultConverter.ResultConverterAdapter<PropertyContainer, String>() {
|
||||
public String convert(PropertyContainer element, Class<String> type) {
|
||||
return (String) element.getProperty("name");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user