1818import static org .assertj .core .api .Assertions .assertThat ;
1919import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
2020
21- import java .lang .reflect .Field ;
2221import java .text .SimpleDateFormat ;
2322import java .time .ZoneId ;
2423import java .time .ZonedDateTime ;
4140import org .junit .jupiter .api .DynamicTest ;
4241import org .junit .jupiter .api .Test ;
4342import org .junit .jupiter .api .TestFactory ;
44- import org .junit .platform .commons .util .AnnotationUtils ;
4543import org .neo4j .driver .Driver ;
4644import org .neo4j .driver .Session ;
4745import org .neo4j .driver .Value ;
5351import org .springframework .core .convert .support .DefaultConversionService ;
5452import org .springframework .data .mapping .MappingException ;
5553import org .springframework .data .neo4j .config .AbstractNeo4jConfig ;
56- import org .springframework .data .neo4j .core .convert .ConvertWith ;
5754import org .springframework .data .neo4j .core .convert .Neo4jConversions ;
55+ import org .springframework .data .neo4j .core .Neo4jTemplate ;
56+ import org .springframework .data .neo4j .integration .shared .common .ThingWithAllCypherTypes2 ;
5857import org .springframework .data .neo4j .integration .shared .conversion .Neo4jConversionsITBase ;
5958import org .springframework .data .neo4j .integration .shared .conversion .ThingWithAllAdditionalTypes ;
6059import org .springframework .data .neo4j .integration .shared .common .ThingWithAllCypherTypes ;
6160import org .springframework .data .neo4j .integration .shared .common .ThingWithAllSpatialTypes ;
62- import org .springframework .data .neo4j .integration .shared .conversion .ThingWithCompositeProperties ;
6361import org .springframework .data .neo4j .integration .shared .conversion .ThingWithCustomTypes ;
64- import org .springframework .data .neo4j .integration .shared .common .ThingWithNonExistingPrimitives ;
6562import org .springframework .data .neo4j .integration .shared .common .ThingWithUUIDID ;
6663import org .springframework .data .neo4j .repository .Neo4jRepository ;
6764import org .springframework .data .neo4j .repository .config .EnableNeo4jRepositories ;
6865import org .springframework .data .neo4j .test .Neo4jIntegrationTest ;
6966import org .springframework .test .util .ReflectionTestUtils ;
7067import org .springframework .transaction .annotation .EnableTransactionManagement ;
71- import org .springframework .util .ReflectionUtils ;
7268
7369/**
7470 * @author Michael J. Simons
7874@ Neo4jIntegrationTest
7975class TypeConversionIT extends Neo4jConversionsITBase {
8076
81- private final Driver driver ;
82-
83- @ Autowired CypherTypesRepository cypherTypesRepository ;
77+ private final CypherTypesRepository cypherTypesRepository ;
8478
8579 private final AdditionalTypesRepository additionalTypesRepository ;
8680
@@ -90,11 +84,10 @@ class TypeConversionIT extends Neo4jConversionsITBase {
9084
9185 private final DefaultConversionService defaultConversionService ;
9286
93- @ Autowired TypeConversionIT (Driver driver , CypherTypesRepository cypherTypesRepository ,
87+ @ Autowired TypeConversionIT (CypherTypesRepository cypherTypesRepository ,
9488 AdditionalTypesRepository additionalTypesRepository , SpatialTypesRepository spatialTypesRepository ,
9589 CustomTypesRepository customTypesRepository ,
9690 Neo4jConversions neo4jConversions ) {
97- this .driver = driver ;
9891 this .cypherTypesRepository = cypherTypesRepository ;
9992 this .additionalTypesRepository = additionalTypesRepository ;
10093 this .spatialTypesRepository = spatialTypesRepository ;
@@ -104,14 +97,20 @@ class TypeConversionIT extends Neo4jConversionsITBase {
10497 }
10598
10699 @ Test
107- void thereShallBeNoDefaultValuesForNonExistingAttributes (@ Autowired NonExistingPrimitivesRepository repository ) {
100+ void thereShallBeNoDefaultValuesForNonExistingAttributes () {
101+
102+ Long id ;
103+ try (Session session = neo4jConnectionSupport .getDriver ().session ()) {
104+
105+ id = session .writeTransaction (tx -> tx .run ("CREATE (n:CypherTypes) RETURN id(n)" ).single ().get (0 ).asLong ());
106+ }
108107
109108 assertThatExceptionOfType (MappingException .class )
110- .isThrownBy (() -> repository .findById (ID_OF_NON_EXISTING_PRIMITIVES_NODE ))
109+ .isThrownBy (() -> cypherTypesRepository .findById (id ))
111110 .withMessageMatching (
112- "Error mapping Record<\\ {n: \\ {__internalNeo4jId__: \\ d+, id : NULL, someBoolean : NULL, __nodeLabels__: \\ [\" NonExistingPrimitives \" \\ ] \\ } \\ }>" )
113- .withStackTraceContaining ("unboxBoolean " )
114- .withRootCauseInstanceOf (NullPointerException .class );
111+ "Error mapping Record<\\ {n: \\ {__internalNeo4jId__: \\ d+, aBoolean : NULL, aString : NULL, aLong: NULL, anOffsetTime: NULL, aLocalDateTime: NULL, aDouble: NULL, aByteArray: NULL, aPoint: NULL, aZeroDuration: NULL, aZoneDateTime: NULL, __nodeLabels__: \\ [\" CypherTypes \" ], aLocalDate: NULL, aZeroPeriod: NULL, anIsoDuration: NULL, aLocalTime: NULL, id: NULL} }>" )
112+ .withStackTraceContaining ("Illegal arguments for constructor " )
113+ .withRootCauseInstanceOf (IllegalArgumentException .class );
115114 }
116115
117116 @ TestFactory
@@ -158,10 +157,10 @@ Stream<DynamicNode> conversionsShouldBeAppliedToEntities() {
158157 () -> assertThat (ReflectionTestUtils .getField (thing , a .getKey ()))
159158 .isEqualTo (a .getValue ()))));
160159
161- DynamicContainer writes = DynamicContainer .dynamicContainer ("write" , entry .getValue ().entrySet ().stream ()
162- .map (a -> DynamicTest
163- .dynamicTest (a . getKey () ,
164- () -> assertWrite (copyOfThing , a . getKey () , defaultConversionService ))));
160+ DynamicContainer writes = DynamicContainer .dynamicContainer ("write" , entry .getValue ().keySet ().stream ()
161+ .map (o -> DynamicTest
162+ .dynamicTest (o ,
163+ () -> assertWrite (copyOfThing , o , defaultConversionService ))));
165164
166165 return DynamicContainer .dynamicContainer (entry .getKey (), Arrays .asList (reads , writes ));
167166 });
@@ -172,8 +171,6 @@ void assertWrite(Object thing, String fieldName, ConversionService conversionSer
172171 long id = (long ) ReflectionTestUtils .getField (thing , "id" );
173172 Object domainValue = ReflectionTestUtils .getField (thing , fieldName );
174173
175- Field field = ReflectionUtils .findField (thing .getClass (), fieldName );
176- Optional <ConvertWith > annotation = AnnotationUtils .findAnnotation (field , ConvertWith .class );
177174 Function <Object , Value > conversion ;
178175 if (fieldName .equals ("dateAsLong" )) {
179176 conversion = o -> Values .value (((Date ) o ).getTime ());
@@ -185,8 +182,7 @@ void assertWrite(Object thing, String fieldName, ConversionService conversionSer
185182 Value driverValue ;
186183 if (domainValue != null && Collection .class .isAssignableFrom (domainValue .getClass ())) {
187184 Collection <?> sourceCollection = (Collection <?>) domainValue ;
188- Object [] targetCollection = (sourceCollection ).stream ()
189- .map (element -> conversion .apply (element )).toArray ();
185+ Object [] targetCollection = (sourceCollection ).stream ().map (conversion ).toArray ();
190186 driverValue = Values .value (targetCollection );
191187 } else {
192188 driverValue = conversion .apply (domainValue );
@@ -236,6 +232,34 @@ void parametersTargetingConvertedAttributesMustBeConverted(@Autowired CustomType
236232 .hasSizeGreaterThan (0 );
237233 }
238234
235+ @ Test // GH-2348
236+ void nonExistingPrimitivesShouldNotFailWithFieldAccess (@ Autowired Neo4jTemplate template ) {
237+ Long id ;
238+ try (Session session = neo4jConnectionSupport .getDriver ().session ()) {
239+
240+ id = session .writeTransaction (tx -> tx .run ("CREATE (n:ThingWithAllCypherTypes2) RETURN id(n)" ).single ().get (0 ).asLong ());
241+ }
242+
243+ Optional <ThingWithAllCypherTypes2 > optionalResult = template .findById (id , ThingWithAllCypherTypes2 .class );
244+ assertThat (optionalResult ).hasValueSatisfying (result -> {
245+ assertThat (result .isABoolean ()).isFalse ();
246+ assertThat (result .getALong ()).isEqualTo (0L );
247+ assertThat (result .getAnInt ()).isEqualTo (0 );
248+ assertThat (result .getADouble ()).isEqualTo (0.0 );
249+ assertThat (result .getAString ()).isNull ();
250+ assertThat (result .getAByteArray ()).isNull ();
251+ assertThat (result .getALocalDate ()).isNull ();
252+ assertThat (result .getAnOffsetTime ()).isNull ();
253+ assertThat (result .getALocalTime ()).isNull ();
254+ assertThat (result .getAZoneDateTime ()).isNull ();
255+ assertThat (result .getALocalDateTime ()).isNull ();
256+ assertThat (result .getAnIsoDuration ()).isNull ();
257+ assertThat (result .getAPoint ()).isNull ();
258+ assertThat (result .getAZeroPeriod ()).isNull ();
259+ assertThat (result .getAZeroDuration ()).isNull ();
260+ });
261+ }
262+
239263 public interface ConvertedIDsRepository extends Neo4jRepository <ThingWithUUIDID , UUID > {
240264 }
241265
@@ -248,18 +272,11 @@ public interface AdditionalTypesRepository extends Neo4jRepository<ThingWithAllA
248272 public interface SpatialTypesRepository extends Neo4jRepository <ThingWithAllSpatialTypes , Long > {
249273 }
250274
251- public interface NonExistingPrimitivesRepository extends Neo4jRepository <ThingWithNonExistingPrimitives , Long > {
252- }
253-
254275 public interface CustomTypesRepository extends Neo4jRepository <ThingWithCustomTypes , Long > {
255276
256277 List <ThingWithCustomTypes > findAllByDateAsString (Date theDate );
257278 }
258279
259- public interface ThingWithCompositePropertiesRepository
260- extends Neo4jRepository <ThingWithCompositeProperties , Long > {
261- }
262-
263280 @ Configuration
264281 @ EnableNeo4jRepositories (considerNestedRepositories = true )
265282 @ EnableTransactionManagement
0 commit comments