1515 */
1616package org.springframework.data.repository.init;
1717
18+ import reactor.core.publisher.Flux;
19+ import reactor.core.publisher.Mono;
20+
1821import java.io.IOException;
22+ import java.lang.reflect.Method;
1923import java.util.Arrays;
2024import java.util.Collection;
2125import java.util.Collections;
26+ import java.util.HashMap;
27+ import java.util.Map;
2228
2329import org.apache.commons.logging.Log;
2430import org.apache.commons.logging.LogFactory;
31+ import org.reactivestreams.Publisher;
32+
2533import org.springframework.context.ApplicationEventPublisher;
2634import org.springframework.context.ApplicationEventPublisherAware;
2735import org.springframework.core.io.Resource;
2836import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
2937import org.springframework.core.io.support.ResourcePatternResolver;
30- import org.springframework.data.repository.support.DefaultRepositoryInvokerFactory;
38+ import org.springframework.data.repository.CrudRepository;
39+ import org.springframework.data.repository.core.CrudMethods;
40+ import org.springframework.data.repository.core.RepositoryInformation;
41+ import org.springframework.data.repository.core.RepositoryMetadata;
42+ import org.springframework.data.repository.core.support.DefaultCrudMethods;
43+ import org.springframework.data.repository.reactive.ReactiveCrudRepository;
3144import org.springframework.data.repository.support.Repositories;
32- import org.springframework.data.repository.support.RepositoryInvoker;
33- import org.springframework.data.repository.support.RepositoryInvokerFactory;
45+ import org.springframework.data.repository.util.ReactiveWrapperConverters;
3446import org.springframework.lang.Nullable;
3547import org.springframework.util.Assert;
48+ import org.springframework.util.ReflectionUtils;
3649
3750/**
3851 * A {@link RepositoryPopulator} using a {@link ResourceReader} to read objects from the configured {@link Resource}s.
3952 *
4053 * @author Oliver Gierke
4154 * @author Christoph Strobl
55+ * @author Mark Paluch
4256 * @since 1.4
4357 */
4458public class ResourceReaderRepositoryPopulator implements RepositoryPopulator, ApplicationEventPublisherAware {
4559
46- private static final Log logger = LogFactory.getLog(ResourceReaderRepositoryPopulator.class);
60+ private static final Log logger = LogFactory.getLog(ResourceReaderRepositoryPopulator.class);
4761
4862 private final ResourceReader reader;
4963 private final @Nullable ClassLoader classLoader;
@@ -114,7 +128,7 @@ public void populate(Repositories repositories) {
114128
115129 Assert.notNull(repositories, "Repositories must not be null!");
116130
117- RepositoryInvokerFactory invokerFactory = new DefaultRepositoryInvokerFactory (repositories);
131+ RepositoryPersisterFactory persisterFactory = new RepositoryPersisterFactory (repositories);
118132
119133 for (Resource resource : resources) {
120134
@@ -125,13 +139,13 @@ public void populate(Repositories repositories) {
125139 if (result instanceof Collection) {
126140 for (Object element : (Collection<?>) result) {
127141 if (element != null) {
128- persist(element, invokerFactory );
142+ persist(element, persisterFactory );
129143 } else {
130144 logger.info("Skipping null element found in unmarshal result!");
131145 }
132146 }
133147 } else {
134- persist(result, invokerFactory );
148+ persist(result, persisterFactory );
135149 }
136150 }
137151
@@ -158,12 +172,172 @@ private Object readObjectFrom(Resource resource) {
158172 * Persists the given {@link Object} using a suitable repository.
159173 *
160174 * @param object must not be {@literal null}.
161- * @param invokerFactory must not be {@literal null}.
175+ * @param persisterFactory must not be {@literal null}.
176+ */
177+ private void persist(Object object, RepositoryPersisterFactory persisterFactory) {
178+
179+ RepositoryPersister persister = persisterFactory.getPersisterFor(object.getClass());
180+ logger.debug(String.format("Persisting %s using repository %s", object, persister));
181+ persister.save(object);
182+ }
183+
184+ /**
185+ * Factory to create {@link RepositoryPersister} instances.
186+ */
187+ static class RepositoryPersisterFactory {
188+
189+ private final Map<Class<?>, RepositoryPersister> persisters = new HashMap<>();
190+ private final Repositories repositories;
191+
192+ public RepositoryPersisterFactory(Repositories repositories) {
193+ this.repositories = repositories;
194+ }
195+
196+ /**
197+ * Obtain a {@link RepositoryPersister}.
198+ *
199+ * @param domainType
200+ * @return
201+ */
202+ public RepositoryPersister getPersisterFor(Class<?> domainType) {
203+ return persisters.computeIfAbsent(domainType, this::createPersisterFor);
204+ }
205+
206+ private RepositoryPersister createPersisterFor(Class<?> domainType) {
207+
208+ RepositoryInformation repositoryInformation = repositories.getRequiredRepositoryInformation(domainType);
209+ Object repository = repositories.getRepositoryFor(domainType).orElseThrow(
210+ () -> new IllegalArgumentException(String.format("No repository found for domain type: %s", domainType)));
211+
212+ if (repositoryInformation.isReactiveRepository()) {
213+ return repository instanceof ReactiveCrudRepository ? new ReactiveCrudRepositoryPersister(repository)
214+ : new ReflectiveReactivePersister(repositoryInformation, repository);
215+ }
216+
217+ if (repository instanceof CrudRepository) {
218+ return new CrudRepositoryPersister(repository);
219+ }
220+
221+ return new ReflectivePersister(repositoryInformation, repository);
222+ }
223+ }
224+
225+ /**
226+ * Interface defining a save method to persist an object within a repository.
227+ */
228+ interface RepositoryPersister {
229+
230+ /**
231+ * Saves the {@code object} in an appropriate repository.
232+ *
233+ * @param object
234+ */
235+ void save(Object object);
236+ }
237+
238+ /**
239+ * Reflection variant of a {@link RepositoryPersister}.
240+ */
241+ private static class ReflectivePersister implements RepositoryPersister {
242+
243+ private final CrudMethods methods;
244+ private final Object repository;
245+
246+ public ReflectivePersister(RepositoryMetadata metadata, Object repository) {
247+ this.methods = new DefaultCrudMethods(metadata);
248+ this.repository = repository;
249+ }
250+
251+ @Override
252+ public void save(Object object) {
253+
254+ doPersist(object);
255+ }
256+
257+ Object doPersist(Object object) {
258+ Method method = methods.getSaveMethod()//
259+ .orElseThrow(() -> new IllegalStateException("Repository doesn't have a save-method declared!"));
260+
261+ return ReflectionUtils.invokeMethod(method, repository, object);
262+ }
263+
264+ @Override
265+ public String toString() {
266+ return repository.toString();
267+ }
268+ }
269+
270+ /**
271+ * Reactive extension to save objects in a reactive repository.
272+ */
273+ private static class ReflectiveReactivePersister extends ReflectivePersister {
274+
275+ public ReflectiveReactivePersister(RepositoryMetadata metadata, Object repository) {
276+ super(metadata, repository);
277+ }
278+
279+ @Override
280+ public void save(Object object) {
281+
282+ Object wrapper = doPersist(object);
283+
284+ Publisher<?> publisher = ReactiveWrapperConverters.toWrapper(wrapper, Publisher.class);
285+
286+ if (!(publisher instanceof Mono)) {
287+ publisher = Flux.from(publisher).collectList();
288+ }
289+
290+ Mono.from(publisher).block();
291+ }
292+ }
293+
294+ /**
295+ * {@link RepositoryPersister} to operate with {@link CrudRepository}.
162296 */
163- private void persist(Object object, RepositoryInvokerFactory invokerFactory) {
297+ private static class CrudRepositoryPersister implements RepositoryPersister {
164298
165- RepositoryInvoker invoker = invokerFactory.getInvokerFor(object.getClass());
166- logger.debug(String.format("Persisting %s using repository %s", object, invoker));
167- invoker.invokeSave(object);
299+ private final CrudRepository<Object, Object> repository;
300+
301+ @SuppressWarnings("unchecked")
302+ public CrudRepositoryPersister(Object repository) {
303+
304+ Assert.isInstanceOf(CrudRepository.class, repository);
305+ this.repository = (CrudRepository<Object, Object>) repository;
306+ }
307+
308+ @Override
309+ public void save(Object object) {
310+ repository.save(object);
311+ }
312+
313+ @Override
314+ public String toString() {
315+ return repository.toString();
316+ }
317+ }
318+
319+ /**
320+ * {@link RepositoryPersister} to operate with {@link ReactiveCrudRepository}.
321+ */
322+ private static class ReactiveCrudRepositoryPersister implements RepositoryPersister {
323+
324+ private final ReactiveCrudRepository<Object, Object> repository;
325+
326+ @SuppressWarnings("unchecked")
327+ public ReactiveCrudRepositoryPersister(Object repository) {
328+
329+ Assert.isInstanceOf(ReactiveCrudRepository.class, repository);
330+ this.repository = (ReactiveCrudRepository<Object, Object>) repository;
331+ }
332+
333+ @Override
334+ public void save(Object object) {
335+ repository.save(object).block();
336+ }
337+
338+ @Override
339+ public String toString() {
340+ return repository.toString();
341+ }
168342 }
169343}
0 commit comments