diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index d4c7b547e5..cf323298cb 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -166,6 +166,11 @@ spring-data-mongodb true + + org.springframework.data + spring-data-neo4j + true + org.springframework.data spring-data-redis diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 9951e96cb1..b6de2d4b7b 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -379,6 +379,17 @@ + + org.springframework.data + spring-data-neo4j + true + + + jcl-over-slf4j + org.slf4j + + + org.springframework.data spring-data-redis @@ -635,5 +646,6 @@ tomcat-embed-jasper test + diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java index 1afa03ff98..7a8630d260 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoRepositoriesAutoConfiguration.java @@ -53,8 +53,7 @@ import org.springframework.data.mongodb.repository.support.MongoRepositoryFactor */ @Configuration @ConditionalOnClass({ Mongo.class, MongoRepository.class }) -@ConditionalOnMissingBean({ MongoRepositoryFactoryBean.class, - MongoRepositoryConfigurationExtension.class }) +@ConditionalOnMissingBean({ MongoRepositoryFactoryBean.class, MongoRepositoryConfigurationExtension.class }) @ConditionalOnProperty(prefix = "spring.data.mongodb.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) @Import(MongoRepositoriesAutoConfigureRegistrar.class) @AutoConfigureAfter(MongoDataAutoConfiguration.class) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jAutoConfiguration.java new file mode 100644 index 0000000000..b5b9b69f1b --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jAutoConfiguration.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012-2015 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.boot.autoconfigure.data.neo4j; + +import org.neo4j.ogm.session.Neo4jSession; +import org.neo4j.ogm.session.Session; +import org.neo4j.ogm.session.SessionFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; + +import org.springframework.data.neo4j.config.Neo4jConfiguration; +import org.springframework.data.neo4j.template.Neo4jOperations; +import org.springframework.data.neo4j.template.Neo4jTemplate; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Neo4j support. + *

+ * Registers a {@link Neo4jTemplate} bean if no other bean of + * the same type is configured. + * + * @author Michael Hunger + * @author Josh Long + * @author Vince Bickers + * @since 1.3.0 + */ +@Configuration +@EnableConfigurationProperties(Neo4jProperties.class) +@ConditionalOnMissingBean(type = "org.springframework.data.neo4j.template.Neo4jOperations") +@ConditionalOnClass({ Neo4jSession.class, Neo4jOperations.class }) +public class Neo4jAutoConfiguration extends Neo4jConfiguration { + + @Autowired + private Neo4jProperties properties; + + @Value("${spring.data.neo4j.domain.packages:null}") + private String[] domainPackages; + + @Bean + @ConditionalOnMissingBean(org.neo4j.ogm.config.Configuration.class) + public org.neo4j.ogm.config.Configuration configuration() { + return this.properties.configure(); + } + + @Override + @ConditionalOnMissingBean(SessionFactory.class) + public SessionFactory getSessionFactory() { + return new SessionFactory(configuration(), this.domainPackages); + } + + @Bean + @ConditionalOnMissingBean(Session.class) + @Scope(value = "${spring.data.neo4j.session.lifetime:session}", proxyMode = ScopedProxyMode.TARGET_CLASS) + public Session getSession() throws Exception { + return getSessionFactory().openSession(); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jProperties.java new file mode 100644 index 0000000000..73b3974883 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jProperties.java @@ -0,0 +1,105 @@ +/* + * Copyright 2012-2015 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.boot.autoconfigure.data.neo4j; + +import org.neo4j.ogm.config.Configuration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for Neo4j. + * + * @author Dave Syer + * @author Phillip Webb + * @author Josh Long + * @author Andy Wilkinson + * @author EddĂș MelĂ©ndez + * @author Michael Hunger + * @author Vince Bickers + */ +@ConfigurationProperties(prefix = "spring.data.neo4j") +public class Neo4jProperties { + + // if you don't set this up somewhere, this is what we'll use by default + private String driver = "org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver"; + private String compiler; + private String URI; + private String username; + private String password; + + public String getDriver() { + return this.driver; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + public String getCompiler() { + return this.compiler; + } + + public void setCompiler(String compiler) { + this.compiler = compiler; + } + + public String getURI() { + return this.URI; + } + + public void setURI(String URI) { + this.URI = URI; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Configuration configure() { + Configuration configuration = new Configuration(); + + if (this.driver != null) { + configuration.driverConfiguration().setDriverClassName(this.driver); + } + + if (this.URI != null) { + configuration.driverConfiguration().setURI(this.URI); + } + + if (this.username != null && this.password != null) { + configuration.driverConfiguration().setCredentials(this.username, this.password); + } + + if (this.compiler != null) { + configuration.compilerConfiguration().setCompilerClassName(this.compiler); + } + + return configuration; + } +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java new file mode 100644 index 0000000000..2cc223d8c7 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfiguration.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.data.neo4j; + +import org.neo4j.ogm.session.Neo4jSession; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import org.springframework.data.neo4j.repository.GraphRepository; + +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; +import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; +import org.springframework.data.neo4j.repository.support.GraphRepositoryFactoryBean; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Neo4j + * Repositories. + *

+ * Activates when there is no bean of type + * {@link org.springframework.data.neo4j.repository.support.GraphRepositoryFactoryBean} + * configured in the context, the Spring Data Neo4j + * {@link org.springframework.data.neo4j.repository.GraphRepository} type is on the + * classpath, the Neo4j client driver API is on the classpath, and there is no other + * configured {@link org.springframework.data.neo4j.repository.GraphRepository}. + *

+ * Once in effect, the auto-configuration is the equivalent of enabling Neo4j repositories + * using the + * {@link org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories} + * annotation. + * + * @author Dave Syer + * @author Oliver Gierke + * @author Josh Long + * @see EnableNeo4jRepositories + */ +@Configuration +@ConditionalOnClass({ Neo4jSession.class, GraphRepository.class }) +@ConditionalOnMissingBean({ GraphRepositoryFactoryBean.class, Neo4jRepositoryConfigurationExtension.class }) +@ConditionalOnProperty(prefix = "spring.data.neo4j.repositories", name = "enabled", havingValue = "true", matchIfMissing = true) +@Import({Neo4jRepositoriesAutoConfigureRegistrar.class}) +@AutoConfigureAfter(Neo4jAutoConfiguration.class) +public class Neo4jRepositoriesAutoConfiguration { + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigureRegistrar.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigureRegistrar.java new file mode 100644 index 0000000000..1651010535 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigureRegistrar.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.data.neo4j; + +import java.lang.annotation.Annotation; + +import org.springframework.boot.autoconfigure.data.AbstractRepositoryConfigurationSourceSupport; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; +import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension; +import org.springframework.data.repository.config.RepositoryConfigurationExtension; + +/** + * {@link ImportBeanDefinitionRegistrar} used to auto-configure Spring Data Neo4j + * Repositories. + * + * @author Dave Syer + */ +class Neo4jRepositoriesAutoConfigureRegistrar extends + AbstractRepositoryConfigurationSourceSupport { + + @Override + protected Class getAnnotation() { + return EnableNeo4jRepositories.class; + } + + @Override + protected Class getConfiguration() { + return EnableNeo4jRepositoriesConfiguration.class; + } + + @Override + protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() { + return new Neo4jRepositoryConfigurationExtension(); + } + + @EnableNeo4jRepositories + private static class EnableNeo4jRepositoriesConfiguration { + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/package-info.java new file mode 100644 index 0000000000..a681b2a7d5 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2012-2015 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. + */ + +/** + * Auto-configuration for Spring Data Neo4j. + */ +package org.springframework.boot.autoconfigure.data.neo4j; + diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index fba5f4e02c..6b720f28ab 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -90,6 +90,12 @@ "description": "Enable Mongo repositories.", "defaultValue": true }, + { + "name": "spring.data.neo4j.repositories.enabled", + "type": "java.lang.Boolean", + "description": "Enable Neo4j repositories.", + "defaultValue": true + }, { "name": "spring.data.redis.repositories.enabled", "type": "java.lang.Boolean", diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 811f7d78f3..90463d5960 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -31,6 +31,8 @@ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositor org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.neo4j.Neo4jAutoConfiguration,\ +org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/neo4j/CityNeo4jRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/neo4j/CityNeo4jRepository.java new file mode 100644 index 0000000000..722bc7a823 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/alt/neo4j/CityNeo4jRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.data.alt.neo4j; + +import org.springframework.boot.autoconfigure.data.neo4j.city.City; +import org.springframework.data.neo4j.repository.GraphRepository; + +public interface CityNeo4jRepository extends GraphRepository { +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java new file mode 100644 index 0000000000..6741bb0cb5 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java @@ -0,0 +1,175 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.data.neo4j; + +import java.util.ArrayList; +import java.util.List; + +import org.assertj.core.api.Assertions; + +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; +import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration; +import org.springframework.boot.autoconfigure.data.jpa.city.City; +import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository; +import org.springframework.boot.autoconfigure.data.neo4j.country.Country; +import org.springframework.boot.autoconfigure.data.neo4j.country.CountryRepository; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfigurationTests; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; + +import org.springframework.boot.orm.jpa.EntityScan; + +import org.springframework.boot.test.EnvironmentTestUtils; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportSelector; + +import org.springframework.core.type.AnnotationMetadata; + +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; + +/** + * Tests for {@link org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration}. + * + * @author Dave Syer + * @author Oliver Gierke + * @author Michael Hunger + * @author Vince Bickers + */ +public class MixedNeo4jRepositoriesAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + this.context.close(); + } + + @Test + public void testDefaultRepositoryConfiguration() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false"); + this.context.register(TestConfiguration.class, BaseConfiguration.class); + this.context.refresh(); + Assertions.assertThat(this.context.getBean(CountryRepository.class)).isNotNull(); + } + + @Test + public void testMixedRepositoryConfiguration() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false"); + this.context.register(MixedConfiguration.class, BaseConfiguration.class); + this.context.refresh(); + Assertions.assertThat(this.context.getBean(CountryRepository.class)).isNotNull(); + Assertions.assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + } + + @Test + public void testJpaRepositoryConfigurationWithNeo4jTemplate() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false"); + this.context.register(JpaConfiguration.class, BaseConfiguration.class); + this.context.refresh(); + Assertions.assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + } + + @Test + @Ignore + public void testJpaRepositoryConfigurationWithNeo4jOverlap() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false"); + this.context.register(OverlapConfiguration.class, BaseConfiguration.class); + this.context.refresh(); + Assertions.assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + } + + @Test + public void testJpaRepositoryConfigurationWithNeo4jOverlapDisabled() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.datasource.initialize:false", + "spring.data.neo4j.repositories.enabled:false"); + this.context.register(OverlapConfiguration.class, BaseConfiguration.class); + this.context.refresh(); + Assertions.assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + } + + @Configuration + @TestAutoConfigurationPackage(Neo4jAutoConfigurationTests.class) + // Not this package or its parent + @EnableNeo4jRepositories(basePackageClasses = Country.class) + protected static class TestConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(Neo4jAutoConfigurationTests.class) + @EnableNeo4jRepositories(basePackageClasses = Country.class) + @EntityScan(basePackageClasses = City.class) + @EnableJpaRepositories(basePackageClasses = CityRepository.class) + protected static class MixedConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(Neo4jAutoConfigurationTests.class) + @EntityScan(basePackageClasses = City.class) + @EnableJpaRepositories(basePackageClasses = CityRepository.class) + protected static class JpaConfiguration { + + } + + // In this one the Jpa repositories and the autoconfiguration packages overlap, so + // Neo4j will try and configure the same repositories + @Configuration + @TestAutoConfigurationPackage(CityRepository.class) + @EnableJpaRepositories(basePackageClasses = CityRepository.class) + protected static class OverlapConfiguration { + + } + + @Configuration + @Import(Registrar.class) + protected static class BaseConfiguration { + + } + + protected static class Registrar implements ImportSelector { + + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + List names = new ArrayList(); + for (Class type : new Class[] { DataSourceAutoConfiguration.class, + HibernateJpaAutoConfiguration.class, + JpaRepositoriesAutoConfiguration.class, + Neo4jAutoConfiguration.class, + Neo4jRepositoriesAutoConfiguration.class }) { + names.add(type.getName()); + } + return names.toArray(new String[names.size()]); + } + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java new file mode 100644 index 0000000000..5afdb1d86e --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java @@ -0,0 +1,125 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.data.neo4j; + +import org.assertj.core.api.Assertions; + +import org.junit.After; +import org.junit.Test; + +import org.neo4j.ogm.session.SessionFactory; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; + +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; +import org.springframework.boot.autoconfigure.data.alt.neo4j.CityNeo4jRepository; +import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage; +import org.springframework.boot.autoconfigure.data.neo4j.city.City; +import org.springframework.boot.autoconfigure.data.neo4j.city.CityRepository; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; + +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories; + +/** + * Tests for {@link org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration}. + * + * @author Dave Syer + * @author Oliver Gierke + * @author Michael Hunger + * @author Vince Bickers + */ +public class Neo4jRepositoriesAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + this.context.close(); + } + + @Test + public void testDefaultRepositoryConfiguration() throws Exception { + + prepareApplicationContext(TestConfiguration.class); + + Assertions.assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + + Neo4jMappingContext mappingContext = this.context.getBean(Neo4jMappingContext.class); + Assertions.assertThat(mappingContext.getPersistentEntity(City.class)).isNotNull(); + + } + + @Test + public void testNoRepositoryConfiguration() throws Exception { + prepareApplicationContext(EmptyConfiguration.class); + Assertions.assertThat(this.context.getBean(SessionFactory.class)).isNotNull(); + } + + @Test + public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() { + prepareApplicationContext(CustomizedConfiguration.class); + + Assertions.assertThat(this.context.getBean(CityNeo4jRepository.class)).isNotNull(); + } + + @Test(expected = NoSuchBeanDefinitionException.class) + public void autoConfigurationShouldNotKickInEvenIfManualConfigDidNotCreateAnyRepositories() { + prepareApplicationContext(SortOfInvalidCustomConfiguration.class); + + this.context.getBean(CityRepository.class); + } + + private void prepareApplicationContext(Class... configurationClasses) { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(configurationClasses); + this.context.register(Neo4jAutoConfiguration.class, + Neo4jRepositoriesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + } + + @Configuration + @TestAutoConfigurationPackage(City.class) + protected static class TestConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(EmptyDataPackage.class) + protected static class EmptyConfiguration { + + } + + @Configuration + @TestAutoConfigurationPackage(Neo4jRepositoriesAutoConfigurationTests.class) + @EnableNeo4jRepositories(basePackageClasses = CityNeo4jRepository.class) + protected static class CustomizedConfiguration { + + } + + @Configuration + // To not find any repositories + @EnableNeo4jRepositories("foo.bar") + @TestAutoConfigurationPackage(Neo4jRepositoriesAutoConfigurationTests.class) + protected static class SortOfInvalidCustomConfiguration { + + } +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/City.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/City.java new file mode 100644 index 0000000000..df7694538f --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/City.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2013 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.boot.autoconfigure.data.neo4j.city; + +import java.io.Serializable; + +import org.neo4j.ogm.annotation.GraphId; +import org.neo4j.ogm.annotation.NodeEntity; + +import org.springframework.boot.autoconfigure.data.neo4j.country.Country; + +@NodeEntity +public class City implements Serializable { + + private static final long serialVersionUID = 1L; + + @GraphId + private Long id; + + private String name; + + private String state; + + private Country country; + + private String map; + + public City() { + } + + public City(String name, Country country) { + this.name = name; + this.country = country; + } + + public String getName() { + return this.name; + } + + public String getState() { + return this.state; + } + + public Country getCountry() { + return this.country; + } + + public String getMap() { + return this.map; + } + + @Override + public String toString() { + return getName() + "," + getState() + "," + getCountry(); + } +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/CityRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/CityRepository.java new file mode 100644 index 0000000000..a89e07909f --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/city/CityRepository.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-2013 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.boot.autoconfigure.data.neo4j.city; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.neo4j.repository.GraphRepository; + +public interface CityRepository extends GraphRepository { + + Page findAll(Pageable pageable); + +// TODO: cannot resolve queries like this at the moment. +// +// Page findByNameLikeAndCountryLikeAllIgnoringCase(String name, String country, +// Pageable pageable); +// +// City findByNameAndCountry(String name, String country); + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/Country.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/Country.java new file mode 100644 index 0000000000..012ddd1e54 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/Country.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2013 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.boot.autoconfigure.data.neo4j.country; + +import java.io.Serializable; + +import org.neo4j.ogm.annotation.GraphId; +import org.neo4j.ogm.annotation.NodeEntity; + +@NodeEntity +public class Country implements Serializable { + + private static final long serialVersionUID = 1L; + + @GraphId + private Long id; + + private String name; + + public Country() { + } + + public Country(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + @Override + public String toString() { + return getName(); + } +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/CountryRepository.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/CountryRepository.java new file mode 100644 index 0000000000..1814c642da --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/country/CountryRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright 2012-2013 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.boot.autoconfigure.data.neo4j.country; + +import org.springframework.data.neo4j.repository.GraphRepository; + +public interface CountryRepository extends GraphRepository { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java new file mode 100644 index 0000000000..d4c10bee39 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012-2014 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.boot.autoconfigure.neo4j; + +import org.assertj.core.api.Assertions; + +import org.junit.After; +import org.junit.Test; + +import org.neo4j.ogm.config.Configuration; + +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.data.neo4j.Neo4jAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * Tests for {@link Neo4jAutoConfiguration}. + * + * @author Dave Syer + * @author Michael Hunger + * @author Vince Bickers + */ +public class Neo4jAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void configurationExists() { + this.context = new AnnotationConfigApplicationContext( + PropertyPlaceholderAutoConfiguration.class, Neo4jAutoConfiguration.class); + Assertions.assertThat(this.context.getBeanNamesForType(Configuration.class).length).isEqualTo(1); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jDataAutoConfigurationTests.java new file mode 100644 index 0000000000..8227b24f7c --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jDataAutoConfigurationTests.java @@ -0,0 +1,96 @@ +/* + * Copyright 2012-2015 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.boot.autoconfigure.neo4j; + +import org.assertj.core.api.Assertions; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; + +import org.junit.rules.ExpectedException; + +import org.neo4j.ogm.session.SessionFactory; + +import org.springframework.boot.autoconfigure.AutoConfigurationPackages; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.data.neo4j.Neo4jAutoConfiguration; +import org.springframework.boot.autoconfigure.data.neo4j.city.City; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import org.springframework.data.neo4j.mapping.Neo4jMappingContext; +import org.springframework.data.neo4j.template.Neo4jOperations; + +/** + * Tests for {@link Neo4jAutoConfiguration}. + * + * @author Josh Long + * @author Oliver Gierke + * @author Vince Bickers + */ +public class Neo4jDataAutoConfigurationTests { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void templateExists() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(PropertyPlaceholderAutoConfiguration.class, Neo4jAutoConfiguration.class); + this.context.refresh(); + Assertions.assertThat(this.context.getBeanNamesForType(Neo4jOperations.class).length).isEqualTo(1); + } + + @Test + public void sessionFactoryExists() { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(PropertyPlaceholderAutoConfiguration.class, Neo4jAutoConfiguration.class); + this.context.refresh(); + Assertions.assertThat(this.context.getBeanNamesForType(SessionFactory.class).length).isEqualTo(1); + } + + @Test + public void usesAutoConfigurationPackageToPickUpDomainTypes() { + this.context = new AnnotationConfigApplicationContext(); + String cityPackage = City.class.getPackage().getName(); + AutoConfigurationPackages.register(this.context, cityPackage); + this.context.register(Neo4jAutoConfiguration.class); + this.context.refresh(); + assertDomainTypesDiscovered(this.context.getBean(Neo4jMappingContext.class), + City.class); + } + + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static void assertDomainTypesDiscovered(Neo4jMappingContext mappingContext, + Class... types) { + for (Class type : types) { + Assertions.assertThat(mappingContext.getPersistentEntity(type)).isNotNull(); + } + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jPropertiesTests.java new file mode 100644 index 0000000000..e619acbf95 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jPropertiesTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2015 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.boot.autoconfigure.neo4j; + +import org.assertj.core.api.Assertions; + +import org.junit.Test; + +import org.springframework.boot.autoconfigure.data.neo4j.Neo4jProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; + +/** + * Tests for {@link Neo4jProperties}. + * + * @author Phillip Webb + * @author Andy Wilkinson + * @author Vince Bickers + */ + +public class Neo4jPropertiesTests { + + @Test + public void shouldHaveCorrectDefaultDriver() { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(Conf.class); + context.refresh(); + + Neo4jProperties neo4jProperties = context.getBean(Neo4jProperties.class); + + Assertions.assertThat(neo4jProperties.getDriver()).isEqualTo("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver"); + } + + @Test + public void shouldConfigureFromDefaults() { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(Conf.class); + context.refresh(); + + Neo4jProperties neo4jProperties = context.getBean(Neo4jProperties.class); + + org.neo4j.ogm.config.Configuration configuration = neo4jProperties.configure(); + Assertions.assertThat(configuration.driverConfiguration().getDriverClassName()) + .isEqualTo("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver"); + + } + + @Test + public void shouldBeCustomisable() { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(context, "spring.data.neo4j.driver:CustomDriver"); + context.register(Conf.class); + context.refresh(); + + Neo4jProperties neo4jProperties = context.getBean(Neo4jProperties.class); + + Assertions.assertThat(neo4jProperties.getDriver()).isEqualTo("CustomDriver"); + + } + + @Configuration + @EnableConfigurationProperties(Neo4jProperties.class) + static class Conf { + } +} diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index d6570f51fb..39156dd5f8 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -55,9 +55,11 @@ 2.1.9 1.9.2 3.2.2 + 1.10 1.4 2.1.1 2.1 + 3.4 1.6 2.4.2 2.2.3 @@ -325,6 +327,11 @@ spring-boot-starter-data-redis 1.4.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-starter-data-neo4j + 1.4.0.BUILD-SNAPSHOT + org.springframework.boot spring-boot-starter-data-rest @@ -768,11 +775,17 @@ commons-collections ${commons-collections.version} + + commons-codec + commons-codec + ${commons-codec.version} + commons-dbcp commons-dbcp ${commons-dbcp.version} + commons-digester commons-digester @@ -2149,6 +2162,7 @@ wsdl4j ${wsdl4j.version} + diff --git a/spring-boot-docs/pom.xml b/spring-boot-docs/pom.xml index 3500b5bd58..b36c2a3d56 100644 --- a/spring-boot-docs/pom.xml +++ b/spring-boot-docs/pom.xml @@ -488,6 +488,11 @@ spring-data-mongodb true + + org.springframework.data + spring-data-neo4j + true + org.springframework.data spring-data-redis diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 7c88a8e2e1..0568ede96b 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3009,6 +3009,179 @@ Mongo instance's configuration and logging routing. +[[boot-features-neo4j]] +=== Neo4j +http://neo4j.com/[Neo4j] is an open-source NoSQL graph database that uses a +rich data model of nodes related by first class relationships which is better +suited for connected big data than traditional rdbms approaches. +Spring Boot offers several conveniences for working with Neo4j, including the +`spring-boot-starter-data-neo4j` '`Starter POM`'. + +[[boot-features-connecting-to-neo4j]] +==== Connecting to a Neo4j database +You can inject an auto-configured `org.neo4j.ogm.session.Neo4jSession` to +access Neo4j databases. + +In your `application properties`, you can supply any domain packages to be scanned by the OGM at startup +as well as the lifetime of the OGM session that will be established for web clients. + +By default your application will be configured to use an in-process embedded instance of Neo4j that will not persist any data when your application shuts down. You can also connect to a remote Neo4j server, or to an embedded instance that persists data between restarts of your application. + +The following sections show how you can configure your application for each of these scenarios. + +[[boot-features-neo4j-embedded]] +==== Connecting to an embedded database +[source,properties,indent=0] +---- + # embedded driver (optional: default is embedded driver) + spring.data.neo4j.driver=org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver + + # database path (optional: default is in-memory) + spring.data.neo4j.URI=file://var/tmp/graph.db + + # declare the domain packages for the OGM to scan at startup + # note: if you don't need to do any object mapping, you can omit this property + spring.data.neo4.domain.packages=my.app.domain.core, my.app.domain.external, ... + + # OGM session lifetime for web clients + # options: session (httpSession), request (httpRequest) + # default: session + spring.data.neo4j.session.lifetime=session +---- + +[[boot-features-neo4j-remote]] +==== Connecting to a remote database +[source,properties,indent=0] +---- + # http driver + spring.data.neo4j.driver=org.neo4j.ogm.drivers.http.driver.HttpDriver + + # database uri + spring.data.neo4j.URI=http://user:password@localhost:7474 + + # declare the domain packages for the OGM to scan at startup + # note: if you don't need to do any object mapping, you can omit this property + spring.data.neo4.domain.packages=my.app.domain.core, my.app.domain.external, ... + + # OGM session lifetime for web clients + # options: session (httpSession), request (httpRequest) + # default: session + spring.data.neo4j.session.lifetime=session +---- + +[[boot-features-spring-data-neo4j-application]] +==== Application +[source,java,indent=0]] +---- + @SpringBootApplication + @Import(Neo4jAutoConfiguration.class) + public class Application { + + public static void main(String[] args) { + new SpringApplication(Application.class).run(args); + } + + } +---- + +[[boot-features-neo4j-ogm-session]] +==== Neo4jSession +[source,java,indent=0] +---- + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Component; + + import org.neo4j.ogm.session.Neo4jSession; + + @Component + public class MyBean { + + private final Session session; + + @Autowired + public MyBean(Session session) { + this.session = session; + } + + // ... + public void example() { + Iterable result = session.query("MATCH (c:Customer) RETURN count(*)",null); + // ... + } + + } +---- + +[[boot-features-spring-data-neo4j-template]] +==== Neo4jTemplate +Spring Data Neo4j provides a +{spring-data-neo4j-javadoc}/core/Neo4jTemplate.html[`Neo4jTemplate`] class that is very +similar in its design to Spring's `JdbcTemplate`. As with `JdbcTemplate` Spring Boot +auto-configures a bean for you to simply inject: + +[source,java,indent=0] +---- + import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.stereotype.Component; + + import org.springframework.data.neo4j.template.Neo4jTemplate; + + @Component + public class MyBean { + + private final Neo4jTemplate neo4jTemplate; + + @Autowired + public MyBean(Neo4jTemplate neo4jTemplate) { + this.neo4jTemplate = neo4jTemplate; + } + + // ... + + } +---- + +See the `Neo4jOperations` Javadoc for complete details. + +[[boot-features-spring-data-neo4j-repositories]] +==== Spring Data Neo4j repositories +Spring Data includes repository support for Neo4j. + +In fact, both Spring Data JPA and Spring Data Neo4j share the same common +infrastructure; so you could take the JPA example from earlier and, assuming that `City` +is now a Neo4j OGM `@NodeEntity` rather than a JPA `@Entity`, it will work in the same way. + +To enable repository support (and optionally support for `@Transactional`), add the following two annotations to +your Spring configuration: + +[source,java,indent=0] +---- + @EnableNeo4jRepositories(basePackages = "com.example.myapp.repository") + @EnableTransactionManagement +---- + +==== Repository example +[source,java,indent=0] +---- + package com.example.myapp.domain; + + import org.springframework.data.domain.*; + import org.springframework.data.repository.*; + + public interface CityRepository extends GraphRepository { + + Page findAll(Pageable pageable); + + City findByNameAndCountry(String name, String country); + + } +---- + +TIP: For complete details of Spring Data Neo4j, including its rich object mapping +technologies, refer to their http://projects.spring.io/spring-data-neo4j/[reference +documentation]. + + [[boot-features-gemfire]] === Gemfire https://github.com/spring-projects/spring-data-gemfire[Spring Data Gemfire] provides diff --git a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc index 01db16519e..d040454f67 100644 --- a/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc +++ b/spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc @@ -420,6 +420,9 @@ and Hibernate. |`spring-boot-starter-data-redis` |Support for the REDIS key-value data store, including `spring-data-redis`. +|`spring-boot-starter-data-neo4j` +|Support for the Neo4j Graph Database, including `spring-data-neo4j`. + |`spring-boot-starter-data-rest` |Support for exposing Spring Data repositories over REST via `spring-data-rest-webmvc`. diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/pom.xml b/spring-boot-samples/spring-boot-sample-data-neo4j/pom.xml new file mode 100644 index 0000000000..e87954e532 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-neo4j/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.4.0.BUILD-SNAPSHOT + + spring-boot-sample-data-neo4j + Spring Boot Data Neo4j Sample + Spring Boot Data Neo4j Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-data-neo4j + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.neo4j + neo4j-ogm-embedded-driver + 2.0.0-M04 + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/Customer.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/Customer.java new file mode 100644 index 0000000000..4267e34b46 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/Customer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2013 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 sample.data.neo4j; + +import org.neo4j.ogm.annotation.NodeEntity; +import org.neo4j.ogm.annotation.GraphId; + +@NodeEntity +public class Customer { + + @GraphId + private Long id; + + private String firstName; + private String lastName; + + public Customer() { + } + + public Customer(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + @Override + public String toString() { + return String.format("Customer[id=%s, firstName='%s', lastName='%s']", id, + firstName, lastName); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/CustomerRepository.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/CustomerRepository.java new file mode 100644 index 0000000000..6c809b901f --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/CustomerRepository.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012-2013 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 sample.data.neo4j; + +import java.util.List; + +import org.springframework.data.neo4j.repository.GraphRepository; + +public interface CustomerRepository extends GraphRepository { + + public Customer findByFirstName(String firstName); + + public List findByLastName(String lastName); + +} diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/SampleNeo4jApplication.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/SampleNeo4jApplication.java new file mode 100644 index 0000000000..20b2c8ef46 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/java/sample/data/neo4j/SampleNeo4jApplication.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2015 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 sample.data.neo4j; + +import org.springframework.beans.factory.annotation.Autowired; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import org.springframework.context.annotation.Import; + +@SpringBootApplication +public class SampleNeo4jApplication implements CommandLineRunner { + + @Autowired + private CustomerRepository repository; + + @Override + public void run(String... args) throws Exception { + this.repository.deleteAll(); + + // save a couple of customers + this.repository.save(new Customer("Alice", "Smith")); + this.repository.save(new Customer("Bob", "Smith")); + + // fetch all customers + System.out.println("Customers found with findAll():"); + System.out.println("-------------------------------"); + for (Customer customer : this.repository.findAll()) { + System.out.println(customer); + } + System.out.println(); + + // fetch an individual customer + System.out.println("Customer found with findByFirstName('Alice'):"); + System.out.println("--------------------------------"); + System.out.println(this.repository.findByFirstName("Alice")); + + System.out.println("Customers found with findByLastName('Smith'):"); + System.out.println("--------------------------------"); + for (Customer customer : this.repository.findByLastName("Smith")) { + System.out.println(customer); + } + } + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleNeo4jApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/resources/application.properties new file mode 100644 index 0000000000..b56c18a6b3 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/main/resources/application.properties @@ -0,0 +1,3 @@ +spring.data.neo4j.driver=org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver +spring.data.neo4j.domain.packages=sample.data.neo4j +spring.data.neo4j.session.lifetime=prototype \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-data-neo4j/src/test/java/sample/data/neo4j/SampleNeo4jApplicationTests.java b/spring-boot-samples/spring-boot-sample-data-neo4j/src/test/java/sample/data/neo4j/SampleNeo4jApplicationTests.java new file mode 100644 index 0000000000..a7d445800c --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-data-neo4j/src/test/java/sample/data/neo4j/SampleNeo4jApplicationTests.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2015 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 sample.data.neo4j; + +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.OutputCapture; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link SampleNeo4jApplication}. + * + * @author Dave Syer + * @author Andy Wilkinson + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(SampleNeo4jApplication.class) +@IntegrationTest +public class SampleNeo4jApplicationTests { + + @ClassRule + public static OutputCapture outputCapture = new OutputCapture(); + + @Test + public void testDefaultSettings() throws Exception { + String output = SampleNeo4jApplicationTests.outputCapture.toString(); + assertTrue("Wrong output: " + output, + output.contains("firstName='Alice', lastName='Smith'")); + } + +} diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index e5667e3c3a..57c9258cdc 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -33,6 +33,7 @@ spring-boot-starter-data-gemfire spring-boot-starter-data-jpa spring-boot-starter-data-mongodb + spring-boot-starter-data-neo4j spring-boot-starter-data-redis spring-boot-starter-data-rest spring-boot-starter-data-solr @@ -69,6 +70,7 @@ spring-boot-starter-websocket spring-boot-starter-ws + diff --git a/spring-boot-starters/spring-boot-starter-data-neo4j/pom.xml b/spring-boot-starters/spring-boot-starter-data-neo4j/pom.xml new file mode 100644 index 0000000000..3a753b0c3e --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-data-neo4j/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starters + 1.4.0.BUILD-SNAPSHOT + + spring-boot-starter-data-neo4j + Spring Boot Data Neo4j Starter + Spring Boot Data Neo4j Starter + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.data + spring-data-neo4j + + + diff --git a/spring-boot-starters/spring-boot-starter-data-neo4j/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-data-neo4j/src/main/resources/META-INF/spring.provides new file mode 100644 index 0000000000..38cc6363c5 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-data-neo4j/src/main/resources/META-INF/spring.provides @@ -0,0 +1 @@ +provides: spring-data-neo4j \ No newline at end of file