18
18
import static org .assertj .core .api .Assertions .assertThat ;
19
19
import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
20
20
21
- import java .lang .reflect .Field ;
22
21
import java .text .SimpleDateFormat ;
23
22
import java .time .ZoneId ;
24
23
import java .time .ZonedDateTime ;
41
40
import org .junit .jupiter .api .DynamicTest ;
42
41
import org .junit .jupiter .api .Test ;
43
42
import org .junit .jupiter .api .TestFactory ;
44
- import org .junit .platform .commons .util .AnnotationUtils ;
45
43
import org .neo4j .driver .Driver ;
46
44
import org .neo4j .driver .Session ;
47
45
import org .neo4j .driver .Value ;
53
51
import org .springframework .core .convert .support .DefaultConversionService ;
54
52
import org .springframework .data .mapping .MappingException ;
55
53
import org .springframework .data .neo4j .config .AbstractNeo4jConfig ;
56
- import org .springframework .data .neo4j .core .convert .ConvertWith ;
57
54
import 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 ;
58
57
import org .springframework .data .neo4j .integration .shared .conversion .Neo4jConversionsITBase ;
59
58
import org .springframework .data .neo4j .integration .shared .conversion .ThingWithAllAdditionalTypes ;
60
59
import org .springframework .data .neo4j .integration .shared .common .ThingWithAllCypherTypes ;
61
60
import org .springframework .data .neo4j .integration .shared .common .ThingWithAllSpatialTypes ;
62
- import org .springframework .data .neo4j .integration .shared .conversion .ThingWithCompositeProperties ;
63
61
import org .springframework .data .neo4j .integration .shared .conversion .ThingWithCustomTypes ;
64
- import org .springframework .data .neo4j .integration .shared .common .ThingWithNonExistingPrimitives ;
65
62
import org .springframework .data .neo4j .integration .shared .common .ThingWithUUIDID ;
66
63
import org .springframework .data .neo4j .repository .Neo4jRepository ;
67
64
import org .springframework .data .neo4j .repository .config .EnableNeo4jRepositories ;
68
65
import org .springframework .data .neo4j .test .Neo4jIntegrationTest ;
69
66
import org .springframework .test .util .ReflectionTestUtils ;
70
67
import org .springframework .transaction .annotation .EnableTransactionManagement ;
71
- import org .springframework .util .ReflectionUtils ;
72
68
73
69
/**
74
70
* @author Michael J. Simons
78
74
@ Neo4jIntegrationTest
79
75
class TypeConversionIT extends Neo4jConversionsITBase {
80
76
81
- private final Driver driver ;
82
-
83
- @ Autowired CypherTypesRepository cypherTypesRepository ;
77
+ private final CypherTypesRepository cypherTypesRepository ;
84
78
85
79
private final AdditionalTypesRepository additionalTypesRepository ;
86
80
@@ -90,11 +84,10 @@ class TypeConversionIT extends Neo4jConversionsITBase {
90
84
91
85
private final DefaultConversionService defaultConversionService ;
92
86
93
- @ Autowired TypeConversionIT (Driver driver , CypherTypesRepository cypherTypesRepository ,
87
+ @ Autowired TypeConversionIT (CypherTypesRepository cypherTypesRepository ,
94
88
AdditionalTypesRepository additionalTypesRepository , SpatialTypesRepository spatialTypesRepository ,
95
89
CustomTypesRepository customTypesRepository ,
96
90
Neo4jConversions neo4jConversions ) {
97
- this .driver = driver ;
98
91
this .cypherTypesRepository = cypherTypesRepository ;
99
92
this .additionalTypesRepository = additionalTypesRepository ;
100
93
this .spatialTypesRepository = spatialTypesRepository ;
@@ -104,14 +97,20 @@ class TypeConversionIT extends Neo4jConversionsITBase {
104
97
}
105
98
106
99
@ 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
+ }
108
107
109
108
assertThatExceptionOfType (MappingException .class )
110
- .isThrownBy (() -> repository .findById (ID_OF_NON_EXISTING_PRIMITIVES_NODE ))
109
+ .isThrownBy (() -> cypherTypesRepository .findById (id ))
111
110
.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 );
115
114
}
116
115
117
116
@ TestFactory
@@ -158,10 +157,10 @@ Stream<DynamicNode> conversionsShouldBeAppliedToEntities() {
158
157
() -> assertThat (ReflectionTestUtils .getField (thing , a .getKey ()))
159
158
.isEqualTo (a .getValue ()))));
160
159
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 ))));
165
164
166
165
return DynamicContainer .dynamicContainer (entry .getKey (), Arrays .asList (reads , writes ));
167
166
});
@@ -172,8 +171,6 @@ void assertWrite(Object thing, String fieldName, ConversionService conversionSer
172
171
long id = (long ) ReflectionTestUtils .getField (thing , "id" );
173
172
Object domainValue = ReflectionTestUtils .getField (thing , fieldName );
174
173
175
- Field field = ReflectionUtils .findField (thing .getClass (), fieldName );
176
- Optional <ConvertWith > annotation = AnnotationUtils .findAnnotation (field , ConvertWith .class );
177
174
Function <Object , Value > conversion ;
178
175
if (fieldName .equals ("dateAsLong" )) {
179
176
conversion = o -> Values .value (((Date ) o ).getTime ());
@@ -185,8 +182,7 @@ void assertWrite(Object thing, String fieldName, ConversionService conversionSer
185
182
Value driverValue ;
186
183
if (domainValue != null && Collection .class .isAssignableFrom (domainValue .getClass ())) {
187
184
Collection <?> sourceCollection = (Collection <?>) domainValue ;
188
- Object [] targetCollection = (sourceCollection ).stream ()
189
- .map (element -> conversion .apply (element )).toArray ();
185
+ Object [] targetCollection = (sourceCollection ).stream ().map (conversion ).toArray ();
190
186
driverValue = Values .value (targetCollection );
191
187
} else {
192
188
driverValue = conversion .apply (domainValue );
@@ -236,6 +232,34 @@ void parametersTargetingConvertedAttributesMustBeConverted(@Autowired CustomType
236
232
.hasSizeGreaterThan (0 );
237
233
}
238
234
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
+
239
263
public interface ConvertedIDsRepository extends Neo4jRepository <ThingWithUUIDID , UUID > {
240
264
}
241
265
@@ -248,18 +272,11 @@ public interface AdditionalTypesRepository extends Neo4jRepository<ThingWithAllA
248
272
public interface SpatialTypesRepository extends Neo4jRepository <ThingWithAllSpatialTypes , Long > {
249
273
}
250
274
251
- public interface NonExistingPrimitivesRepository extends Neo4jRepository <ThingWithNonExistingPrimitives , Long > {
252
- }
253
-
254
275
public interface CustomTypesRepository extends Neo4jRepository <ThingWithCustomTypes , Long > {
255
276
256
277
List <ThingWithCustomTypes > findAllByDateAsString (Date theDate );
257
278
}
258
279
259
- public interface ThingWithCompositePropertiesRepository
260
- extends Neo4jRepository <ThingWithCompositeProperties , Long > {
261
- }
262
-
263
280
@ Configuration
264
281
@ EnableNeo4jRepositories (considerNestedRepositories = true )
265
282
@ EnableTransactionManagement
0 commit comments