Add opt-in support for Neo4j-OGM native types

This includes tests for the autoconfiguration using that new property.
The test require the native types for Bolt and embedded in the test
scope, so the Neo4j-OGM native types have been added to managed
dependencies.

The enhanced autoconfiguration throws an
InvalidConfigurationPropertyValueException when native types cannot be
used due to missing dependencies or wrong transport mode.

See gh-15637
pull/13916/head
Michael Simons 6 years ago committed by Andy Wilkinson
parent c8070f4408
commit af21b847a0

@ -782,7 +782,7 @@
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>
<artifactId>neo4j-ogm-bolt-native-types</artifactId>
<scope>test</scope>
</dependency>
<dependency>
@ -790,6 +790,11 @@
<artifactId>neo4j-ogm-embedded-driver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>

@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 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.
@ -20,6 +20,9 @@ import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.neo4j.ogm.driver.NativeTypesException;
import org.neo4j.ogm.driver.NativeTypesNotAvailableException;
import org.neo4j.ogm.driver.NativeTypesNotSupportedException;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.session.event.EventListener;
@ -34,12 +37,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.autoconfigure.domain.EntityScanPackages;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.data.neo4j.web.support.OpenSessionInViewInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -53,6 +58,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
* @author Vince Bickers
* @author Stephane Nicoll
* @author Kazuki Shimizu
* @author Michael Simons
* @since 1.4.0
*/
@Configuration
@ -73,10 +79,44 @@ public class Neo4jDataAutoConfiguration {
public SessionFactory sessionFactory(org.neo4j.ogm.config.Configuration configuration,
ApplicationContext applicationContext,
ObjectProvider<EventListener> eventListeners) {
SessionFactory sessionFactory = new SessionFactory(configuration,
getPackagesToScan(applicationContext));
eventListeners.stream().forEach(sessionFactory::register);
return sessionFactory;
try {
SessionFactory sessionFactory = new SessionFactory(configuration,
getPackagesToScan(applicationContext));
eventListeners.forEach(sessionFactory::register);
return sessionFactory;
}
catch (NativeTypesException ex) {
InvalidConfigurationPropertyValueException translatedMessage = translateNativeTypesException(
ex);
throw (translatedMessage != null) ? translatedMessage : ex;
}
}
@Nullable
private static InvalidConfigurationPropertyValueException translateNativeTypesException(
NativeTypesException cause) {
String propertyName = Neo4jProperties.CONFIGURATION_PREFIX + ".use-native-types";
boolean propertyValue = true;
if (cause instanceof NativeTypesNotAvailableException) {
String message = String.format(
"The native type module for your Neo4j-OGM driver is not available. "
+ "Please add the following dependency to your build:%n'%s'.",
((NativeTypesNotAvailableException) cause).getRequiredModule());
return new InvalidConfigurationPropertyValueException(propertyName,
propertyValue, message);
}
if (cause instanceof NativeTypesNotSupportedException) {
String message = String.format(
"The configured Neo4j-OGM driver %s does not support Neo4j native types. "
+ "Please consider one of the drivers that support Neo4js native types like the Bolt or the embedded driver.",
cause.getDriverClassName());
return new InvalidConfigurationPropertyValueException(propertyName,
propertyValue, message);
}
return null;
}
@Bean

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2019 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.
@ -33,11 +33,14 @@ import org.springframework.util.ClassUtils;
* @author Michael Hunger
* @author Vince Bickers
* @author Aurélien Leboulanger
* @author Michael Simons
* @since 1.4.0
*/
@ConfigurationProperties(prefix = "spring.data.neo4j")
@ConfigurationProperties(prefix = Neo4jProperties.CONFIGURATION_PREFIX)
public class Neo4jProperties implements ApplicationContextAware {
static final String CONFIGURATION_PREFIX = "spring.data.neo4j";
static final String EMBEDDED_DRIVER = "org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver";
static final String HTTP_DRIVER = "org.neo4j.ogm.drivers.http.driver.HttpDriver";
@ -72,6 +75,12 @@ public class Neo4jProperties implements ApplicationContextAware {
*/
private Boolean openInView;
/**
* Disables the conversion of java.time.* and spatial types in Neo4j-OGM entities and
* uses Neo4j native types wherever possible.
*/
private boolean useNativeTypes = false;
private final Embedded embedded = new Embedded();
private ClassLoader classLoader = Neo4jProperties.class.getClassLoader();
@ -116,6 +125,14 @@ public class Neo4jProperties implements ApplicationContextAware {
this.openInView = openInView;
}
public boolean isUseNativeTypes() {
return this.useNativeTypes;
}
public void setUseNativeTypes(boolean useNativeTypes) {
this.useNativeTypes = useNativeTypes;
}
public Embedded getEmbedded() {
return this.embedded;
}
@ -146,6 +163,9 @@ public class Neo4jProperties implements ApplicationContextAware {
builder.credentials(this.username, this.password);
}
builder.autoIndex(this.getAutoIndex().getName());
if (this.useNativeTypes) {
builder.useNativeTypes();
}
}
private void configureUriWithDefaults(Builder builder) {

@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 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.
@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.data.neo4j.city.City;
import org.springframework.boot.autoconfigure.data.neo4j.country.Country;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
@ -46,13 +47,14 @@ import org.springframework.data.neo4j.web.support.OpenSessionInViewInterceptor;
import org.springframework.web.context.WebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link Neo4jDataAutoConfiguration}. Tests can't use the embedded driver as we
* use Lucene 4 and Neo4j still requires 3.
* Tests for {@link Neo4jDataAutoConfiguration}. Tests should not use the embedded driver
* as it requires the complete Neo4j-Kernel and server to function properly.
*
* @author Stephane Nicoll
* @author Michael Hunger
@ -116,7 +118,6 @@ public class Neo4jDataAutoConfigurationTests {
assertThat(context)
.hasSingleBean(org.neo4j.ogm.config.Configuration.class);
});
}
@Test
@ -144,6 +145,50 @@ public class Neo4jDataAutoConfigurationTests {
.doesNotHaveBean(OpenSessionInViewInterceptor.class));
}
@Test
public void shouldBeAbleToUseNativeTypesWithBolt() {
this.contextRunner
.withPropertyValues("spring.data.neo4j.uri=bolt://localhost:7687",
"spring.data.neo4j.use-native-types:true")
.withConfiguration(AutoConfigurations.of(Neo4jDataAutoConfiguration.class,
TransactionAutoConfiguration.class))
.run((context) -> assertThat(context)
.getBean(org.neo4j.ogm.config.Configuration.class)
.hasFieldOrPropertyWithValue("useNativeTypes", true));
}
@Test
public void shouldDealWithNativeTypesNotAvailableException() {
assertThatIllegalStateException().isThrownBy(() -> {
this.contextRunner
.withClassLoader(
new FilteredClassLoader("org.neo4j.ogm.drivers.bolt.types"))
.withPropertyValues("spring.data.neo4j.uri=bolt://localhost:7687",
"spring.data.neo4j.use-native-types:true")
.withConfiguration(
AutoConfigurations.of(Neo4jDataAutoConfiguration.class,
TransactionAutoConfiguration.class))
.run((context) -> context.getBean(SessionFactory.class));
}).withRootCauseInstanceOf(InvalidConfigurationPropertyValueException.class)
.withStackTraceContaining(
"The native type module for your Neo4j-OGM driver is not available. Please add the following dependency to your build:");
}
@Test
public void shouldDealWithNativeTypesNotSupportedException() {
assertThatIllegalStateException().isThrownBy(() -> {
this.contextRunner
.withPropertyValues("spring.data.neo4j.uri=http://localhost:7474",
"spring.data.neo4j.use-native-types:true")
.withConfiguration(
AutoConfigurations.of(Neo4jDataAutoConfiguration.class,
TransactionAutoConfiguration.class))
.run((context) -> context.getBean(SessionFactory.class));
}).withRootCauseInstanceOf(InvalidConfigurationPropertyValueException.class)
.withStackTraceContaining(
" The configured Neo4j-OGM driver org.neo4j.ogm.drivers.http.driver.HttpDriver does not support Neo4j native types.");
}
@Test
public void eventListenersAreAutoRegistered() {
this.contextRunner.withUserConfiguration(EventListenerConfiguration.class)

@ -1,5 +1,5 @@
/*
* Copyright 2012-2018 the original author or authors.
* Copyright 2012-2019 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.
@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link Neo4jProperties}.
*
* @author Stephane Nicoll
* @author Michael Simons
*/
public class Neo4jPropertiesTests {
@ -147,6 +148,21 @@ public class Neo4jPropertiesTests {
"file:relative/path/to/my.db");
}
@Test
public void nativeTypesAreSetToFalseByDefault() {
Neo4jProperties properties = load(true);
Configuration configuration = properties.createConfiguration();
assertThat(configuration.getUseNativeTypes()).isFalse();
}
@Test
public void nativeTypesCanBeConfigured() {
Neo4jProperties properties = load(true,
"spring.data.neo4j.use-native-types=true");
Configuration configuration = properties.createConfiguration();
assertThat(configuration.getUseNativeTypes()).isTrue();
}
private static void assertDriver(Configuration actual, String driver, String uri) {
assertThat(actual).isNotNull();
assertThat(actual.getDriverClassName()).isEqualTo(driver);

@ -2527,6 +2527,11 @@
<artifactId>neo4j-ogm-bolt-driver</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-bolt-native-types</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-core</artifactId>
@ -2537,6 +2542,11 @@
<artifactId>neo4j-ogm-embedded-driver</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-embedded-native-types</artifactId>
<version>${neo4j-ogm.version}</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>

@ -4440,6 +4440,25 @@ in your configuration, e.g. `spring.data.neo4j.uri=file://var/tmp/graph.db`.
[[boot-features-neo4j-ogm-native-types]]
==== Using native types
Neo4j-OGM can map some types, like `java.time.*` to `String` based properties or
use one of the various native types that Neo4j provides.
For backwards compatibility reasons the default for Neo4j-OGM is using a `String` based representation.
To change that behaviour, please add one of `org.neo4j:neo4j-ogm-bolt-native-types` or `org.neo4j:neo4j-ogm-embedded-native-types`
to the dependencies of your application
and use the following property in your Spring Boot configuration:
[source,properties,indent=0]
----
spring.data.neo4j.use-native-types=true
----
Please refer to the Neo4j-OGM reference for more information about
https://neo4j.com/docs/ogm-manual/current/reference/#reference:native-property-types[native property types].
[[boot-features-neo4j-ogm-session]]
==== Neo4jSession
By default, if you are running a web application, the session is bound to the thread for

Loading…
Cancel
Save