16
16
package org .springframework .data .jdbc .repository .config ;
17
17
18
18
import java .util .ArrayList ;
19
+ import java .util .Collection ;
19
20
import java .util .Collections ;
21
+ import java .util .HashSet ;
20
22
import java .util .List ;
21
23
import java .util .Optional ;
24
+ import java .util .Set ;
22
25
23
26
import org .apache .commons .logging .Log ;
24
27
import org .apache .commons .logging .LogFactory ;
28
+
25
29
import org .springframework .beans .BeansException ;
26
30
import org .springframework .beans .factory .NoSuchBeanDefinitionException ;
31
+ import org .springframework .beans .factory .config .BeanDefinition ;
27
32
import org .springframework .context .ApplicationContext ;
28
33
import org .springframework .context .ApplicationContextAware ;
29
34
import org .springframework .context .annotation .Bean ;
35
+ import org .springframework .context .annotation .ClassPathScanningCandidateComponentProvider ;
30
36
import org .springframework .context .annotation .Configuration ;
31
37
import org .springframework .context .annotation .Lazy ;
32
38
import org .springframework .core .convert .converter .Converter ;
39
+ import org .springframework .core .type .filter .AnnotationTypeFilter ;
33
40
import org .springframework .data .convert .CustomConversions ;
34
41
import org .springframework .data .jdbc .core .JdbcAggregateOperations ;
35
42
import org .springframework .data .jdbc .core .JdbcAggregateTemplate ;
36
43
import org .springframework .data .jdbc .core .convert .*;
37
- import org .springframework .data .jdbc .core .convert .JdbcArrayColumns ;
38
44
import org .springframework .data .jdbc .core .dialect .JdbcDialect ;
39
45
import org .springframework .data .jdbc .core .mapping .JdbcMappingContext ;
40
46
import org .springframework .data .jdbc .core .mapping .JdbcSimpleTypes ;
41
47
import org .springframework .data .mapping .model .SimpleTypeHolder ;
48
+ import org .springframework .data .relational .RelationalManagedTypes ;
42
49
import org .springframework .data .relational .core .conversion .RelationalConverter ;
43
50
import org .springframework .data .relational .core .dialect .Dialect ;
44
51
import org .springframework .data .relational .core .mapping .NamingStrategy ;
52
+ import org .springframework .data .relational .core .mapping .Table ;
45
53
import org .springframework .jdbc .core .namedparam .NamedParameterJdbcOperations ;
54
+ import org .springframework .util .ClassUtils ;
55
+ import org .springframework .util .StringUtils ;
46
56
47
57
/**
48
58
* Beans that must be registered for Spring Data JDBC to work.
@@ -63,19 +73,50 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
63
73
64
74
private ApplicationContext applicationContext ;
65
75
76
+ /**
77
+ * Returns the base packages to scan for JDBC mapped entities at startup. Returns the package name of the
78
+ * configuration class' (the concrete class, not this one here) by default. So if you have a
79
+ * {@code com.acme.AppConfig} extending {@link AbstractJdbcConfiguration} the base package will be considered
80
+ * {@code com.acme} unless the method is overridden to implement alternate behavior.
81
+ *
82
+ * @return the base packages to scan for mapped {@link Table} classes or an empty collection to not enable scanning
83
+ * for entities.
84
+ * @since 3.0
85
+ */
86
+ protected Collection <String > getMappingBasePackages () {
87
+
88
+ Package mappingBasePackage = getClass ().getPackage ();
89
+ return Collections .singleton (mappingBasePackage == null ? null : mappingBasePackage .getName ());
90
+ }
91
+
92
+ /**
93
+ * Returns the a {@link RelationalManagedTypes} object holding the initial entity set.
94
+ *
95
+ * @return new instance of {@link RelationalManagedTypes}.
96
+ * @throws ClassNotFoundException
97
+ * @since 3.0
98
+ */
99
+ @ Bean
100
+ public RelationalManagedTypes jdbcManagedTypes () throws ClassNotFoundException {
101
+ return RelationalManagedTypes .fromIterable (getInitialEntitySet ());
102
+ }
103
+
66
104
/**
67
105
* Register a {@link JdbcMappingContext} and apply an optional {@link NamingStrategy}.
68
106
*
69
107
* @param namingStrategy optional {@link NamingStrategy}. Use {@link NamingStrategy#INSTANCE} as fallback.
70
108
* @param customConversions see {@link #jdbcCustomConversions()}.
109
+ * @param jdbcManagedTypes JDBC managed types, typically discovered through {@link #jdbcManagedTypes() an entity
110
+ * scan}.
71
111
* @return must not be {@literal null}.
72
112
*/
73
113
@ Bean
74
114
public JdbcMappingContext jdbcMappingContext (Optional <NamingStrategy > namingStrategy ,
75
- JdbcCustomConversions customConversions ) {
115
+ JdbcCustomConversions customConversions , RelationalManagedTypes jdbcManagedTypes ) {
76
116
77
117
JdbcMappingContext mappingContext = new JdbcMappingContext (namingStrategy .orElse (NamingStrategy .INSTANCE ));
78
118
mappingContext .setSimpleTypeHolder (customConversions .getSimpleTypeHolder ());
119
+ mappingContext .setManagedTypes (jdbcManagedTypes );
79
120
80
121
return mappingContext ;
81
122
}
@@ -190,4 +231,56 @@ public Dialect jdbcDialect(NamedParameterJdbcOperations operations) {
190
231
public void setApplicationContext (ApplicationContext applicationContext ) throws BeansException {
191
232
this .applicationContext = applicationContext ;
192
233
}
234
+
235
+ /**
236
+ * Scans the mapping base package for classes annotated with {@link Table}. By default, it scans for entities in all
237
+ * packages returned by {@link #getMappingBasePackages()}.
238
+ *
239
+ * @see #getMappingBasePackages()
240
+ * @return
241
+ * @throws ClassNotFoundException
242
+ * @since 3.0
243
+ */
244
+ protected Set <Class <?>> getInitialEntitySet () throws ClassNotFoundException {
245
+
246
+ Set <Class <?>> initialEntitySet = new HashSet <>();
247
+
248
+ for (String basePackage : getMappingBasePackages ()) {
249
+ initialEntitySet .addAll (scanForEntities (basePackage ));
250
+ }
251
+
252
+ return initialEntitySet ;
253
+ }
254
+
255
+ /**
256
+ * Scans the given base package for entities, i.e. JDBC-specific types annotated with {@link Table}.
257
+ *
258
+ * @param basePackage must not be {@literal null}.
259
+ * @return
260
+ * @throws ClassNotFoundException
261
+ * @since 3.0
262
+ */
263
+ protected Set <Class <?>> scanForEntities (String basePackage ) throws ClassNotFoundException {
264
+
265
+ if (!StringUtils .hasText (basePackage )) {
266
+ return Collections .emptySet ();
267
+ }
268
+
269
+ Set <Class <?>> initialEntitySet = new HashSet <>();
270
+
271
+ if (StringUtils .hasText (basePackage )) {
272
+
273
+ ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider (
274
+ false );
275
+ componentProvider .addIncludeFilter (new AnnotationTypeFilter (Table .class ));
276
+
277
+ for (BeanDefinition candidate : componentProvider .findCandidateComponents (basePackage )) {
278
+
279
+ initialEntitySet
280
+ .add (ClassUtils .forName (candidate .getBeanClassName (), AbstractJdbcConfiguration .class .getClassLoader ()));
281
+ }
282
+ }
283
+
284
+ return initialEntitySet ;
285
+ }
193
286
}
0 commit comments