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