1
16 package org.springframework.data.jpa.repository.config;
17
18 import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*;
19
20 import lombok.experimental.UtilityClass;
21
22 import java.lang.annotation.Annotation;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.LinkedHashSet;
27 import java.util.Locale;
28 import java.util.Optional;
29 import java.util.Set;
30
31 import javax.persistence.Entity;
32 import javax.persistence.MappedSuperclass;
33 import javax.persistence.PersistenceContext;
34 import javax.persistence.PersistenceUnit;
35
36 import org.springframework.beans.factory.support.AbstractBeanDefinition;
37 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
38 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
39 import org.springframework.beans.factory.support.RootBeanDefinition;
40 import org.springframework.context.annotation.AnnotationConfigUtils;
41 import org.springframework.core.annotation.AnnotationAttributes;
42 import org.springframework.core.io.ResourceLoader;
43 import org.springframework.dao.DataAccessException;
44 import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
45 import org.springframework.data.jpa.repository.JpaRepository;
46 import org.springframework.data.jpa.repository.support.DefaultJpaContext;
47 import org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor;
48 import org.springframework.data.jpa.repository.support.JpaEvaluationContextExtension;
49 import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
50 import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
51 import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
52 import org.springframework.data.repository.config.RepositoryConfigurationSource;
53 import org.springframework.data.repository.config.XmlRepositoryConfigurationSource;
54 import org.springframework.lang.Nullable;
55 import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor;
56 import org.springframework.util.ClassUtils;
57 import org.springframework.util.StringUtils;
58
59
74 public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport {
75
76 private static final Class<?> PAB_POST_PROCESSOR = PersistenceAnnotationBeanPostProcessor.class;
77 private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
78 private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";
79 private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup";
80 private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter";
81
82
86 @Override
87 public String getModuleName() {
88 return "JPA";
89 }
90
91
95 @Override
96 public String getRepositoryFactoryBeanClassName() {
97 return JpaRepositoryFactoryBean.class.getName();
98 }
99
100
104 @Override
105 protected String getModulePrefix() {
106 return getModuleName().toLowerCase(Locale.US);
107 }
108
109
113 @Override
114 protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
115 return Arrays.asList(Entity.class, MappedSuperclass.class);
116 }
117
118
122 @Override
123 protected Collection<Class<?>> getIdentifyingTypes() {
124 return Collections.<Class<?>> singleton(JpaRepository.class);
125 }
126
127
131 @Override
132 public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {
133
134 Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef");
135 builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
136 builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(source, source.getSource()));
137 builder.addPropertyValue(ESCAPE_CHARACTER_PROPERTY, getEscapeCharacter(source).orElse('\\'));
138 builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);
139 }
140
141
145 private static Optional<Character> getEscapeCharacter(RepositoryConfigurationSource source) {
146
147 try {
148 return source.getAttribute(ESCAPE_CHARACTER_PROPERTY, Character.class);
149 } catch (IllegalArgumentException ___) {
150 return Optional.empty();
151 }
152 }
153
154
158 @Override
159 public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) {
160
161 AnnotationAttributes attributes = config.getAttributes();
162
163 builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE,
164 attributes.getBoolean(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE));
165 }
166
167
171 @Override
172 public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) {
173
174 Optional<String> enableDefaultTransactions = config.getAttribute(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE);
175
176 if (enableDefaultTransactions.isPresent() && StringUtils.hasText(enableDefaultTransactions.get())) {
177 builder.addPropertyValue(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE, enableDefaultTransactions.get());
178 }
179 }
180
181
185 @Override
186 public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource config) {
187
188 super.registerBeansForRoot(registry, config);
189
190 Object source = config.getSource();
191
192 registerLazyIfNotAlreadyRegistered(
193 () -> new RootBeanDefinition(EntityManagerBeanDefinitionRegistrarPostProcessor.class), registry,
194 EM_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME, source);
195
196 registerLazyIfNotAlreadyRegistered(() -> new RootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class),
197 registry, JPA_MAPPING_CONTEXT_BEAN_NAME, source);
198
199 registerLazyIfNotAlreadyRegistered(() -> new RootBeanDefinition(PAB_POST_PROCESSOR), registry,
200 AnnotationConfigUtils.PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME, source);
201
202
203
204 registerLazyIfNotAlreadyRegistered(() -> {
205
206 RootBeanDefinition contextDefinition = new RootBeanDefinition(DefaultJpaContext.class);
207 contextDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
208
209 return contextDefinition;
210
211 }, registry, JPA_CONTEXT_BEAN_NAME, source);
212
213 registerIfNotAlreadyRegistered(() -> new RootBeanDefinition(JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME), registry,
214 JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME, source);
215
216
217
218 registerIfNotAlreadyRegistered(() -> {
219
220 Object value = AnnotationRepositoryConfigurationSource.class.isInstance(config)
221 ? config.getRequiredAttribute(ESCAPE_CHARACTER_PROPERTY, Character.class)
222 : config.getAttribute(ESCAPE_CHARACTER_PROPERTY).orElse("\\");
223
224 BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(JpaEvaluationContextExtension.class);
225 builder.addConstructorArgValue(value);
226
227 return builder.getBeanDefinition();
228
229 }, registry, JpaEvaluationContextExtension.class.getName(), source);
230 }
231
232
236 @Override
237 protected ClassLoader getConfigurationInspectionClassLoader(ResourceLoader loader) {
238
239 ClassLoader classLoader = loader.getClassLoader();
240
241 return classLoader != null && LazyJvmAgent.isActive(loader.getClassLoader())
242 ? new InspectionClassLoader(loader.getClassLoader())
243 : loader.getClassLoader();
244 }
245
246
254 private static AbstractBeanDefinition getEntityManagerBeanDefinitionFor(RepositoryConfigurationSource config,
255 @Nullable Object source) {
256
257 BeanDefinitionBuilder builder = BeanDefinitionBuilder
258 .rootBeanDefinition("org.springframework.orm.jpa.SharedEntityManagerCreator");
259 builder.setFactoryMethod("createSharedEntityManager");
260 builder.addConstructorArgReference(getEntityManagerBeanRef(config));
261
262 AbstractBeanDefinition bean = builder.getRawBeanDefinition();
263 bean.setSource(source);
264
265 return bean;
266 }
267
268 private static String getEntityManagerBeanRef(RepositoryConfigurationSource config) {
269
270 Optional<String> entityManagerFactoryRef = config.getAttribute("entityManagerFactoryRef");
271 return entityManagerFactoryRef.orElse("entityManagerFactory");
272 }
273
274
280 @UtilityClass
281 static class LazyJvmAgent {
282
283 private static final Set<String> AGENT_CLASSES;
284
285 static {
286
287 Set<String> agentClasses = new LinkedHashSet<>();
288
289 agentClasses.add("org.springframework.instrument.InstrumentationSavingAgent");
290 agentClasses.add("org.eclipse.persistence.internal.jpa.deployment.JavaSECMPInitializerAgent");
291
292 AGENT_CLASSES = Collections.unmodifiableSet(agentClasses);
293 }
294
295
300 static boolean isActive(@Nullable ClassLoader classLoader) {
301
302 return AGENT_CLASSES.stream()
303 .anyMatch(agentClass -> ClassUtils.isPresent(agentClass, classLoader));
304 }
305 }
306 }
307