1515 */
1616package org .springframework .data .jdbc .core .convert ;
1717
18+ import java .util .ArrayList ;
19+ import java .util .Arrays ;
1820import java .util .Collection ;
1921import java .util .Collections ;
2022import java .util .List ;
23+ import java .util .function .Consumer ;
2124
25+ import org .springframework .core .convert .converter .Converter ;
26+ import org .springframework .core .convert .converter .ConverterFactory ;
27+ import org .springframework .core .convert .converter .GenericConverter ;
2228import org .springframework .core .convert .converter .GenericConverter .ConvertiblePair ;
29+ import org .springframework .data .convert .ConverterBuilder ;
2330import org .springframework .data .convert .CustomConversions ;
2431import org .springframework .data .jdbc .core .mapping .JdbcSimpleTypes ;
32+ import org .springframework .data .relational .core .dialect .Dialect ;
33+ import org .springframework .lang .Contract ;
34+ import org .springframework .util .Assert ;
2535
2636/**
2737 * Value object to capture custom conversion. {@link JdbcCustomConversions} also act as factory for
@@ -50,10 +60,10 @@ public JdbcCustomConversions() {
5060 * Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store
5161 * converters.
5262 *
53- * @param converters must not be {@literal null}.
63+ * @param userConverters must not be {@literal null}.
5464 */
55- public JdbcCustomConversions (List <?> converters ) {
56- super ( constructConverterConfiguration ( converters ) );
65+ public JdbcCustomConversions (List <?> userConverters ) {
66+ this ( StoreConversions . of ( JdbcSimpleTypes . HOLDER , STORE_CONVERTERS ), userConverters );
5767 }
5868
5969 /**
@@ -63,12 +73,7 @@ public JdbcCustomConversions(List<?> converters) {
6373 * @since 2.3
6474 */
6575 public JdbcCustomConversions (StoreConversions storeConversions , List <?> userConverters ) {
66-
67- super (new ConverterConfiguration ( //
68- storeConversions , //
69- userConverters , //
70- JdbcCustomConversions ::excludeConversionsBetweenDateAndJsr310Types //
71- ));
76+ super (JdbcConverterConfigurer .from (storeConversions ).registerConverters (userConverters ).createConfiguration ());
7277 }
7378
7479 /**
@@ -82,15 +87,40 @@ public JdbcCustomConversions(ConverterConfiguration converterConfiguration) {
8287 super (converterConfiguration );
8388 }
8489
85- private static ConverterConfiguration constructConverterConfiguration (List <?> converters ) {
90+ /**
91+ * Create a new {@link JdbcCustomConversions} from the given {@link Dialect} and {@code converters}.
92+ *
93+ * @param dialect must not be {@literal null}.
94+ * @param converters must not be {@literal null}.
95+ * @return a new {@link JdbcCustomConversions} instance configured from the given dialect and configured converters.
96+ * @since 4.0
97+ */
98+ public static JdbcCustomConversions of (Dialect dialect , Collection <?> converters ) {
99+
100+ Assert .notNull (dialect , "Dialect must not be null" );
101+ Assert .notNull (converters , "Converters must not be null" );
86102
87- return new ConverterConfiguration ( //
88- StoreConversions .of (JdbcSimpleTypes .HOLDER , STORE_CONVERTERS ), //
89- converters , //
90- JdbcCustomConversions ::excludeConversionsBetweenDateAndJsr310Types //
91- );
103+ return create (dialect , configurer -> configurer .registerConverters (converters ));
92104 }
93105
106+ /**
107+ * Create a new {@link JdbcCustomConversions} instance using the given {@link Dialect} and
108+ * {@link JdbcConverterConfigurer} callback to configure converters.
109+ *
110+ * @param dialect the {@link Dialect} to use, must not be {@literal null}.
111+ * @param configurer the configurer callback to configure converters, must not be {@literal null}.
112+ * @return a new {@link JdbcCustomConversions} instance configured from the given dialect and configured converters.
113+ */
114+ public static JdbcCustomConversions create (Dialect dialect , Consumer <JdbcConverterConfigurer > configurer ) {
115+
116+ Assert .notNull (dialect , "Dialect must not be null" );
117+ Assert .notNull (configurer , "JdbcConverterConfigurer Consumer must not be null" );
118+
119+ JdbcConverterConfigurer converterConfigurer = JdbcConverterConfigurer .from (dialect );
120+ configurer .accept (converterConfigurer );
121+
122+ return new JdbcCustomConversions (converterConfigurer .createConfiguration ());
123+ }
94124
95125 /**
96126 * Obtain a read only copy of default store converters.
@@ -118,4 +148,112 @@ private static boolean isDateTimeApiConversion(ConvertiblePair cp) {
118148 private static boolean excludeConversionsBetweenDateAndJsr310Types (ConvertiblePair cp ) {
119149 return !isDateTimeApiConversion (cp );
120150 }
151+
152+ /**
153+ * {@link JdbcConverterConfigurer} encapsulates creation of
154+ * {@link org.springframework.data.convert.CustomConversions.ConverterConfiguration} with JDBC specifics.
155+ *
156+ * @author Mark Paluch
157+ * @since 4.0
158+ */
159+ public static class JdbcConverterConfigurer {
160+
161+ private final StoreConversions storeConversions ;
162+ private final List <Object > customConverters = new ArrayList <>();
163+
164+ private JdbcConverterConfigurer (StoreConversions storeConversions ) {
165+ this .storeConversions = storeConversions ;
166+ }
167+
168+ /**
169+ * Create a {@link JdbcConverterConfigurer} using the provided {@code dialect} and our own codecs for JSR-310 types.
170+ *
171+ * @param dialect must not be {@literal null}.
172+ * @return
173+ */
174+ static JdbcConverterConfigurer from (Dialect dialect ) {
175+
176+ List <Object > converters = new ArrayList <>();
177+ converters .addAll (dialect .getConverters ());
178+ converters .addAll (JdbcCustomConversions .storeConverters ());
179+
180+ StoreConversions storeConversions = StoreConversions .of (JdbcSimpleTypes .HOLDER , converters );
181+
182+ return new JdbcConverterConfigurer (storeConversions );
183+ }
184+
185+ /**
186+ * Create a {@link JdbcConverterConfigurer} using the provided {@code storeConversions}.
187+ *
188+ * @param storeConversions must not be {@literal null}.
189+ * @return
190+ */
191+ static JdbcConverterConfigurer from (StoreConversions storeConversions ) {
192+ return new JdbcConverterConfigurer (storeConversions );
193+ }
194+
195+ /**
196+ * Add a custom {@link Converter} implementation.
197+ *
198+ * @param converter must not be {@literal null}.
199+ * @return this.
200+ */
201+ @ Contract ("_ -> this" )
202+ public JdbcConverterConfigurer registerConverter (Converter <?, ?> converter ) {
203+
204+ Assert .notNull (converter , "Converter must not be null" );
205+ customConverters .add (converter );
206+ return this ;
207+ }
208+
209+ /**
210+ * Add {@link Converter converters}, {@link ConverterFactory factories}, {@link ConverterBuilder.ConverterAware
211+ * converter-aware objects}, and {@link GenericConverter generic converters}.
212+ *
213+ * @param converters must not be {@literal null} nor contain {@literal null} values.
214+ * @return this.
215+ */
216+ @ Contract ("_ -> this" )
217+ public JdbcConverterConfigurer registerConverters (Object ... converters ) {
218+ return registerConverters (Arrays .asList (converters ));
219+ }
220+
221+ /**
222+ * Add {@link Converter converters}, {@link ConverterFactory factories}, {@link ConverterBuilder.ConverterAware
223+ * converter-aware objects}, and {@link GenericConverter generic converters}.
224+ *
225+ * @param converters must not be {@literal null} nor contain {@literal null} values.
226+ * @return this.
227+ */
228+ @ Contract ("_ -> this" )
229+ public JdbcConverterConfigurer registerConverters (Collection <?> converters ) {
230+
231+ Assert .notNull (converters , "Converters must not be null" );
232+ Assert .noNullElements (converters , "Converters must not be null nor contain null values" );
233+
234+ customConverters .addAll (converters );
235+ return this ;
236+ }
237+
238+ /**
239+ * Add a custom {@link ConverterFactory} implementation.
240+ *
241+ * @param converterFactory must not be {@literal null}.
242+ * @return this.
243+ */
244+ @ Contract ("_ -> this" )
245+ public JdbcConverterConfigurer registerConverterFactory (ConverterFactory <?, ?> converterFactory ) {
246+
247+ Assert .notNull (converterFactory , "ConverterFactory must not be null" );
248+ customConverters .add (converterFactory );
249+ return this ;
250+ }
251+
252+ ConverterConfiguration createConfiguration () {
253+ return new ConverterConfiguration (storeConversions , this .customConverters ,
254+ JdbcCustomConversions ::excludeConversionsBetweenDateAndJsr310Types );
255+ }
256+
257+ }
258+
121259}
0 commit comments