1
16 package org.springframework.data.repository.support;
17
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Optional;
25 import java.util.Set;
26
27 import org.springframework.beans.factory.BeanFactory;
28 import org.springframework.beans.factory.BeanFactoryUtils;
29 import org.springframework.beans.factory.ListableBeanFactory;
30 import org.springframework.beans.factory.config.BeanDefinition;
31 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
32 import org.springframework.data.mapping.PersistentEntity;
33 import org.springframework.data.mapping.context.MappingContext;
34 import org.springframework.data.repository.core.EntityInformation;
35 import org.springframework.data.repository.core.RepositoryInformation;
36 import org.springframework.data.repository.core.support.RepositoryFactoryInformation;
37 import org.springframework.data.repository.query.QueryMethod;
38 import org.springframework.data.util.ProxyUtils;
39 import org.springframework.util.Assert;
40 import org.springframework.util.ClassUtils;
41
42
50 public class Repositories implements Iterable<Class<?>> {
51
52 static final Repositories NONE = new Repositories();
53
54 private static final RepositoryFactoryInformation<Object, Object> EMPTY_REPOSITORY_FACTORY_INFO = EmptyRepositoryFactoryInformation.INSTANCE;
55 private static final String DOMAIN_TYPE_MUST_NOT_BE_NULL = "Domain type must not be null!";
56
57 private final Optional<BeanFactory> beanFactory;
58 private final Map<Class<?>, String> repositoryBeanNames;
59 private final Map<Class<?>, RepositoryFactoryInformation<Object, Object>> repositoryFactoryInfos;
60
61
64 private Repositories() {
65
66 this.beanFactory = Optional.empty();
67 this.repositoryBeanNames = Collections.emptyMap();
68 this.repositoryFactoryInfos = Collections.emptyMap();
69 }
70
71
77 public Repositories(ListableBeanFactory factory) {
78
79 Assert.notNull(factory, "ListableBeanFactory must not be null!");
80
81 this.beanFactory = Optional.of(factory);
82 this.repositoryFactoryInfos = new HashMap<>();
83 this.repositoryBeanNames = new HashMap<>();
84
85 populateRepositoryFactoryInformation(factory);
86 }
87
88 private void populateRepositoryFactoryInformation(ListableBeanFactory factory) {
89
90 for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, RepositoryFactoryInformation.class,
91 false, false)) {
92 cacheRepositoryFactory(name);
93 }
94 }
95
96 @SuppressWarnings("rawtypes")
97 private synchronized void cacheRepositoryFactory(String name) {
98
99 RepositoryFactoryInformation repositoryFactoryInformation = beanFactory.get().getBean(name,
100 RepositoryFactoryInformation.class);
101 Class<?> domainType = ClassUtils
102 .getUserClass(repositoryFactoryInformation.getRepositoryInformation().getDomainType());
103
104 RepositoryInformation information = repositoryFactoryInformation.getRepositoryInformation();
105 Set<Class<?>> alternativeDomainTypes = information.getAlternativeDomainTypes();
106
107 Set<Class<?>> typesToRegister = new HashSet<>(alternativeDomainTypes.size() + 1);
108 typesToRegister.add(domainType);
109 typesToRegister.addAll(alternativeDomainTypes);
110
111 for (Class<?> type : typesToRegister) {
112 cacheFirstOrPrimary(type, repositoryFactoryInformation, BeanFactoryUtils.transformedBeanName(name));
113 }
114 }
115
116
122 public boolean hasRepositoryFor(Class<?> domainClass) {
123
124 Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
125
126 Class<?> userClass = ProxyUtils.getUserClass(domainClass);
127
128 return repositoryFactoryInfos.containsKey(userClass);
129 }
130
131
137 public Optional<Object> getRepositoryFor(Class<?> domainClass) {
138
139 Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
140
141 Class<?> userClass = ProxyUtils.getUserClass(domainClass);
142 Optional<String> repositoryBeanName = Optional.ofNullable(repositoryBeanNames.get(userClass));
143
144 return beanFactory.flatMap(it -> repositoryBeanName.map(it::getBean));
145 }
146
147
155 private RepositoryFactoryInformation<Object, Object> getRepositoryFactoryInfoFor(Class<?> domainClass) {
156
157 Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
158
159 Class<?> userType = ProxyUtils.getUserClass(domainClass);
160 RepositoryFactoryInformation<Object, Object> repositoryInfo = repositoryFactoryInfos.get(userType);
161
162 if (repositoryInfo != null) {
163 return repositoryInfo;
164 }
165
166 if (!userType.equals(Object.class)) {
167 return getRepositoryFactoryInfoFor(userType.getSuperclass());
168 }
169
170 return EMPTY_REPOSITORY_FACTORY_INFO;
171 }
172
173
179 @SuppressWarnings("unchecked")
180 public <T, S> EntityInformation<T, S> getEntityInformationFor(Class<?> domainClass) {
181
182 Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
183
184 return (EntityInformation<T, S>) getRepositoryFactoryInfoFor(domainClass).getEntityInformation();
185 }
186
187
194 public Optional<RepositoryInformation> getRepositoryInformationFor(Class<?> domainClass) {
195
196 Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
197
198 RepositoryFactoryInformation<Object, Object> information = getRepositoryFactoryInfoFor(domainClass);
199 return information == EMPTY_REPOSITORY_FACTORY_INFO ? Optional.empty()
200 : Optional.of(information.getRepositoryInformation());
201 }
202
203
210 public RepositoryInformation getRequiredRepositoryInformation(Class<?> domainType) {
211
212 return getRepositoryInformationFor(domainType).orElseThrow(() -> new IllegalArgumentException(
213 "No required RepositoryInformation found for domain type " + domainType.getName() + "!"));
214 }
215
216
224 public Optional<RepositoryInformation> getRepositoryInformation(Class<?> repositoryInterface) {
225
226 return repositoryFactoryInfos.values().stream()
227 .map(RepositoryFactoryInformation::getRepositoryInformation)
228 .filter(information -> information.getRepositoryInterface().equals(repositoryInterface))
229 .findFirst();
230 }
231
232
240 public PersistentEntity<?, ?> getPersistentEntity(Class<?> domainClass) {
241
242 Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
243 return getRepositoryFactoryInfoFor(domainClass).getPersistentEntity();
244 }
245
246
252 public List<QueryMethod> getQueryMethodsFor(Class<?> domainClass) {
253
254 Assert.notNull(domainClass, DOMAIN_TYPE_MUST_NOT_BE_NULL);
255 return getRepositoryFactoryInfoFor(domainClass).getQueryMethods();
256 }
257
258
262 public Iterator<Class<?>> iterator() {
263 return repositoryFactoryInfos.keySet().iterator();
264 }
265
266
274 @SuppressWarnings({ "rawtypes", "unchecked" })
275 private void cacheFirstOrPrimary(Class<?> type, RepositoryFactoryInformation information, String name) {
276
277 if (repositoryBeanNames.containsKey(type)) {
278
279 Boolean presentAndPrimary = beanFactory
280 .filter(ConfigurableListableBeanFactory.class::isInstance)
281 .map(ConfigurableListableBeanFactory.class::cast)
282 .map(it -> it.getBeanDefinition(name))
283 .map(BeanDefinition::isPrimary)
284 .orElse(false);
285
286 if (!presentAndPrimary) {
287 return;
288 }
289 }
290
291 this.repositoryFactoryInfos.put(type, information);
292 this.repositoryBeanNames.put(type, name);
293 }
294
295
300 private static enum EmptyRepositoryFactoryInformation implements RepositoryFactoryInformation<Object, Object> {
301
302 INSTANCE;
303
304 @Override
305 public EntityInformation<Object, Object> getEntityInformation() {
306 throw new UnsupportedOperationException();
307 }
308
309 @Override
310 public RepositoryInformation getRepositoryInformation() {
311 throw new UnsupportedOperationException();
312 }
313
314 @Override
315 public PersistentEntity<?, ?> getPersistentEntity() {
316 throw new UnsupportedOperationException();
317 }
318
319 @Override
320 public List<QueryMethod> getQueryMethods() {
321 return Collections.emptyList();
322 }
323 }
324 }
325