DATAGRAPH-241 - base-package for entities, fix for alias lookup of loaded but not registered entities

This commit is contained in:
Michael Hunger
2012-06-04 10:04:13 +02:00
parent 66d2fd6ec2
commit cbd00de1d2
12 changed files with 250 additions and 11 deletions

View File

@@ -6,7 +6,7 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd
http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

View File

@@ -16,23 +16,30 @@
package org.springframework.data.neo4j.config;
import antlr.StringUtils;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.*;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.util.ClassUtils;
import org.w3c.dom.Element;
import java.util.Set;
import static org.springframework.util.StringUtils.hasText;
public class DataGraphBeanDefinitionParser extends AbstractBeanDefinitionParser {
private static final String GRAPH_DATABASE_SERVICE = "graphDatabaseService";
private static final String BASE_PACKAGE = "base-package";
public static final String ASPECTJ_CONFIG = "org.springframework.data.neo4j.aspects.config.Neo4jAspectConfiguration";
public static final String CROSS_STORE_CONFIG = "org.springframework.data.neo4j.cross_store.config.CrossStoreNeo4jConfiguration";
@@ -41,10 +48,40 @@ public class DataGraphBeanDefinitionParser extends AbstractBeanDefinitionParser
BeanDefinitionBuilder configBuilder = createConfigurationBeanDefinition(element);
setupGraphDatabase(element, context, configBuilder);
setupEntityManagerFactory(element, configBuilder);
setupBaseEntities(element, configBuilder);
setupConfigurationClassPostProcessor(context);
return getSourcedBeanDefinition(configBuilder, element, context);
}
private void setupBaseEntities(Element element, BeanDefinitionBuilder configBuilder) {
Set<String> initialEntityClasses = getInitialEntityClasses(element);
if (initialEntityClasses!=null) {
configBuilder.addPropertyValue("initialEntitySet", initialEntityClasses);
}
}
public Set<String> getInitialEntityClasses(Element element) {
String basePackage = element.getAttribute(BASE_PACKAGE);
if (!hasText(basePackage)) {
return null;
}
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(NodeEntity.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(RelationshipEntity.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
Set<String> classes = new ManagedSet<String>();
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
classes.add(candidate.getBeanClassName());
}
return classes;
}
private BeanDefinitionBuilder createConfigurationBeanDefinition(Element element) {
BeanDefinitionBuilder configBuilder = createConfigBuilderByMode(element);

View File

@@ -65,6 +65,9 @@ import org.springframework.transaction.jta.UserTransactionAdapter;
import javax.transaction.TransactionManager;
import javax.validation.Validator;
import java.util.Collection;
import java.util.Set;
import static java.util.Arrays.asList;
/**
@@ -79,6 +82,8 @@ public abstract class Neo4jConfiguration {
private ConversionService conversionService;
private Set<? extends Class<?>> initialEntitySet;
@Autowired(required = false)
private Validator validator;
@@ -197,6 +202,9 @@ public abstract class Neo4jConfiguration {
@Bean
public Neo4jMappingContext neo4jMappingContext() throws Exception {
final Neo4jMappingContext mappingContext = new Neo4jMappingContext();
if (initialEntitySet!=null) {
mappingContext.setInitialEntitySet(initialEntitySet);
}
mappingContext.setEntityAlias(entityAlias());
return mappingContext;
}
@@ -275,4 +283,11 @@ public abstract class Neo4jConfiguration {
return new IndexProviderImpl(neo4jMappingContext(), graphDatabase());
}
public Set<? extends Class<?>> getInitialEntitySet() {
return initialEntitySet;
}
public void setInitialEntitySet(Set<? extends Class<?>> initialEntitySet) {
this.initialEntitySet = initialEntitySet;
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.neo4j.support.mapping;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationListener;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.event.MappingContextEvent;
@@ -23,13 +24,17 @@ import org.springframework.data.neo4j.mapping.Neo4jPersistentProperty;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.data.neo4j.support.index.IndexType;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* @author mh
* @since 12.04.12
*/
public class IndexCreationMappingEventListener implements ApplicationListener<MappingContextEvent<Neo4jPersistentEntity<?>, Neo4jPersistentProperty>> {
public class IndexCreationMappingEventListener implements ApplicationListener<MappingContextEvent<Neo4jPersistentEntity<?>, Neo4jPersistentProperty>>, InitializingBean {
private Neo4jTemplate template;
private Queue<Neo4jPersistentEntity> initialEntities=new ConcurrentLinkedQueue<Neo4jPersistentEntity>();
private boolean isInitialized=false;
public IndexCreationMappingEventListener(Neo4jTemplate template) {
this.template = template;
}
@@ -37,8 +42,16 @@ public class IndexCreationMappingEventListener implements ApplicationListener<Ma
@Override
public void onApplicationEvent(MappingContextEvent<Neo4jPersistentEntity<?>, Neo4jPersistentProperty> event) {
if (!(event.getSource() instanceof Neo4jPersistentEntity)) return;
final Neo4jPersistentEntity entity = event.getPersistentEntity();
if (!isInitialized) {
initialEntities.add(entity);
}
else {
ensureEntityIndexes(entity);
}
}
private void ensureEntityIndexes(Neo4jPersistentEntity entity) {
final Class entityType = entity.getType();
template.getIndex(entityType, null, IndexType.SIMPLE);
entity.doWithProperties(new PropertyHandler<Neo4jPersistentProperty>() {
@@ -50,4 +63,13 @@ public class IndexCreationMappingEventListener implements ApplicationListener<Ma
}
});
}
@Override
public void afterPropertiesSet() throws Exception {
isInitialized = true;
for (Neo4jPersistentEntity entity : initialEntities) {
ensureEntityIndexes(entity);
}
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.data.neo4j.support.mapping;
import org.neo4j.graphdb.PropertyContainer;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
@@ -93,6 +94,36 @@ public class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersistentE
for (Neo4jPersistentEntityImpl<?> entity : getPersistentEntities()) {
if (entity.matchesAlias(alias)) return entity;
}
return tryToResolveAliasAsEntityClassName(alias);
}
@Override
public void afterPropertiesSet() {
try {
super.afterPropertiesSet();
} catch(Exception e) {
e.printStackTrace();
}
}
private Neo4jPersistentEntity<?> tryToResolveAliasAsEntityClassName(Object alias) {
if (alias instanceof Class) {
try {
return getPersistentEntity((Class)alias);
} catch(MappingException me) {
// ignores
}
}
if (alias instanceof String && alias.toString().contains(".")) {
try {
return tryToResolveAliasAsEntityClassName(Class.forName(alias.toString()));
} catch (ClassNotFoundException cnfe) {
// ignore
}
}
if (alias instanceof StoredEntityType) {
return ((StoredEntityType)alias).getEntity();
}
return null;
}

View File

@@ -18,9 +18,29 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="storeDirectory" type="xsd:string" />
<xsd:attribute name="storeDirectory" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
default store-directory of the Neo4j database
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="graphDatabaseService" type="graphDatabaseServiceRef" />
<xsd:attribute name="entityManagerFactory" type="xsd:string" />
<xsd:attribute name="base-package" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
base package for persistent entities.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="entityManagerFactory" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
entity manager factory bean reference for cross-store configuration
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>

View File

@@ -31,13 +31,17 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.data.neo4j.model.PersonRepository;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import org.springframework.data.neo4j.support.mapping.Neo4jMappingContext;
import org.springframework.data.neo4j.support.mapping.Neo4jPersistentEntityImpl;
import org.springframework.transaction.PlatformTransactionManager;
import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author mh
@@ -53,6 +57,8 @@ public class DataGraphNamespaceHandlerTest {
Neo4jTemplate neo4jTemplate;
@Autowired
PlatformTransactionManager transactionManager;
@Autowired
Neo4jMappingContext mappingContext;
@Autowired(required = false)
PersonRepository personRepository;
}
@@ -96,6 +102,13 @@ public class DataGraphNamespaceHandlerTest {
assertInjected("-code");
}
@Test
public void injectionForBasePackageOfEntities() {
Config config = assertInjected("-entities");
Collection<Neo4jPersistentEntityImpl<?>> entities = config.mappingContext.getPersistentEntities();
System.out.println(entities);
assertTrue(entities.size() > 0);
}
@Test
public void injectionForConversionService() {
final Config config = assertInjected("-conversion");
final ConversionService conversionService = config.neo4jTemplate.getConversionService();

View File

@@ -16,6 +16,7 @@
package org.springframework.data.neo4j.model;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.Indexed;
import org.springframework.data.neo4j.annotation.NodeEntity;
@@ -25,6 +26,8 @@ import org.springframework.data.neo4j.annotation.NodeEntity;
*/
@NodeEntity
public class Attribute<T> {
private @GraphId Long id;
@Indexed
T value;

View File

@@ -21,8 +21,9 @@ import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
@NodeEntity
public abstract class Car {
public class Car {
@GraphId
public
Long id;
public Car() {
}

View File

@@ -0,0 +1,70 @@
/**
* 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.repository;
import org.junit.Ignore;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.impl.util.FileUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.neo4j.model.Car;
import org.springframework.data.neo4j.model.Volvo;
import org.springframework.data.neo4j.support.Neo4jTemplate;
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.assertEquals;
@Ignore
public class ReadWriteTest {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:ReadWriteTest-context.xml");
boolean delete = false;
try {
Neo4jTemplate template = ctx.getBean(Neo4jTemplate.class);
Car car = findOne(template);
if (car != null) {
delete = true;
assertEquals(Volvo.class, car.getClass());
} else {
Transaction tx = template.beginTx();
Volvo volvo = template.save(new Volvo());
assertEquals(1, volvo.id.intValue());
tx.success();
tx.finish();
}
} catch (Exception e) {
e.printStackTrace();
delete = true;
throw new RuntimeException(e);
} finally {
ctx.close();
if (delete) {
FileUtils.deleteRecursively(new File("target/read-write.db"));
}
}
}
private static Car findOne(Neo4jTemplate template) {
try {
return template.findOne(1, Car.class);
} catch (DataRetrievalFailureException nfe) {
return null;
}
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd">
<context:annotation-config/>
<neo4j:config storeDirectory="target/read-write.db"/>
<neo4j:repositories base-package="org.springframework.data.neo4j.repository"/>
</beans>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd">
<context:annotation-config/>
<neo4j:config storeDirectory="target/config-test" base-package="org.springframework.data.neo4j.model"/>
<bean id="config" class="org.springframework.data.neo4j.config.DataGraphNamespaceHandlerTest$Config"/>
<neo4j:repositories base-package="org.springframework.data.neo4j.model"/>
</beans>