|
16 | 16 | package org.springframework.data.jdbc.repository.support;
|
17 | 17 |
|
18 | 18 | import java.io.Serializable;
|
19 |
| -import java.util.List; |
20 |
| -import java.util.Map; |
21 |
| -import java.util.Optional; |
22 |
| -import java.util.stream.Collectors; |
23 |
| -import java.util.stream.Stream; |
24 | 19 |
|
25 |
| -import javax.sql.DataSource; |
26 |
| - |
27 |
| -import org.apache.ibatis.session.SqlSessionFactory; |
28 |
| -import org.springframework.context.ApplicationContext; |
| 20 | +import org.springframework.beans.factory.annotation.Autowired; |
29 | 21 | import org.springframework.context.ApplicationEventPublisher;
|
30 |
| -import org.springframework.data.jdbc.core.CascadingDataAccessStrategy; |
| 22 | +import org.springframework.context.ApplicationEventPublisherAware; |
31 | 23 | import org.springframework.data.jdbc.core.DataAccessStrategy;
|
32 |
| -import org.springframework.data.jdbc.core.DefaultDataAccessStrategy; |
33 |
| -import org.springframework.data.jdbc.core.DelegatingDataAccessStrategy; |
34 |
| -import org.springframework.data.jdbc.core.SqlGeneratorSource; |
35 |
| -import org.springframework.data.jdbc.mapping.model.ConversionCustomizer; |
36 |
| -import org.springframework.data.jdbc.mapping.model.DefaultNamingStrategy; |
37 | 24 | import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
|
38 |
| -import org.springframework.data.jdbc.mapping.model.NamingStrategy; |
39 |
| -import org.springframework.data.jdbc.mybatis.MyBatisDataAccessStrategy; |
40 | 25 | import org.springframework.data.repository.Repository;
|
41 | 26 | import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
42 | 27 | import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
|
43 |
| -import org.springframework.data.util.Optionals; |
44 |
| -import org.springframework.jdbc.core.JdbcOperations; |
45 |
| -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; |
46 |
| -import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; |
47 |
| -import org.springframework.util.ClassUtils; |
| 28 | +import org.springframework.util.Assert; |
48 | 29 |
|
49 | 30 | /**
|
50 | 31 | * Special adapter for Springs {@link org.springframework.beans.factory.FactoryBean} interface to allow easy setup of
|
|
55 | 36 | * @since 2.0
|
56 | 37 | */
|
57 | 38 | public class JdbcRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> //
|
58 |
| - extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> { |
59 |
| - |
60 |
| - private static final String NO_NAMED_PARAMETER_JDBC_OPERATION_ERROR_MESSAGE = // |
61 |
| - "No unique NamedParameterJdbcOperation could be found, " // |
62 |
| - + "nor JdbcOperations or DataSource to construct one from."; |
63 |
| - |
64 |
| - private static final String NAMED_PARAMETER_JDBC_OPERATIONS_BEAN_NAME = "namedParameterJdbcTemplate"; |
65 |
| - private static final String JDBC_OPERATIONS_BEAN_NAME = "jdbcTemplate"; |
66 |
| - private static final String DATA_SOURCE_BEAN_NAME = "dataSource"; |
67 |
| - private static final String NAMING_STRATEGY_BEAN_NAME = "namingStrategy"; |
68 |
| - private static final String SQL_SESSION_FACTORY_BEAN_NAME = "sqlSessionFactory"; |
69 |
| - private static final String CONVERSION_CUSTOMIZER_BEAN_NAME = "conversionCustomizer"; |
| 39 | + extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> implements ApplicationEventPublisherAware { |
70 | 40 |
|
71 |
| - private final ApplicationEventPublisher applicationEventPublisher; |
72 |
| - private final ApplicationContext applicationContext; |
73 |
| - |
74 |
| - JdbcRepositoryFactoryBean(Class<? extends T> repositoryInterface, ApplicationEventPublisher applicationEventPublisher, |
75 |
| - ApplicationContext applicationContext) { |
| 41 | + private ApplicationEventPublisher publisher; |
| 42 | + private JdbcMappingContext mappingContext; |
| 43 | + private DataAccessStrategy dataAccessStrategy; |
76 | 44 |
|
| 45 | + JdbcRepositoryFactoryBean(Class<? extends T> repositoryInterface) { |
77 | 46 | super(repositoryInterface);
|
78 |
| - this.applicationEventPublisher = applicationEventPublisher; |
79 |
| - this.applicationContext = applicationContext; |
80 | 47 | }
|
81 | 48 |
|
82 | 49 | @Override
|
83 |
| - protected RepositoryFactorySupport doCreateRepositoryFactory() { |
84 |
| - |
85 |
| - final JdbcMappingContext context = new JdbcMappingContext(findOrCreateNamingStrategy(), findOrCreateConversionCustomizer()); |
86 |
| - |
87 |
| - return new JdbcRepositoryFactory(applicationEventPublisher, context, createDataAccessStrategy(context)); |
| 50 | + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { |
| 51 | + |
| 52 | + super.setApplicationEventPublisher(publisher); |
| 53 | + this.publisher = publisher; |
88 | 54 | }
|
89 | 55 |
|
90 | 56 | /**
|
91 |
| - * <p> |
92 |
| - * Create the {@link DataAccessStrategy}, by combining all applicable strategies into one. |
93 |
| - * </p> |
94 |
| - * <p> |
95 |
| - * The challenge is that the {@link DefaultDataAccessStrategy} when used for reading needs a |
96 |
| - * {@link DataAccessStrategy} for loading referenced entities (see. |
97 |
| - * {@link DefaultDataAccessStrategy#getEntityRowMapper(Class)}. But it should use all configured |
98 |
| - * {@link DataAccessStrategy}s for this. This creates a cyclic dependency. In order to build this the |
99 |
| - * {@link DefaultDataAccessStrategy} gets passed in a {@link DelegatingDataAccessStrategy} which at the end gets set |
100 |
| - * to the full {@link CascadingDataAccessStrategy}. |
101 |
| - * </p> |
| 57 | + * Creates the actual {@link RepositoryFactorySupport} instance. |
| 58 | + * |
| 59 | + * @return |
102 | 60 | */
|
103 |
| - private CascadingDataAccessStrategy createDataAccessStrategy(JdbcMappingContext context) { |
104 |
| - |
105 |
| - DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy(); |
106 |
| - |
107 |
| - List<DataAccessStrategy> accessStrategies = Stream.of( // |
108 |
| - createMyBatisDataAccessStrategy(), // |
109 |
| - createDefaultAccessStrategy(context, delegatingDataAccessStrategy) // |
110 |
| - ) // |
111 |
| - .filter(Optional::isPresent) // |
112 |
| - .map(Optional::get) // |
113 |
| - .collect(Collectors.toList()); |
114 |
| - |
115 |
| - CascadingDataAccessStrategy strategy = new CascadingDataAccessStrategy(accessStrategies); |
116 |
| - delegatingDataAccessStrategy.setDelegate(strategy); |
117 |
| - |
118 |
| - return strategy; |
119 |
| - } |
120 |
| - |
121 |
| - private Optional<DataAccessStrategy> createMyBatisDataAccessStrategy() { |
122 |
| - |
123 |
| - String myBatisSqlSessionFactoryClassName = "org.apache.ibatis.session.SqlSessionFactory"; |
124 |
| - ClassLoader classLoader = this.getClass().getClassLoader(); |
125 |
| - |
126 |
| - if (!ClassUtils.isPresent(myBatisSqlSessionFactoryClassName, classLoader)) { |
127 |
| - return Optional.empty(); |
128 |
| - } |
129 |
| - |
130 |
| - try { |
131 |
| - |
132 |
| - return getBean(classLoader.loadClass(myBatisSqlSessionFactoryClassName), SQL_SESSION_FACTORY_BEAN_NAME) |
133 |
| - // note that the cast to SqlSessionFactory happens in a lambda, which is basically a separate class |
134 |
| - // thus it won't get loaded if this code path doesn't get executed. |
135 |
| - .map(ssf -> new MyBatisDataAccessStrategy((SqlSessionFactory) ssf)); |
136 |
| - } catch (ClassNotFoundException e) { |
137 |
| - throw new IllegalStateException("Detected MyBatis on classpath but failed to load the class " + myBatisSqlSessionFactoryClassName); |
138 |
| - } |
139 |
| - } |
140 |
| - |
141 |
| - private Optional<DataAccessStrategy> createDefaultAccessStrategy(JdbcMappingContext context, |
142 |
| - DelegatingDataAccessStrategy delegatingDataAccessStrategy) { |
143 |
| - |
144 |
| - return Optional.of(new DefaultDataAccessStrategy(new SqlGeneratorSource(context), findOrCreateJdbcOperations(), |
145 |
| - context, delegatingDataAccessStrategy)); |
146 |
| - } |
147 |
| - |
148 |
| - private NamedParameterJdbcOperations findOrCreateJdbcOperations() { |
149 |
| - |
150 |
| - return Optionals.firstNonEmpty( // |
151 |
| - this::getNamedParameterJdbcOperations, // |
152 |
| - () -> getJdbcOperations().map(NamedParameterJdbcTemplate::new), // |
153 |
| - () -> getDataSource().map(NamedParameterJdbcTemplate::new)) // |
154 |
| - .orElseThrow(() -> new IllegalStateException(NO_NAMED_PARAMETER_JDBC_OPERATION_ERROR_MESSAGE)); |
155 |
| - } |
156 |
| - |
157 |
| - private NamingStrategy findOrCreateNamingStrategy() { |
158 |
| - return getNamingStrategy().orElse(new DefaultNamingStrategy()); |
159 |
| - } |
160 |
| - |
161 |
| - private ConversionCustomizer findOrCreateConversionCustomizer() { |
162 |
| - return getConversionCustomizer().orElse(conversionService->{}); |
163 |
| - } |
164 |
| - |
165 |
| - private Optional<NamedParameterJdbcOperations> getNamedParameterJdbcOperations() { |
166 |
| - return getBean(NamedParameterJdbcOperations.class, NAMED_PARAMETER_JDBC_OPERATIONS_BEAN_NAME); |
| 61 | + @Override |
| 62 | + protected RepositoryFactorySupport doCreateRepositoryFactory() { |
| 63 | + return new JdbcRepositoryFactory(publisher, mappingContext, dataAccessStrategy); |
167 | 64 | }
|
168 | 65 |
|
169 |
| - private Optional<JdbcOperations> getJdbcOperations() { |
170 |
| - return getBean(JdbcOperations.class, JDBC_OPERATIONS_BEAN_NAME); |
171 |
| - } |
| 66 | + @Autowired |
| 67 | + protected void setMappingContext(JdbcMappingContext mappingContext) { |
172 | 68 |
|
173 |
| - private Optional<DataSource> getDataSource() { |
174 |
| - return getBean(DataSource.class, DATA_SOURCE_BEAN_NAME); |
| 69 | + super.setMappingContext(mappingContext); |
| 70 | + this.mappingContext = mappingContext; |
175 | 71 | }
|
176 | 72 |
|
177 |
| - private Optional<NamingStrategy> getNamingStrategy() { |
178 |
| - return getBean(NamingStrategy.class, NAMING_STRATEGY_BEAN_NAME); |
| 73 | + @Autowired |
| 74 | + public void setDataAccessStrategy(DataAccessStrategy dataAccessStrategy) { |
| 75 | + this.dataAccessStrategy = dataAccessStrategy; |
179 | 76 | }
|
180 | 77 |
|
181 |
| - private Optional<ConversionCustomizer> getConversionCustomizer() { |
182 |
| - return getBean(ConversionCustomizer.class, CONVERSION_CUSTOMIZER_BEAN_NAME); |
183 |
| - } |
184 |
| - |
185 |
| - private <R> Optional<R> getBean(Class<R> type, String name) { |
186 |
| - |
187 |
| - Map<String, R> beansOfType = applicationContext.getBeansOfType(type); |
188 |
| - |
189 |
| - if (beansOfType.size() == 1) { |
190 |
| - return beansOfType.values().stream().findFirst(); |
191 |
| - } |
| 78 | + @Override |
| 79 | + public void afterPropertiesSet() { |
192 | 80 |
|
193 |
| - return Optional.ofNullable(beansOfType.get(name)); |
| 81 | + Assert.notNull(this.dataAccessStrategy, "DataAccessStrategy must not be null!"); |
| 82 | + Assert.notNull(this.mappingContext, "MappingContext must not be null!"); |
| 83 | + super.afterPropertiesSet(); |
194 | 84 | }
|
195 | 85 | }
|
0 commit comments