javaneo4jspring-dataspring-data-neo4jgraphaware

How to I configure my own GraphDatabaseService and GraphAwareRuntime in a spring unit test with boot 2.0 and Neo4j SDN5


I'm writing some unit tests and want to use TimeTree along with Spring repositories, to auto attach event nodes to a time tree. Something like this issue, but I'm using boot 2.0 and SDN5. I think my main issue is I don't know how to set up the configuration so my repositories and my TimeTree use the same GraphDatabaseService. My @Configuration is like this:

@Configuration
public class SpringConfig {

    @Bean
    public SessionFactory sessionFactory() {
        // with domain entity base package(s)
        return new SessionFactory(configuration(), "org.neo4j.boot.test.domain");
    }

    @Bean
    public org.neo4j.ogm.config.Configuration configuration() {
        return new org.neo4j.ogm.config.Configuration.Builder()
            .uri("bolt://localhost")
            .build();
    }

    @Bean
    public Session getSession() {
        return sessionFactory().openSession();
    }

    @Bean
    public GraphDatabaseService graphDatabaseService() {
        return new GraphDatabaseFactory()
            .newEmbeddedDatabase(new File("/tmp/graphDb"));
    }

    @Bean
    public GraphAwareRuntime graphAwareRuntime() {
        GraphDatabaseService graphDatabaseService = graphDatabaseService();
        GraphAwareRuntime runtime = GraphAwareRuntimeFactory
            .createRuntime(graphDatabaseService);

        runtime.registerModule(new TimeTreeModule("timetree",
            TimeTreeConfiguration
                .defaultConfiguration()
                .withAutoAttach(true)
                .with(new NodeInclusionPolicy() {
                    @Override
                    public Iterable<Node> getAll(GraphDatabaseService graphDatabaseService) {
                        return null;
                    }

                    @Override
                    public boolean include(Node node) {
                        return node.hasLabel(Label.label("User"));
                    }
                })
                .withRelationshipType(RelationshipType.withName("CREATED_ON"))
                .withTimeZone(DateTimeZone.forTimeZone(TimeZone.getTimeZone("GMT+1")))
                .withTimestampProperty("createdOn")
                .withResolution(Resolution.DAY)
//                      .withCustomTimeTreeRootProperty("timeTreeName")
                .withResolution(Resolution.HOUR), graphDatabaseService));
        runtime.start();
        return runtime;
    }
}

And my test looks like this:

User user = new User("Michal");
user.setCreatedOn(1431937636995l);
userRepository.save(user);

GraphUnit.assertSameGraph(graphDb, "CREATE (u:User {name:'Michal', createdOn:1431937636995})," +
    "(root:TimeTreeRoot)," +
    "(root)-[:FIRST]->(year:Year {value:2015})," +
    "(root)-[:CHILD]->(year)," +
    "(root)-[:LAST]->(year)," +
    "(year)-[:FIRST]->(month:Month {value:5})," +
    "(year)-[:CHILD]->(month)," +
    "(year)-[:LAST]->(month)," +
    "(month)-[:FIRST]->(day:Day {value:18})," +
    "(month)-[:CHILD]->(day)," +
    "(month)-[:LAST]->(day)," +
    "(day)<-[:CREATED_ON]-(u)"
);

GraphUnit.printGraph(graphDb);
graphDb.shutdown();

There's a host of errors, but I think they all stem from this one:

Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to 
instantiate [org.springframework.data.repository.support.Repositories]: 
Factory method 'repositories' threw exception; nested exception is 
org.springframework.beans.factory.UnsatisfiedDependencyException: Error 
creating bean with name 'userRepository': Unsatisfied dependency 
expressed through method 'setSession' parameter 0; nested exception is 
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No 
qualifying bean of type 'org.neo4j.ogm.session.Session' available: 
expected single matching bean but found 2: getSession,
org.springframework.data.neo4j.transaction.SharedSessionCreator#0

Solution

  • It is because the configuration class redefines some beans already automatically configured by Spring boot (here the Session).

    So spring injection does not know how to choose between the 2. Removing the getSession() should help.

    A second thing is that your SessionFactory has to use the embedded DB setup in the graphDatabaseService() method. For this, configure an embedded driver with the existing database.

    Summary config that should work fine for you :

    @Bean
    public SessionFactory sessionFactory() {
        EmbeddedDriver driver = new EmbeddedDriver(graphDatabaseService());
        return new SessionFactory(driver, "org.neo4j.boot.test.domain");
    }
    
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new Neo4jTransactionManager(sessionFactory());
    }
    
    @Bean
    public GraphDatabaseService graphDatabaseService() {
        return new TestGraphDatabaseFactory().newImpermanentDatabaseBuilder().newGraphDatabase();
    }
    
    @Bean
    public GraphAwareRuntime graphAwareRuntime() {
        ...