1616package org .springframework .data .jdbc .repository .config ;
1717
1818import java .util .ArrayList ;
19+ import java .util .Collection ;
1920import java .util .Collections ;
21+ import java .util .HashSet ;
2022import java .util .List ;
2123import java .util .Optional ;
24+ import java .util .Set ;
2225
2326import org .apache .commons .logging .Log ;
2427import org .apache .commons .logging .LogFactory ;
28+
2529import org .springframework .beans .BeansException ;
2630import org .springframework .beans .factory .NoSuchBeanDefinitionException ;
31+ import org .springframework .beans .factory .config .BeanDefinition ;
2732import org .springframework .context .ApplicationContext ;
2833import org .springframework .context .ApplicationContextAware ;
2934import org .springframework .context .annotation .Bean ;
35+ import org .springframework .context .annotation .ClassPathScanningCandidateComponentProvider ;
3036import org .springframework .context .annotation .Configuration ;
3137import org .springframework .context .annotation .Lazy ;
3238import org .springframework .core .convert .converter .Converter ;
39+ import org .springframework .core .type .filter .AnnotationTypeFilter ;
3340import org .springframework .data .convert .CustomConversions ;
3441import org .springframework .data .jdbc .core .JdbcAggregateOperations ;
3542import org .springframework .data .jdbc .core .JdbcAggregateTemplate ;
3643import org .springframework .data .jdbc .core .convert .*;
37- import org .springframework .data .jdbc .core .convert .JdbcArrayColumns ;
3844import org .springframework .data .jdbc .core .dialect .JdbcDialect ;
3945import org .springframework .data .jdbc .core .mapping .JdbcMappingContext ;
4046import org .springframework .data .jdbc .core .mapping .JdbcSimpleTypes ;
4147import org .springframework .data .mapping .model .SimpleTypeHolder ;
48+ import org .springframework .data .relational .RelationalManagedTypes ;
4249import org .springframework .data .relational .core .conversion .RelationalConverter ;
4350import org .springframework .data .relational .core .dialect .Dialect ;
4451import org .springframework .data .relational .core .mapping .NamingStrategy ;
52+ import org .springframework .data .relational .core .mapping .Table ;
4553import org .springframework .jdbc .core .namedparam .NamedParameterJdbcOperations ;
54+ import org .springframework .util .ClassUtils ;
55+ import org .springframework .util .StringUtils ;
4656
4757/**
4858 * Beans that must be registered for Spring Data JDBC to work.
@@ -63,19 +73,50 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
6373
6474 private ApplicationContext applicationContext ;
6575
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+
66104 /**
67105 * Register a {@link JdbcMappingContext} and apply an optional {@link NamingStrategy}.
68106 *
69107 * @param namingStrategy optional {@link NamingStrategy}. Use {@link NamingStrategy#INSTANCE} as fallback.
70108 * @param customConversions see {@link #jdbcCustomConversions()}.
109+ * @param jdbcManagedTypes JDBC managed types, typically discovered through {@link #jdbcManagedTypes() an entity
110+ * scan}.
71111 * @return must not be {@literal null}.
72112 */
73113 @ Bean
74114 public JdbcMappingContext jdbcMappingContext (Optional <NamingStrategy > namingStrategy ,
75- JdbcCustomConversions customConversions ) {
115+ JdbcCustomConversions customConversions , RelationalManagedTypes jdbcManagedTypes ) {
76116
77117 JdbcMappingContext mappingContext = new JdbcMappingContext (namingStrategy .orElse (NamingStrategy .INSTANCE ));
78118 mappingContext .setSimpleTypeHolder (customConversions .getSimpleTypeHolder ());
119+ mappingContext .setManagedTypes (jdbcManagedTypes );
79120
80121 return mappingContext ;
81122 }
@@ -190,4 +231,56 @@ public Dialect jdbcDialect(NamedParameterJdbcOperations operations) {
190231 public void setApplicationContext (ApplicationContext applicationContext ) throws BeansException {
191232 this .applicationContext = applicationContext ;
192233 }
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+ }
193286}
0 commit comments