Spring Boot with Neo4j & MySQL
By: Tyler Bobella | Updated on: February 01, 2017
Our customers use LogicGate to build complex process applications that link organizational hierarchies, assets, and compliance requirements across the enterprise. The dynamic nature of the platform (giving users the ability to customize objects and their attributes, workflow, etc.) can be supported by a relational database, to a point, using an entity-attribute-value model. However, for complex processes with recursively linked entities, this relational model restricts insight across deeply linked assets.
How do we access these recursively linked entities? Answer: Neo4j.
Neo4j uses nodes and relationships instead of tables and join columns. Nodes store a small amount of data where the majority of the data stored are in the relationships between the nodes. This allows for large scale traversals of recursively linked entities to be done with ease.
After scouring the Internet for resources on how to use Neo4j with another datasource I struggled with a large volume of out-dated resources. With lots of help from the Neo4j slack channel I was able to get a MySQL datasource and a Neo4j datasource running together in the same application. In this post I will explain how to configure all of it. Enjoy!
Graph database + Relational database = < 3
Neo4j 4.1.6 is the last iteration before 4.2.0 which was officially released on Jan. 25th, 2017. One would say, “Why not just use 4.2.0?” Well, 4.2.0 requires Spring Boot 1.5.0 which does not have a release version just yet. So let’s focus on the latest Neo4j release version and Spring Boot 1.4.X.
Firstly, install Neo4j. Follow the instructions found on this page. If on a Mac simply run brew install neo4j
. When Neo4j is done installing run neo4j start
in terminal to start up the database. That is all that is needed to install Neo4j.
Let’s dive into the Spring Boot portion. Open build.gradle
file and add the following dependencies:
compile "org.springframework.data:spring-data-neo4j-rest:3.4.6.RELEASE" compile "org.springframework.data:spring-data-neo4j:4.1.6.RELEASE" compile "org.neo4j:neo4j-ogm-core:2.0.6" compile "org.neo4j:neo4j-ogm-http-driver:2.0.6"
For this use case, the communication method to the Neo4j database has to be a RESTful call. To achieve this the HTTP driver can be used. There are two other driver options: Bolt and Embedded. This post will focus on using the HTTP driver.
Refresh the gradle dependencies by running ./gradlew clean build
in the root directory of theSpring Boot project. After this, we can start configuring the application.
We will need to edit existing / add new annotations within the Java file that contains the application configuration.
Application Class Annotations
@ComponentScan(values = {"com.example"})
This tells Spring Boot to scan all project packages.com.example
holds all the classes that pertain to both relational and graph databases. This includes @Controller, @Service, @Entity, and @Repository
.
@EnableAutoConfiguration(exclude = {Neo4jDataAutoConfiguration.class, DataSourceAutoConfiguration.class})
This explicitly tell Spring Boot how to set up our datasources. This is why Neo4jDataAutoConfiguration.class
and DataSourceAutoConfiguration.cass
are excluded.
Currently the application class should look like the following:
package com.example; import ... @Configuration @ComponentScan(values = {"com.example"}) @EnableAutoConfiguration(exclude = {Neo4jDataAutoConfiguration.class, DataSourceAutoConfiguration.class}) public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
Datasource Configuration Class
The next step will be to create a configuration file that will configures both the MySQL and Neo4j databases. The annotations for this class file are the following:
@Configuration @EnableNeo4jRepositories(basePackages = "com.example.graph") @EnableJpaRepositories(basePackages = "com.example.relational") @EnableTransactionManagement
@Configuration
annotation tells Spring that “This is a configuration file please load it!”. This will generate bean definitions at runtime@EnableNeo4jRepositories(basePackages = "com.example.graph)
will tell Spring Boot to enable all repositories under the packagecom.example.graph
to be a neo4j graph repository@EnableJpaRepositories(basePackages = "com.example.relational")
will tell Spring Boot to enable all repositories under the packagecom.example.relational
to be relational repositories.@EnableTransactionManagement
allows us to use annotation-driven transaction management
Now that annotations are set up let’s beginning building out our configuration class.
public class DatasourceConfig extends Neo4jConfiguration
Our class needs to extend Neo4jConfiguration so configuration for Neo4j settings can be set explicitly.
Next, create a configuration bean that will configure the Neo4j database.
@Bean public org.neo4j.ogm.config.Configuration getConfiguration() { org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration(); config .driverConfiguration() .setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver") .setURI("http://YOUR_USERNAME:YOUR_PASSWORD@localhost:7474"); return config; }
This method wires up the Neo4j database with Spring Boot. Setting the location of the database with a username and password and we also state which driver we are using. In this case, using the HttpDriver
.
The next bean sets the configuration settings in the Neo4j session that is used to interact with the Neo4j database.
@Bean public SessionFactory getSessionFactory() { return new SessionFactory(getConfiguration(), "com.example.graph"); }
Another Neo4j bean that needs to be configured is the getSession
bean. This allows Neo4j to integrate with the Spring Boot application.
@Bean public Session getSession() throws Exception { return super.getSession(); }
Now that Neo4j is almost taken care of let’s set up the relational datasource. In this case, MySQL is used. To achieve this, creating a datasource bean as well as a entity manager bean is needed.
@Primary @Bean(name = "dataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource() { return DataSourceBuilder .create() .driverClassName("com.mysql.jdbc.Driver") .build(); } @Primary @Bean @Autowired public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean(); entityManagerFactory.setDataSource(dataSource); entityManagerFactory.setPackagesToScan("com.example.core"); entityManagerFactory.setJpaDialect(new HibernateJpaDialect()); Map<String, String> jpaProperties = new HashMap<>(); jpaProperties.put("hibernate.connection.charSet", "UTF-8"); jpaProperties.put("spring.jpa.hibernate.ddl-auto", "none"); jpaProperties.put("spring.jpa.hibernate.naming-strategy", "org.springframework.boot.orm.jpa.SpringNamingStrategy"); jpaProperties.put("hibernate.bytecode.provider", "javassist"); jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect"); jpaProperties.put("hibernate.hbm2ddl.auto", "none"); jpaProperties.put("hibernate.order_inserts", "true"); jpaProperties.put("hibernate.jdbc.batch_size", "50"); entityManagerFactory.setJpaPropertyMap(jpaProperties); entityManagerFactory.setPersistenceProvider(new HibernatePersistenceProvider()); return entityManagerFactory; }
These beans are declared primary because the MySQL database should take precedence over the Neo4j database.
The JPA properties can be tweaked to your liking as well!
The last thing that needs to set up are the transaction managers. These manage the transactions for the relational database, Neo4j database, and then the manager for the overall application.
@Autowired @Bean(name = "neo4jTransactionManager") public Neo4jTransactionManager neo4jTransactionManager(Session sessionFactory) { return new Neo4jTransactionManager(sessionFactory); } @Autowired @Primary @Bean(name = "mysqlTransactionManager") public JpaTransactionManager mysqlTransactionManager(LocalContainerEntityManagerFactoryBean entityManagerFactory) throws Exception { return new JpaTransactionManager(entityManagerFactory.getObject()); } @Autowired @Bean(name = "transactionManager") public PlatformTransactionManager transactionManager(Neo4jTransactionManager neo4jTransactionManager, JpaTransactionManager mysqlTransactionManager) { return new ChainedTransactionManager( mysqlTransactionManager, neo4jTransactionManager ); }
The ChainedTransactionManager
allows for multiple transaction managers. This means that any transaction that occurs will be delegated to each manager. If the first manager fails, the second manager will then be invoked.
I have created a repository with a demo application that can be found on GitHub.
That’s it! The application now has access to both MySQL and Neo4j! Like / comment. All constructive criticism welcomed!
This is my first blog post ever! Wahoo!