Bootstrapping Neo4j With Spring-Data – Without XML

With the maturing of Spring-Data I started porting all my personal projects to use Spring Data for bootstrapping. I also wanted to get rid of XML, which proved a little more tricky as I expected.

Dependencies

Let’s start with the required dependencies:

Update: Added a missing validator-dependency

 <properties>
 <spring-core.version>3.2.0.RELEASE</spring-core.version>
 </properties>
 <dependencies>
 <dependency>
 <groupId>org.springframework.data</groupId>
 <artifactId>spring-data-neo4j</artifactId>
 <version>2.2.1.RELEASE</version>
 <exclusions>
 <exclusion>
 <groupId>org.springframework</groupId>
 <artifactId>spring-asm</artifactId>
 </exclusion>
 </exclusions>
 </dependency>
 <dependency>
 <groupId>org.neo4j.app</groupId>
 <artifactId>neo4j-server</artifactId>
 <version>1.8.1</version>
 </dependency>
 <dependency>
 <groupId>org.neo4j.app</groupId>
 <artifactId>neo4j-server</artifactId>
 <classifier>static-web</classifier>
 <version>1.8.1</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-core</artifactId>
 <version>${spring-core.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context</artifactId>
 <version>${spring-core.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-aop</artifactId>
 <version>${spring-core.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-aspects</artifactId>
 <version>${spring-core.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-beans</artifactId>
 <version>${spring-core.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-expression</artifactId>
 <version>${spring-core.version}</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
 <version>${spring-core.version}</version>
 </dependency>
 <dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>2.2.2</version>
 </dependency>
 </dependencies>
 <dependency>
 <groupId>org.hibernate</groupId>
 <artifactId>hibernate-validator</artifactId>
 <version>4.3.0.Final</version>
 </dependency>

We need/want to override the spring dependencies pulled in via Spring Data with a newer one. We also need to explicitly remove spring-asm as newer versions of Spring don’t need it.

The neo4j-dependencies are required to give us the Neo4j-WebServer.

Repositories

I created a small demo-Entity based on the Neo4j-Annotation provided by Spring-Data.

 @NodeEntity
 public class Component {
    @GraphId Long id;
    @Indexed(unique = true) String simpleName;
    @RelatedTo(type = “Component”) Set<Component> relatedTo = new HashSet<Component>();
 …
 …
 }

Next I added a repository to handle the class (oh, the beauty of Spring-Data-Repositories).

public interface ComponentRepository extends GraphRepository<Component>{
}

That’s it.

Configuration

To get it all up and running the only thing required is @Configuration-Annotated class which extends Neo4jConfiguration.

@EnableTransactionManagement
@Configuration
@EnableNeo4jRepositories(basePackages = “de.codepitbull.neo4j”)
public class CustomNeo4jConfig extends Neo4jConfiguration {
    private static final String DB_PATH = "target/neo4j";
    @Bean
    public EmbeddedGraphDatabase graphDatabaseService() {
        return new EmbeddedGraphDatabase(DB_PATH);
    }
    @Bean
    public WrappingNeoServerBootstrapper neo4jWebServer() {
        WrappingNeoServerBootstrapper server = new WrappingNeoServerBootstrapper(graphDatabaseService());
        server.start();
        return server;
    }
}

Using this config we are getting not only an embedded Neo4J instance but also a nice query-interface (including the Neo4J-Shell) running at localhost:7474.
Nice, that’s it 🙂

Create a main-Class, fire up the context and see the magic happen.

public class Main {
     public static void main(String[] args) {
         AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CustomNeo4jConfig.class);
         context.start();
         context.getBean(WrappingNeoServerBootstrapper.class);
         context.registerShutdownHook();
     }
}

Using Converters

After playing around with Neo4J you will run into the need for using converters (everything that goes into the graph has to be converted to a String). This proved to be a little more tricky than I had expected.

Normally you only have to register a new ConversionServiceFactoryBean to provide additional back and forth conversions. Registering this been in the same @Configuration we just created will be followed by a “circular dependency”-problem.

I am not sure why this is happening but I simply moved it into a separate @Configuration _ which uses @Import_ to include the Neo4J-Configuration and things started working.

Obviously you will also have to replace CustomNeo4jConfig.class in your Main-class with MainConfiguration.class.

@Configuration
@Import(CustomNeo4jConfig.class)
public class MainConfiguration {

@Bean
public ConversionServiceFactoryBean conversionService() {
    Set converters = Sets.newHashSet();

    converters.add(new ConstructorListConverter());

    ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
    bean.setConverters(converters);
    return bean;
}
}

Conclusion

I love it, nothing more to say.

Advertisements