3838
3939import org .apiguardian .api .API ;
4040import org .neo4j .cypherdsl .core .Statement ;
41- import org .neo4j .driver .internal .types .InternalTypeSystem ;
4241import org .neo4j .driver .types .TypeSystem ;
4342import org .springframework .beans .BeanUtils ;
4443import org .springframework .beans .BeansException ;
6665import org .springframework .data .neo4j .core .schema .IdGenerator ;
6766import org .springframework .data .neo4j .core .schema .Node ;
6867import org .springframework .data .neo4j .core .schema .PostLoad ;
68+ import org .springframework .data .util .Lazy ;
6969import org .springframework .data .util .TypeInformation ;
7070import org .springframework .lang .Nullable ;
7171import org .springframework .util .ReflectionUtils ;
@@ -117,26 +117,67 @@ public final class Neo4jMappingContext extends AbstractMappingContext<Neo4jPersi
117117
118118 private boolean strict = false ;
119119
120- public Neo4jMappingContext () {
120+ private final Lazy <PersistentPropertyCharacteristicsProvider > propertyCharacteristicsProvider ;
121+
122+ /**
123+ * A builder for creating custom instances of a {@link Neo4jMappingContext}.
124+ * @since 6.3.7
125+ */
126+ public static class Builder {
127+
128+ private Neo4jConversions neo4jConversions ;
129+
130+ @ Nullable
131+ private TypeSystem typeSystem ;
121132
122- this (new Neo4jConversions ());
133+ @ Nullable
134+ private PersistentPropertyCharacteristicsProvider persistentPropertyCharacteristicsProvider ;
135+
136+ private Builder () {
137+ this (new Neo4jConversions (), null , null );
138+ }
139+
140+ private Builder (Neo4jConversions neo4jConversions , @ Nullable TypeSystem typeSystem , @ Nullable PersistentPropertyCharacteristicsProvider persistentPropertyCharacteristicsProvider ) {
141+ this .neo4jConversions = neo4jConversions ;
142+ this .typeSystem = typeSystem ;
143+ this .persistentPropertyCharacteristicsProvider = persistentPropertyCharacteristicsProvider ;
144+ }
145+
146+ @ SuppressWarnings ("HiddenField" )
147+ public Builder withNeo4jConversions (@ Nullable Neo4jConversions neo4jConversions ) {
148+ this .neo4jConversions = neo4jConversions ;
149+ return this ;
150+ }
151+
152+ @ SuppressWarnings ("HiddenField" )
153+ public Builder withPersistentPropertyCharacteristicsProvider (@ Nullable PersistentPropertyCharacteristicsProvider persistentPropertyCharacteristicsProvider ) {
154+ this .persistentPropertyCharacteristicsProvider = persistentPropertyCharacteristicsProvider ;
155+ return this ;
156+ }
157+
158+ @ SuppressWarnings ("HiddenField" )
159+ public Builder withTypeSystem (@ Nullable TypeSystem typeSystem ) {
160+ this .typeSystem = typeSystem ;
161+ return this ;
162+ }
163+
164+ public Neo4jMappingContext build () {
165+ return new Neo4jMappingContext (this );
166+ }
123167 }
124168
125- public Neo4jMappingContext (Neo4jConversions neo4jConversions ) {
169+ public static Builder builder () {
170+ return new Builder ();
171+ }
172+
173+ public Neo4jMappingContext () {
126174
127- this (neo4jConversions , null );
175+ this (new Builder () );
128176 }
129177
130- /**
131- * We need to set the context to non-strict in case we must dynamically add parent classes. As there is no
132- * way to access the original value without reflection, we track its change.
133- *
134- * @param strict The new value for the strict setting.
135- */
136- @ Override
137- public void setStrict (boolean strict ) {
138- super .setStrict (strict );
139- this .strict = strict ;
178+ public Neo4jMappingContext (Neo4jConversions neo4jConversions ) {
179+
180+ this (new Builder (neo4jConversions , null , null ));
140181 }
141182
142183 /**
@@ -145,17 +186,40 @@ public void setStrict(boolean strict) {
145186 *
146187 * @param neo4jConversions The conversions to be used
147188 * @param typeSystem The current drivers type system. If this is null, we use the default one without accessing the driver.
189+ * @deprecated Use {@link Neo4jMappingContext#builder()}
148190 */
149191 @ API (status = API .Status .INTERNAL , since = "6.0" )
192+ @ Deprecated (since = "6.3.7" , forRemoval = true )
150193 public Neo4jMappingContext (Neo4jConversions neo4jConversions , @ Nullable TypeSystem typeSystem ) {
194+ this (new Builder (neo4jConversions , typeSystem , null ));
195+ }
196+
197+ private Neo4jMappingContext (Builder builder ) {
151198
152- this .conversionService = new DefaultNeo4jConversionService (neo4jConversions );
153- this .typeSystem = typeSystem == null ? InternalTypeSystem . TYPE_SYSTEM : typeSystem ;
199+ this .conversionService = new DefaultNeo4jConversionService (builder . neo4jConversions );
200+ this .typeSystem = builder . typeSystem == null ? TypeSystem . getDefault () : builder . typeSystem ;
154201 this .eventSupport = EventSupport .useExistingCallbacks (this , EntityCallbacks .create ());
155202
156- super .setSimpleTypeHolder (neo4jConversions .getSimpleTypeHolder ());
203+ super .setSimpleTypeHolder (builder .neo4jConversions .getSimpleTypeHolder ());
204+
205+ PersistentPropertyCharacteristicsProvider characteristicsProvider = builder .persistentPropertyCharacteristicsProvider ;
206+ this .propertyCharacteristicsProvider = Lazy .of (() -> characteristicsProvider != null || this .beanFactory == null ?
207+ characteristicsProvider : this .beanFactory .getBeanProvider (PersistentPropertyCharacteristicsProvider .class ).getIfUnique ());
157208 }
158209
210+ /**
211+ * We need to set the context to non-strict in case we must dynamically add parent classes. As there is no
212+ * way to access the original value without reflection, we track its change.
213+ *
214+ * @param strict The new value for the strict setting.
215+ */
216+ @ Override
217+ public void setStrict (boolean strict ) {
218+ super .setStrict (strict );
219+ this .strict = strict ;
220+ }
221+
222+
159223 public Neo4jEntityConverter getEntityConverter () {
160224 return new DefaultNeo4jEntityConverter (INSTANTIATORS , nodeDescriptionStore , conversionService , eventSupport ,
161225 typeSystem );
@@ -260,7 +324,12 @@ private static boolean isValidParentNode(@Nullable Class<?> parentClass) {
260324 protected Neo4jPersistentProperty createPersistentProperty (Property property , Neo4jPersistentEntity <?> owner ,
261325 SimpleTypeHolder simpleTypeHolder ) {
262326
263- return new DefaultNeo4jPersistentProperty (property , owner , this , simpleTypeHolder );
327+ PersistentPropertyCharacteristics optionalCharacteristics = this .propertyCharacteristicsProvider
328+ .getOptional ()
329+ .flatMap (provider -> Optional .ofNullable (provider .apply (property , owner )))
330+ .orElse (null );
331+
332+ return new DefaultNeo4jPersistentProperty (property , owner , this , simpleTypeHolder , optionalCharacteristics );
264333 }
265334
266335 @ Override
@@ -278,9 +347,9 @@ public NodeDescription<?> getNodeDescription(Class<?> underlyingClass) {
278347 @ Nullable
279348 public Neo4jPersistentEntity <?> getPersistentEntity (TypeInformation <?> typeInformation ) {
280349
281- NodeDescription <?> existingDescription = this .doGetPersistentEntity (typeInformation );
350+ Neo4jPersistentEntity <?> existingDescription = this .doGetPersistentEntity (typeInformation );
282351 if (existingDescription != null ) {
283- return ( Neo4jPersistentEntity <?>) existingDescription ;
352+ return existingDescription ;
284353 }
285354 return super .getPersistentEntity (typeInformation );
286355 }
0 commit comments