added support for arrays, iterables, collections, sets and lists of primitive and convertible values as node properties
This commit is contained in:
@@ -29,6 +29,7 @@ import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.index.IndexType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
@NodeEntity
|
||||
public class Group {
|
||||
@@ -79,6 +80,7 @@ public class Group {
|
||||
|
||||
@Indexed(level=Indexed.Level.INSTANCE)
|
||||
private String indexLevelName;
|
||||
private String[] roleNames;
|
||||
|
||||
public String getFullTextName() {
|
||||
return fullTextName;
|
||||
@@ -124,6 +126,33 @@ public class Group {
|
||||
this.otherName = otherName;
|
||||
}
|
||||
|
||||
public void setRoleNames(String...roleNames) {
|
||||
this.roleNames=roleNames;
|
||||
}
|
||||
|
||||
public String[] getRoleNames() {
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
public enum Role { ADMIN, USER }
|
||||
Role[] roles;
|
||||
|
||||
public Role[] getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Role...roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
Collection<String> roleNamesColl;
|
||||
Collection<Role> rolesColl;
|
||||
Set<String> roleNamesSet;
|
||||
Set<Role> rolesSet;
|
||||
Iterable<String> roleNamesIterable;
|
||||
Iterable<Role> rolesIterable;
|
||||
|
||||
|
||||
private static class PeopleTraversalBuilder implements FieldTraversalDescriptionBuilder {
|
||||
@Override
|
||||
public TraversalDescription build(Object start, Neo4jPersistentProperty property, String...params) {
|
||||
@@ -177,4 +206,52 @@ public class Group {
|
||||
public Iterable<Relationship> getPeopleRelationships() {
|
||||
return peopleRelationships;
|
||||
}
|
||||
|
||||
public Collection<String> getRoleNamesColl() {
|
||||
return roleNamesColl;
|
||||
}
|
||||
|
||||
public void setRoleNamesColl(Collection<String> roleNamesColl) {
|
||||
this.roleNamesColl = roleNamesColl;
|
||||
}
|
||||
|
||||
public Collection<Role> getRolesColl() {
|
||||
return rolesColl;
|
||||
}
|
||||
|
||||
public void setRolesColl(Collection<Role> rolesColl) {
|
||||
this.rolesColl = rolesColl;
|
||||
}
|
||||
|
||||
public Set<String> getRoleNamesSet() {
|
||||
return roleNamesSet;
|
||||
}
|
||||
|
||||
public void setRoleNamesSet(Set<String> roleNamesSet) {
|
||||
this.roleNamesSet = roleNamesSet;
|
||||
}
|
||||
|
||||
public Set<Role> getRolesSet() {
|
||||
return rolesSet;
|
||||
}
|
||||
|
||||
public void setRolesSet(Set<Role> rolesSet) {
|
||||
this.rolesSet = rolesSet;
|
||||
}
|
||||
|
||||
public Iterable<String> getRoleNamesIterable() {
|
||||
return roleNamesIterable;
|
||||
}
|
||||
|
||||
public void setRoleNamesIterable(Iterable<String> roleNamesIterable) {
|
||||
this.roleNamesIterable = roleNamesIterable;
|
||||
}
|
||||
|
||||
public Iterable<Role> getRolesIterable() {
|
||||
return rolesIterable;
|
||||
}
|
||||
|
||||
public void setRolesIterable(Iterable<Role> rolesIterable) {
|
||||
this.rolesIterable = rolesIterable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,12 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.data.neo4j.aspects.Person.persistedPerson;
|
||||
|
||||
@@ -49,7 +55,7 @@ public class NodeEntityTest extends EntityTestBase {
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testSetProperties() {
|
||||
public void testSetSimpleProperties() {
|
||||
String name = "Michael";
|
||||
int age = 35;
|
||||
short height = 182;
|
||||
@@ -63,6 +69,80 @@ public class NodeEntityTest extends EntityTestBase {
|
||||
assertEquals((Short)height, p.getHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testArrayProperties() {
|
||||
Group g = new Group().persist();
|
||||
final String[] roleNames = {"a", "b", "c"};
|
||||
g.setRoleNames(roleNames);
|
||||
assertArrayEquals(roleNames, (String[])getNodeState(g).getProperty("roleNames"));
|
||||
assertArrayEquals(roleNames, g.getRoleNames());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testConvertedArrayProperties() {
|
||||
Group g = new Group().persist();
|
||||
g.setRoles(Group.Role.values());
|
||||
assertArrayEquals(new String[] {"ADMIN","USER"}, (String[])getNodeState(g).getProperty("roles"));
|
||||
assertArrayEquals(Group.Role.values(), g.getRoles());
|
||||
}
|
||||
@Test
|
||||
@Transactional
|
||||
public void testCollectionProperties() {
|
||||
Group g = new Group().persist();
|
||||
final List<String> roleNames = asList("a", "b", "c");
|
||||
g.setRoleNamesColl(roleNames);
|
||||
assertArrayEquals(roleNames.toArray(), (String[])getNodeState(g).getProperty("roleNamesColl"));
|
||||
assertEquals(roleNames, g.getRoleNamesColl());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testConvertedCollectionProperties() {
|
||||
Group g = new Group().persist();
|
||||
g.setRolesColl(asList(Group.Role.values()));
|
||||
assertArrayEquals(new String[] {"ADMIN","USER"}, (String[])getNodeState(g).getProperty("rolesColl"));
|
||||
assertEquals(asList(Group.Role.values()), g.getRolesColl());
|
||||
}
|
||||
@Test
|
||||
@Transactional
|
||||
public void testIterableProperties() {
|
||||
Group g = new Group().persist();
|
||||
final List<String> roleNames = asList("a", "b", "c");
|
||||
g.setRoleNamesIterable(roleNames);
|
||||
assertArrayEquals(roleNames.toArray(), (String[])getNodeState(g).getProperty("roleNamesIterable"));
|
||||
assertEquals(roleNames, g.getRoleNamesIterable());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testConvertedIterableProperties() {
|
||||
Group g = new Group().persist();
|
||||
g.setRolesIterable(asList(Group.Role.values()));
|
||||
assertArrayEquals(new String[] {"ADMIN","USER"}, (String[])getNodeState(g).getProperty("rolesIterable"));
|
||||
assertEquals(asList(Group.Role.values()), g.getRolesIterable());
|
||||
}
|
||||
@Test
|
||||
@Transactional
|
||||
public void testSetProperties() {
|
||||
Group g = new Group().persist();
|
||||
final Set<String> roleNames = new LinkedHashSet<String>(asList("a", "b", "c"));
|
||||
g.setRoleNamesSet(roleNames);
|
||||
assertArrayEquals(roleNames.toArray(), (String[])getNodeState(g).getProperty("roleNamesSet"));
|
||||
assertEquals(roleNames, g.getRoleNamesSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testConvertedSetProperties() {
|
||||
Group g = new Group().persist();
|
||||
final LinkedHashSet<Group.Role> roles = new LinkedHashSet<Group.Role>(asList(Group.Role.values()));
|
||||
g.setRolesSet(roles);
|
||||
assertArrayEquals(new String[] {"ADMIN","USER"}, (String[])getNodeState(g).getProperty("rolesSet"));
|
||||
assertEquals(roles, g.getRolesSet());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void testSetShortProperty() {
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<dependency>
|
||||
<groupId>org.neo4j</groupId>
|
||||
<artifactId>neo4j-rest-graphdb</artifactId>
|
||||
<version>1.5.M02.U1</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.neo4j</groupId>
|
||||
|
||||
@@ -18,7 +18,6 @@ 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.Neo4jPersistentProperty;
|
||||
import org.springframework.data.neo4j.support.Neo4jTemplate;
|
||||
|
||||
@@ -44,7 +43,7 @@ public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessor
|
||||
@Override
|
||||
public boolean accept(final Neo4jPersistentProperty property) {
|
||||
final ConversionService conversionService = getConversionService();
|
||||
return property.isSerializableField(conversionService) && property.isDeserializableField(conversionService);
|
||||
return property.isSerializablePropertyField(conversionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,28 +53,31 @@ public class ConvertingNodePropertyFieldAccessorFactory implements FieldAccessor
|
||||
|
||||
public static class ConvertingNodePropertyFieldAccessor extends PropertyFieldAccessorFactory.PropertyFieldAccessor {
|
||||
|
||||
private final Class<String> targetType = String.class;
|
||||
private final PropertyConverter propertyConverter;
|
||||
|
||||
public ConvertingNodePropertyFieldAccessor(Neo4jPersistentProperty property, Neo4jTemplate template) {
|
||||
super(template, property);
|
||||
propertyConverter = new PropertyConverter(template.getConversionService(),property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setValue(final Object entity, final Object newVal) {
|
||||
super.setValue(entity, serializePropertyValue(newVal));
|
||||
super.setValue(entity, propertyConverter.serializePropertyValue(newVal,targetType));
|
||||
return newVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object doGetValue(final Object entity) {
|
||||
return deserializePropertyValue(super.doGetValue(entity));
|
||||
return propertyConverter.deserializePropertyValue(super.doGetValue(entity));
|
||||
}
|
||||
|
||||
private Object serializePropertyValue(final Object newVal) {
|
||||
return template.getConversionService().convert(newVal, String.class);
|
||||
@Override
|
||||
protected Object convertSimplePropertyValue(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
private Object deserializePropertyValue(final Object value) {
|
||||
return template.getConversionService().convert(value, fieldType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* 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.fieldaccess;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* @author mh
|
||||
* @since 05.11.11
|
||||
*/
|
||||
public class PropertyConverter {
|
||||
private final ConversionService conversionService;
|
||||
private final Neo4jPersistentProperty property;
|
||||
private final TypeInformation<?> typeInformation;
|
||||
|
||||
public PropertyConverter(ConversionService conversionService, Neo4jPersistentProperty property) {
|
||||
this.conversionService = conversionService;
|
||||
this.property = property;
|
||||
this.typeInformation = property.getTypeInformation();
|
||||
}
|
||||
|
||||
public Object serializePropertyValue(final Object newVal, Class<?> targetType) {
|
||||
if (newVal == null) return null;
|
||||
final TypeInformation<?> typeInformation = property.getTypeInformation();
|
||||
if (typeInformation.isCollectionLike()) {
|
||||
return serializeCollection(newVal, conversionService, typeInformation, targetType);
|
||||
}
|
||||
return conversionService.convert(newVal, targetType);
|
||||
}
|
||||
|
||||
public Object deserializePropertyValue(final Object newVal) {
|
||||
if (newVal == null) return null;
|
||||
if (typeInformation.isCollectionLike() && isCollectionLike(newVal)) {
|
||||
return deserializeCollection(newVal, conversionService, typeInformation);
|
||||
}
|
||||
return conversionService.convert(newVal, typeInformation.getType());
|
||||
}
|
||||
|
||||
private boolean isCollectionLike(Object val) {
|
||||
return val != null && (val.getClass().isArray() || Collection.class.isAssignableFrom(val.getClass()));
|
||||
}
|
||||
|
||||
private Object serializeCollection(Object newVal, ConversionService conversionService, TypeInformation<?> typeInformation, final Class<?> targetType) {
|
||||
final List<Object> values = convertCollection(conversionService, targetType, toCollection(newVal));
|
||||
return values.toArray((Object[]) Array.newInstance(targetType, values.size()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object deserializeCollection(Object newVal, ConversionService conversionService, TypeInformation<?> typeInformation) {
|
||||
final Class<?> actualType = typeInformation.getActualType().getType();
|
||||
final List<Object> result = convertCollection(conversionService, actualType, toCollection(newVal));
|
||||
final Class<?> fieldType = typeInformation.getType();
|
||||
if (fieldType.isArray()) {
|
||||
return result.toArray((Object[]) Array.newInstance(actualType, result.size()));
|
||||
}
|
||||
if (Set.class.isAssignableFrom(fieldType)) return new LinkedHashSet<Object>(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Object> convertCollection(ConversionService conversionService, Class<?> targetType, Iterable<?> values) {
|
||||
final List<Object> result = new ArrayList<Object>();
|
||||
for (Object value : values) {
|
||||
result.add(conversionService.convert(value, targetType));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Iterable<?> toCollection(Object newVal) {
|
||||
if (newVal.getClass().isArray()) {
|
||||
return asList((Object[]) newVal);
|
||||
} else {
|
||||
return (Iterable<?>) newVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,14 +84,18 @@ public class PropertyFieldAccessorFactory implements FieldAccessorFactory {
|
||||
if (element.hasProperty(propertyName)) {
|
||||
Object value = element.getProperty(propertyName);
|
||||
if (value == null || fieldType.isInstance(value)) return value;
|
||||
if (template.getConversionService() !=null) {
|
||||
return template.getConversionService().convert(value, fieldType);
|
||||
}
|
||||
return value;
|
||||
return convertSimplePropertyValue(value);
|
||||
}
|
||||
return getDefaultValue(fieldType);
|
||||
}
|
||||
|
||||
protected Object convertSimplePropertyValue(Object value) {
|
||||
if (template.getConversionService() !=null) {
|
||||
return template.getConversionService().convert(value, fieldType);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private Object getDefaultValue(final Class<?> type) {
|
||||
if (type.isPrimitive()) {
|
||||
if (type.equals(boolean.class)) return false;
|
||||
|
||||
@@ -52,11 +52,7 @@ public interface Neo4jPersistentProperty extends PersistentProperty<Neo4jPersist
|
||||
|
||||
String getNeo4jPropertyName();
|
||||
|
||||
boolean isSimpleValueField();
|
||||
|
||||
boolean isSerializableField(final ConversionService conversionService);
|
||||
|
||||
boolean isDeserializableField(final ConversionService conversionService);
|
||||
boolean isSerializablePropertyField(final ConversionService conversionService);
|
||||
|
||||
boolean isNeo4jPropertyType();
|
||||
|
||||
|
||||
@@ -147,16 +147,16 @@ class Neo4jPersistentPropertyImpl extends AbstractPersistentProperty<Neo4jPersis
|
||||
}
|
||||
|
||||
|
||||
public boolean isSimpleValueField() {
|
||||
return !(getTypeInformation().isCollectionLike() || isRelationship());
|
||||
public boolean isSerializablePropertyField(final ConversionService conversionService) {
|
||||
if (isRelationship()) return false;
|
||||
final Class<?> type = getType();
|
||||
final Class<String> targetType = String.class;
|
||||
if (getTypeInformation().isCollectionLike()) return isConvertibleBetween(conversionService, getComponentType(), targetType);
|
||||
return isConvertibleBetween(conversionService, type, targetType);
|
||||
}
|
||||
|
||||
public boolean isSerializableField(final ConversionService conversionService) {
|
||||
return isSimpleValueField() && conversionService.canConvert(getType(), String.class);
|
||||
}
|
||||
|
||||
public boolean isDeserializableField(final ConversionService conversionService) {
|
||||
return isSimpleValueField() && conversionService.canConvert(String.class, getType());
|
||||
private boolean isConvertibleBetween(ConversionService conversionService, Class<?> type, Class<String> targetType) {
|
||||
return conversionService.canConvert(type, targetType) && conversionService.canConvert(targetType, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user