1 /**
2 * Copyright 2005-2014 The Kuali Foundation
3 *
4 * Licensed under the Educational Community License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/ecl2.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.kuali.rice.krad.data.jpa;
17
18 import org.kuali.rice.core.api.config.property.ConfigContext;
19 import org.kuali.rice.krad.data.jpa.converters.BooleanYNConverter;
20 import org.springframework.beans.factory.BeanClassLoaderAware;
21 import org.springframework.beans.factory.BeanFactory;
22 import org.springframework.beans.factory.BeanFactoryAware;
23 import org.springframework.beans.factory.BeanNameAware;
24 import org.springframework.beans.factory.DisposableBean;
25 import org.springframework.beans.factory.FactoryBean;
26 import org.springframework.beans.factory.InitializingBean;
27 import org.springframework.context.ResourceLoaderAware;
28 import org.springframework.context.weaving.LoadTimeWeaverAware;
29 import org.springframework.core.io.Resource;
30 import org.springframework.core.io.ResourceLoader;
31 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
32 import org.springframework.core.io.support.ResourcePatternResolver;
33 import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
34 import org.springframework.core.type.classreading.MetadataReader;
35 import org.springframework.core.type.classreading.MetadataReaderFactory;
36 import org.springframework.core.type.filter.AnnotationTypeFilter;
37 import org.springframework.core.type.filter.TypeFilter;
38 import org.springframework.dao.DataAccessException;
39 import org.springframework.dao.support.PersistenceExceptionTranslator;
40 import org.springframework.instrument.classloading.LoadTimeWeaver;
41 import org.springframework.orm.jpa.EntityManagerFactoryInfo;
42 import org.springframework.orm.jpa.JpaDialect;
43 import org.springframework.orm.jpa.JpaVendorAdapter;
44 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
45 import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
46 import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
47 import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
48 import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
49 import org.springframework.util.ClassUtils;
50
51 import javax.persistence.Converter;
52 import javax.persistence.EntityManager;
53 import javax.persistence.EntityManagerFactory;
54 import javax.persistence.PersistenceException;
55 import javax.persistence.spi.PersistenceProvider;
56 import javax.persistence.spi.PersistenceUnitInfo;
57 import javax.sql.DataSource;
58 import java.io.IOException;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.HashMap;
62 import java.util.List;
63 import java.util.Map;
64 import java.util.Properties;
65
66 /**
67 * A KRAD-managed {@link javax.persistence.EntityManagerFactory} factory bean which can be used to configure a JPA
68 * persistence unit using the Spring Framework.
69 *
70 * <p>
71 * This implementation does not support the use of a custom PersistenceUnitManager, but rather stores and manages one
72 * internally. This is intended to be an alternative to direct usage of Spring's
73 * {@link LocalContainerEntityManagerFactoryBean} in order to make JPA configuration with KRAD simpler.
74 * </p>
75 *
76 * <h1>Minimal Configuration</h1>
77 *
78 * <p>Minimal configuration of this factory bean will include the following:</p>
79 *
80 * <ul>
81 * <li>{@code persistenceUnitName}</li>
82 * <li>{@code dataSource} or {@code jtaDataSource}</li>
83 * <li>{@code persistenceProvider} or {@code jpaVendorAdapter}</li>
84 * </ul>
85 *
86 * <p>
87 * Note that persistence unit names must be unique, so choose a name which is unlikely to clash with any potential
88 * persistence units configured within the runtime environment.
89 * </p>
90 *
91 * <h1>Behavior</h1>
92 *
93 * <p>
94 * When leveraging this class, persistence.xml files are not used. Rather, persistence unit configuration is loaded
95 * via the various settings provided on this factory bean which contain everything needed to create the desired
96 * persistence unit configuration in the majority of cases. If a KRAD application needs more control over the
97 * configuration of the persistence unit or wants to use persistence.xml and JPA's default bootstrapping and classpath
98 * scanning behavior, an EntityManagerFactory can be configured using the other classes provided by the Spring
99 * framework or by other means as needed. See {@link LocalContainerEntityManagerFactoryBean} for an alternative
100 * approach.
101 * </p>
102 *
103 * <p>
104 * Only one of JTA or non-JTA datasource can be set. Depending on which one is set, the underlying persistence unit
105 * will have it's {@link javax.persistence.spi.PersistenceUnitTransactionType} set to either RESOURCE_LOCAL or JTA. If
106 * both of these are set, then this class will throw an {@code IllegalStateException} when the
107 * {@link #afterPropertiesSet()} method is invoked by the Spring Framework.
108 * </p>
109 *
110 * <p>
111 * Elsewhere, this class delegates to implementations of {@link LocalContainerEntityManagerFactoryBean} and
112 * {@link DefaultPersistenceUnitManager}, so information on the specific behavior of some of the settings and methods on
113 * this class can be found on the javadoc for those classes as well.
114 * </p>
115 *
116 * <h1>JPA Property Defaults</h1>
117 *
118 * <p>
119 * When {@link #afterPropertiesSet()} is invoked, this class will scan the current {@link ConfigContext} for JPA
120 * properties and make them available to the persistence unit. It will combine these with any properties that were set
121 * directly on this factory bean via the {@link #setJpaProperties(java.util.Properties)} or
122 * {@link #setJpaPropertyMap(java.util.Map)} methods.
123 * </p
124 *
125 * <p>
126 * This scanning occurs in the following order, items later in the list will override any properties from earlier if
127 * they happen to set the same effective property value:
128 * </p>
129 *
130 * <ol>
131 * <li>Scan ConfigContext for properties that begin with "rice.krad.jpa.global." For any found, strip off this prefix
132 * prior to placing it into the JPA property map.</li>
133 * <li>Scan ConfigContext for properties that being with "rice.krad.jpa.<persistence-unit-name>" where
134 * "persistence-unit-name" is the configured name of this persistence unit. For any found, strip off this prefix
135 * prior to placing it into the JPA property map.</li>
136 * <li>Invoke {@link #loadCustomJpaDefaults(java.util.Map)} to allow for possible subclass customization of JPA
137 * property defaults</li>
138 * <li>Load the JPA properties configured via {@link #setJpaPropertyMap(java.util.Map)} and
139 * {@link #setJpaProperties(java.util.Properties)}. It is potentially non-deterministic which of these setters will
140 * take precedence over the other, so it is recommended to only invoke one of them on a given instance of this
141 * factory bean.</li>
142 * </ol>
143 *
144 * <h1>Subclassing</h1>
145 *
146 * <p>
147 * This class can be subclassed to provide additional specialized implementations of this factory bean. A potential use
148 * for this might be to provide a factory bean that defaults certain values as part of it's default setup.
149 * </p>
150 *
151 * @author Kuali Rice Team (rice.collab@kuali.org)
152 */
153 public class KradEntityManagerFactoryBean implements FactoryBean<EntityManagerFactory>, BeanClassLoaderAware,
154 BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean, EntityManagerFactoryInfo,
155 PersistenceExceptionTranslator, ResourceLoaderAware, LoadTimeWeaverAware {
156
157 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
158 .getLogger(KradEntityManagerFactoryBean.class);
159
160 /**
161 * Prefix for property names that are passed to the JPA persistence context as JPA properties.
162 *
163 * <p>
164 * To use this, concatenate this prefix with the persistence context name. The total prefix (including the
165 * persistence context) will then be stripped before being passed to the JPA entity manager factory when using
166 * the {@link org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean}.
167 * </p>
168 *
169 * @see org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean
170 */
171 public static final String JPA_PROPERTY_PREFIX = "rice.krad.jpa.";
172
173 /**
174 * Prefix for property names that are passed to *all* JPA persistence contexts as JPA properties.
175 *
176 * <p>
177 * The total prefix will then be stripped before being passed to all JPA entity manager factories that use the
178 * {@link org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean}.
179 * </p>
180 *
181 * @see org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean
182 */
183 public static final String GLOBAL_JPA_PROPERTY_PREFIX = JPA_PROPERTY_PREFIX + "global.";
184
185 private static final boolean DEFAULT_EXCLUDE_UNLISTED_CLASSES = true;
186 private static final String DEFAULT_CONVERTERS_PACKAGE = BooleanYNConverter.class.getPackage().getName();
187
188 private final DefaultPersistenceUnitManager persistenceUnitManager;
189 private final LocalContainerEntityManagerFactoryBean internalFactoryBean;
190
191 private List<PersistenceUnitPostProcessor> persistenceUnitPostProcessors;
192 private List<String> managedClassNames;
193 private List<String> converterPackageNames;
194
195 /**
196 * Creates a default KRAD-managed factory bean.
197 */
198 public KradEntityManagerFactoryBean() {
199 this.persistenceUnitPostProcessors = new ArrayList<PersistenceUnitPostProcessor>();
200 this.managedClassNames = new ArrayList<String>();
201 this.converterPackageNames = new ArrayList<String>();
202 converterPackageNames.add(DEFAULT_CONVERTERS_PACKAGE); // set as default
203 this.persistenceUnitManager = createPersistenceUnitManager();
204 this.internalFactoryBean = createInternalFactoryBean(this.persistenceUnitManager);
205 this.internalFactoryBean.setJpaPropertyMap(createDefaultJpaProperties());
206 }
207
208 /**
209 * Retrieve a reference to the internal {@link LocalContainerEntityManagerFactoryBean} which is used by this factory
210 * bean.
211 *
212 * <p>Primarily intended to allow subclasses to access this internal factory bean when needed.</p>
213 *
214 * @return the internal {@link LocalContainerEntityManagerFactoryBean} managed by this bean
215 */
216 protected LocalContainerEntityManagerFactoryBean getInternalFactoryBean() {
217 return this.internalFactoryBean;
218 }
219
220 /**
221 * Creates a persistence unit manager.
222 *
223 * @return a persistence unit manager.
224 */
225 protected DefaultPersistenceUnitManager createPersistenceUnitManager() {
226 DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
227 // IMPORTANT! - setting these to empty String arrays, this triggers the DefaultPersistenceUnitManager to
228 // behave appropriately and ignore persistence.xml files from META-INF/persistence.xml as well as allowing for
229 // an empty/minimal persistence unit to be created.
230 //
231 // Note that while Intellij complains about "Redundant array creation for calling varargs method", we really do
232 // need to pass an empty array here in order for this code to work properly.
233 pum.setPersistenceXmlLocations(new String[0]);
234 pum.setMappingResources(new String[0]);
235 pum.setPackagesToScan(new String[0]);
236 return pum;
237 }
238
239 /**
240 * Creates a JPA-specific entity manager factory bean.
241 *
242 * @param manager the persistence unit manager to use.
243 * @return a JPA-specific entity manager factory bean.
244 */
245 protected LocalContainerEntityManagerFactoryBean createInternalFactoryBean(PersistenceUnitManager manager) {
246 LocalContainerEntityManagerFactoryBean delegate = new LocalContainerEntityManagerFactoryBean();
247 delegate.setPersistenceUnitManager(manager);
248 return delegate;
249 }
250
251 /**
252 * {@inheritDoc}
253 */
254 @Override
255 public void afterPropertiesSet() throws PersistenceException {
256 if (persistenceUnitManager.getDefaultJtaDataSource() != null &&
257 persistenceUnitManager.getDefaultDataSource() != null) {
258 throw new IllegalStateException(getPersistenceUnitName() + ": " + getClass().getSimpleName()
259 + " was configured with both a JTA and Non-JTA "
260 + " datasource. Must configure one or the other, but not both.");
261 }
262
263 this.internalFactoryBean.setJpaPropertyMap(defaultAndMergeJpaProperties());
264 persistenceUnitManager.setPersistenceUnitPostProcessors(assemblePersistenceUnitPostProcessors());
265 persistenceUnitManager.afterPropertiesSet();
266 internalFactoryBean.afterPropertiesSet();
267 }
268
269 /**
270 * Creates default JPA properties.
271 *
272 * @return a map of default JPA properties.
273 */
274 private Map<String, ?> createDefaultJpaProperties() {
275 Map<String, String> jpaProperties = new HashMap<String, String>();
276 loadGlobalJpaDefaults(jpaProperties);
277 loadPersistenceUnitJpaDefaults(jpaProperties);
278 loadCustomJpaDefaults(jpaProperties);
279
280 return jpaProperties;
281 }
282
283 /**
284 * Gets the default JPA properties and merges them with the configured JPA properties.
285 *
286 * @return a map of merged JPA properties.
287 */
288 private Map<String, ?> defaultAndMergeJpaProperties() {
289 Map<String, Object> jpaProperties = new HashMap<String, Object>(createDefaultJpaProperties());
290 Map<String, Object> configuredJpaPropertyMap = this.internalFactoryBean.getJpaPropertyMap();
291 jpaProperties.putAll(configuredJpaPropertyMap);
292 if (LOG.isDebugEnabled()) {
293 LOG.debug(getPersistenceUnitName() + ": JPA Properties Set:\n" + jpaProperties);
294 }
295
296 return jpaProperties;
297 }
298
299 /**
300 * Allows for loading of custom JPA defaults by subclasses, default implementation does nothing so subclasses need
301 * not call super.loadCustomJpaDefaults.
302 *
303 * <p>
304 * Subclasses are free to override this method as they see fit. This method is executed after other defaults are
305 * loaded. A reference to the current Map of JPA properties is passed. Subclasses should take care if removing or
306 * overwriting any of the values which already exist in the given Map.
307 * </p>
308 *
309 * @param jpaProperties the current Map of JPA property defaults.
310 */
311 protected void loadCustomJpaDefaults(Map<String, String> jpaProperties) {
312 // subclass can override
313 }
314
315 /**
316 * Loads the global JPA defaults from the config.
317 *
318 * @param jpaProperties the current Map of JPA property defaults.
319 */
320 protected void loadGlobalJpaDefaults(Map<String, String> jpaProperties) {
321 jpaProperties.putAll(ConfigContext.getCurrentContextConfig().getPropertiesWithPrefix(GLOBAL_JPA_PROPERTY_PREFIX,
322 true));
323 }
324
325 /**
326 * Loads the persistence unit JPA defaults from the config.
327 *
328 * @param jpaProperties the current Map of JPA property defaults.
329 */
330 protected void loadPersistenceUnitJpaDefaults(Map<String, String> jpaProperties) {
331 jpaProperties.putAll(ConfigContext.getCurrentContextConfig().getPropertiesWithPrefix(
332 constructPersistenceUnitJpaPropertyPrefix(), true));
333 }
334
335 /**
336 * Builds a persistence unit JPA property prefix.
337 * @return a persistence unit JPA property prefix.
338 */
339 protected String constructPersistenceUnitJpaPropertyPrefix() {
340 return JPA_PROPERTY_PREFIX + getPersistenceUnitName() + ".";
341 }
342
343 /**
344 * Assembles the {@link PersistenceUnitPostProcessor}s into an array.
345 *
346 * @return an array of the {@link PersistenceUnitPostProcessor}s.
347 */
348 protected PersistenceUnitPostProcessor[] assemblePersistenceUnitPostProcessors() {
349 this.persistenceUnitPostProcessors = new ArrayList<PersistenceUnitPostProcessor>(this.persistenceUnitPostProcessors);
350 this.persistenceUnitPostProcessors.add(new InternalPersistenceUnitPostProcessor());
351 return this.persistenceUnitPostProcessors.toArray(new PersistenceUnitPostProcessor[this.persistenceUnitPostProcessors.size()]);
352 }
353
354 /**
355 * Returns the list of all managed class names which have been configured on this factory bean.
356 *
357 * <p>This list is modifiable, so the returned list may be modified directly if desired.</p>
358 *
359 * @return list of all managed class names, may be an empty list but will never return null
360 */
361 public List<String> getManagedClassNames() {
362 return managedClassNames;
363 }
364
365 /**
366 * Returns an array of the {@link PersistenceUnitPostProcessor} instances which have been configured on this
367 * factory bean.
368 *
369 * @return array of post processors, may be empty but will never return null
370 */
371 public PersistenceUnitPostProcessor[] getPersistenceUnitPostProcessors() {
372 return persistenceUnitPostProcessors.toArray(new PersistenceUnitPostProcessor[persistenceUnitPostProcessors.size()]);
373 }
374
375 /**
376 * Returns a reference to the internal {@link DefaultPersistenceUnitManager} which is used by this factory bean.
377 *
378 * @return the internal persistence unit manager, will never return null
379 */
380 protected DefaultPersistenceUnitManager getPersistenceUnitManager() {
381 return persistenceUnitManager;
382 }
383
384 /**
385 * {@inheritDoc}
386 */
387 @Override
388 public void destroy() {
389 internalFactoryBean.destroy();
390 }
391
392 /**
393 * {@inheritDoc}
394 */
395 @Override
396 public Class<? extends EntityManagerFactory> getObjectType() {
397 return internalFactoryBean.getObjectType();
398 }
399
400 /**
401 * {@inheritDoc}
402 */
403 @Override
404 public boolean isSingleton() {
405 return internalFactoryBean.isSingleton();
406 }
407
408 /**
409 * {@inheritDoc}
410 */
411 @Override
412 public EntityManagerFactory getObject() {
413 return internalFactoryBean.getObject();
414 }
415
416 /**
417 * {@inheritDoc}
418 */
419 @Override
420 public EntityManagerFactory getNativeEntityManagerFactory() {
421 return internalFactoryBean.getNativeEntityManagerFactory();
422 }
423
424 /**
425 * {@inheritDoc}
426 */
427 @Override
428 public void setBeanName(String name) {
429 internalFactoryBean.setBeanName(name);
430 }
431
432 /**
433 * {@inheritDoc}
434 */
435 @Override
436 public void setBeanFactory(BeanFactory beanFactory) {
437 internalFactoryBean.setBeanFactory(beanFactory);
438 }
439
440 /**
441 * {@inheritDoc}
442 */
443 @Override
444 public ClassLoader getBeanClassLoader() {
445 return internalFactoryBean.getBeanClassLoader();
446 }
447
448 /**
449 * {@inheritDoc}
450 */
451 @Override
452 public void setBeanClassLoader(ClassLoader classLoader) {
453 internalFactoryBean.setBeanClassLoader(classLoader);
454 }
455
456 /**
457 * {@inheritDoc}
458 */
459 @Override
460 public Class<? extends EntityManager> getEntityManagerInterface() {
461 return internalFactoryBean.getEntityManagerInterface();
462 }
463
464 /**
465 * {@inheritDoc}
466 */
467 @Override
468 public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
469 persistenceUnitManager.setLoadTimeWeaver(loadTimeWeaver);
470 }
471
472 /**
473 * {@inheritDoc}
474 */
475 @Override
476 public void setResourceLoader(ResourceLoader resourceLoader) {
477 persistenceUnitManager.setResourceLoader(resourceLoader);
478 }
479
480 /**
481 * {@inheritDoc}
482 */
483 @Override
484 public PersistenceUnitInfo getPersistenceUnitInfo() {
485 return internalFactoryBean.getPersistenceUnitInfo();
486 }
487
488 /**
489 * {@inheritDoc}
490 */
491 @Override
492 public String getPersistenceUnitName() {
493 return internalFactoryBean.getPersistenceUnitName();
494 }
495
496 /**
497 * {@inheritDoc}
498 */
499 @Override
500 public DataSource getDataSource() {
501 PersistenceUnitInfo pui = internalFactoryBean.getPersistenceUnitInfo();
502 if (internalFactoryBean.getPersistenceUnitInfo() != null) {
503 return (pui.getJtaDataSource() != null ?
504 pui.getJtaDataSource() :
505 pui.getNonJtaDataSource());
506 }
507 return (persistenceUnitManager.getDefaultJtaDataSource() != null ?
508 persistenceUnitManager.getDefaultJtaDataSource() :
509 this.persistenceUnitManager.getDefaultDataSource());
510 }
511
512 /**
513 * {@inheritDoc}
514 */
515 @Override
516 public JpaDialect getJpaDialect() {
517 return internalFactoryBean.getJpaDialect();
518 }
519
520 /**
521 * {@inheritDoc}
522 */
523 @Override
524 public PersistenceProvider getPersistenceProvider() {
525 return internalFactoryBean.getPersistenceProvider();
526 }
527
528 /**
529 * {@inheritDoc}
530 */
531 @Override
532 public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
533 return internalFactoryBean.translateExceptionIfPossible(ex);
534 }
535
536 /**
537 * Specifies a List of class names which should be loaded into the resulting EntityManagerFactory as managed
538 * JPA classes.
539 *
540 * @param managedClassNames the List of managed class names to set on this factory bean
541 */
542 public void setManagedClassNames(List<String> managedClassNames) {
543 if (managedClassNames == null) {
544 managedClassNames = new ArrayList<String>();
545 }
546 if (LOG.isInfoEnabled()) {
547 LOG.info(getPersistenceUnitName() + ": Setting Managed Class Names JPA:\n" + managedClassNames);
548 }
549 this.managedClassNames = managedClassNames;
550 }
551
552 /**
553 * Specify the (potentially vendor-specific) EntityManager interface that this factory's EntityManagers are supposed
554 * to implement.
555 *
556 * <p>
557 * The default will be taken from the specific JpaVendorAdapter, if any, or set to the standard
558 * {@code EntityManager} interface else.
559 * </p>
560 *
561 * @param emInterface the {@link EntityManager} interface to use
562 *
563 * @see JpaVendorAdapter#getEntityManagerInterface()
564 * @see EntityManagerFactoryInfo#getEntityManagerInterface()
565 */
566 public void setEntityManagerInterface(Class<? extends EntityManager> emInterface) {
567 internalFactoryBean.setEntityManagerInterface(emInterface);
568 }
569
570 /**
571 * Specify the (potentially vendor-specific) EntityManagerFactory interface that this EntityManagerFactory proxy is
572 * supposed to implement.
573 *
574 * <p>
575 * The default will be taken from the specific JpaVendorAdapter, if any, or set to the standard
576 * {@code EntityManagerFactory} interface else.
577 * </p>
578 *
579 * @param emfInterface the {@link EntityManagerFactory} interface to use
580 *
581 * @see JpaVendorAdapter#getEntityManagerFactoryInterface()
582 */
583 public void setEntityManagerFactoryInterface(Class<? extends EntityManagerFactory> emfInterface) {
584 internalFactoryBean.setEntityManagerFactoryInterface(emfInterface);
585 }
586
587 /**
588 * Allow Map access to the JPA properties to be passed to the persistence provider, with the option to add or
589 * override specific entries.
590 *
591 * <p>Useful for specifying entries directly, for example via "jpaPropertyMap[myKey]".</p>
592 *
593 * @return the map of JPA properties
594 */
595 public Map<String, Object> getJpaPropertyMap() {
596 return internalFactoryBean.getJpaPropertyMap();
597 }
598
599 /**
600 * Specify JPA properties as a Map, to be passed into {@code Persistence.createEntityManagerFactory} (if any).
601 *
602 * <p>Can be populated with a "map" or "props" element in XML bean definitions.</p>
603 *
604 * @param jpaProperties map of JPA properties to set
605 *
606 * @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
607 * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
608 */
609 public void setJpaPropertyMap(Map<String, ?> jpaProperties) {
610 internalFactoryBean.setJpaPropertyMap(jpaProperties);
611 }
612
613 /**
614 * Specify JPA properties, to be passed into {@code Persistence.createEntityManagerFactory} (if any).
615 *
616 * <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a "props" element in XML bean definitions.</p>
617 *
618 * @param jpaProperties the properties to set
619 *
620 * @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
621 * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
622 */
623 public void setJpaProperties(Properties jpaProperties) {
624 internalFactoryBean.setJpaProperties(jpaProperties);
625 }
626
627 /**
628 * Specify the name of the EntityManagerFactory configuration.
629 *
630 * <p>
631 * Default is none, indicating the default EntityManagerFactory configuration. The persistence provider will throw
632 * an exception if ambiguous EntityManager configurations are found.
633 * </p>
634 *
635 * @param persistenceUnitName the name of the persistence unit
636 *
637 * @see javax.persistence.Persistence#createEntityManagerFactory(String)
638 */
639 public void setPersistenceUnitName(String persistenceUnitName) {
640 internalFactoryBean.setPersistenceUnitName(persistenceUnitName);
641 persistenceUnitManager.setDefaultPersistenceUnitName(persistenceUnitName);
642 }
643
644 /**
645 * Set whether to use Spring-based scanning for entity classes in the classpath instead of using JPA's standard
646 * scanning of jar files with {@code persistence.xml} markers in them.
647 *
648 * <p>
649 * In case of Spring-based scanning, no {@code persistence.xml} is necessary; all you need to do is to specify base
650 * packages to search here.
651 * </p>
652 *
653 * <p>
654 * Default is none. Specify packages to search for autodetection of your entity classes in the classpath. This is
655 * analogous to Spring's component-scan feature
656 * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
657 * </p>
658 *
659 * @param packagesToScan one or more base packages to search, analogous to Spring's component-scan configuration for
660 * regular Spring components
661 *
662 * @see {@link DefaultPersistenceUnitManager#setPackagesToScan(String...)}
663 */
664 public void setPackagesToScan(String... packagesToScan) {
665 if (LOG.isInfoEnabled()) {
666 LOG.info(getPersistenceUnitName() + ": Setting Packages to Scan for JPA Annotations:\n"
667 + Arrays.deepToString(packagesToScan));
668 }
669 persistenceUnitManager.setPackagesToScan(packagesToScan);
670 converterPackageNames = Arrays.asList(packagesToScan);
671 }
672
673 /**
674 * Specify one or more mapping resources (equivalent to {@code <mapping-file>} entries in
675 * {@code persistence.xml}) for the default persistence unit.
676 *
677 * <p>
678 * Can be used on its own or in combination with entity scanning in the classpath, in both cases avoiding
679 * {@code persistence.xml}.
680 * </p>
681 *
682 * <p>
683 * Note that mapping resources must be relative to the classpath root, e.g. "META-INF/mappings.xml" or
684 * "com/mycompany/repository/mappings.xml", so that they can be loaded through {@code ClassLoader.getResource}.
685 * </p>
686 *
687 * @param mappingResources one or more mapping resources to use
688 *
689 * @see {@link DefaultPersistenceUnitManager#setMappingResources(String...)}
690 */
691 public void setMappingResources(String... mappingResources) {
692 persistenceUnitManager.setMappingResources(mappingResources);
693 }
694
695 /**
696 * Specify the JDBC DataSource that the JPA persistence provider is supposed to use for accessing the database.
697 *
698 * <p>
699 * This is an alternative to keeping the JDBC configuration in {@code persistence.xml}, passing in a Spring-managed
700 * DataSource instead.
701 * </p>
702 *
703 * <p>
704 * In JPA speak, a DataSource passed in here will be used as "nonJtaDataSource" on the PersistenceUnitInfo passed
705 * to the PersistenceProvider, as well as overriding data source configuration in {@code persistence.xml} (if any).
706 * Note that this variant typically works for JTA transaction management as well; if it does not, consider using the
707 * explicit {@link #setJtaDataSource} instead.
708 * </p>
709 *
710 * @param dataSource the DataSource to use for this EntityManagerFactory
711 *
712 * @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
713 * @see {@link DefaultPersistenceUnitManager#setDefaultDataSource(javax.sql.DataSource)}
714 */
715 public void setDataSource(DataSource dataSource) {
716 persistenceUnitManager.setDefaultDataSource(dataSource);
717 }
718
719 /**
720 * Specify the JDBC DataSource that the JPA persistence provider is supposed to use for accessing the database.
721 *
722 * <p>
723 * This is an alternative to keeping the JDBC configuration in {@code persistence.xml}, passing in a Spring-managed
724 * DataSource instead.
725 * </p>
726 *
727 * <p>
728 * In JPA speak, a DataSource passed in here will be used as "jtaDataSource" on the PersistenceUnitInfo passed to
729 * the PersistenceProvider, as well as overriding data source configuration in {@code persistence.xml} (if any).
730 * </p>
731 *
732 * @param jtaDataSource the JTA-enabled DataSource to use for this EntityManagerFactory
733 *
734 * @see javax.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
735 * @see {@link DefaultPersistenceUnitManager#setDefaultJtaDataSource(javax.sql.DataSource)}
736 */
737 public void setJtaDataSource(DataSource jtaDataSource) {
738 persistenceUnitManager.setDefaultJtaDataSource(jtaDataSource);
739 }
740
741 /**
742 * Set the PersistenceProvider instance to use for creating the EntityManagerFactory.
743 *
744 * <p>
745 * If not specified, the persistence provider will be taken from the JpaVendorAdapter (if any) or determined by the
746 * persistence unit deployment descriptor (as far as possible).
747 * </p>
748 *
749 * @param persistenceProvider the PersistenceProvider to set
750 *
751 * @see JpaVendorAdapter#getPersistenceProvider()
752 * @see javax.persistence.spi.PersistenceProvider
753 * @see javax.persistence.Persistence
754 */
755 public void setPersistenceProvider(PersistenceProvider persistenceProvider) {
756 this.internalFactoryBean.setPersistenceProvider(persistenceProvider);
757 }
758
759 /**
760 * Specify the vendor-specific JpaDialect implementation to associate with this EntityManagerFactory.
761 *
762 * <p>
763 * This will be exposed through the EntityManagerFactoryInfo interface, to be picked up as default dialect by
764 * accessors that intend to use JpaDialect functionality.
765 * </p>
766 *
767 * @param jpaDialect the JPA dialect to set
768 *
769 * @see EntityManagerFactoryInfo#getJpaDialect()
770 */
771 public void setJpaDialect(JpaDialect jpaDialect) {
772 this.internalFactoryBean.setJpaDialect(jpaDialect);
773 }
774
775 /**
776 * Specify the JpaVendorAdapter implementation for the desired JPA provider, if any.
777 *
778 * <p>
779 * This will initialize appropriate defaults for the given provider, such as persistence provider class and
780 * JpaDialect, unless locally overridden in this FactoryBean.
781 * </p>
782 *
783 * @param jpaVendorAdapter the JpaVendorAdapter to set
784 */
785 public void setJpaVendorAdapter(JpaVendorAdapter jpaVendorAdapter) {
786 this.internalFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
787 }
788
789 /**
790 * Set the PersistenceUnitPostProcessors to be applied to the * PersistenceUnitInfo used for creating this
791 * EntityManagerFactory.
792 *
793 * <p>
794 * Note that if executed before {@link #afterPropertiesSet()} then this factory bean may introduce its own post
795 * processor instances. If invoked after, then this method will override internally configured post processors.
796 * </p>
797 *
798 * <p>
799 * Such post-processors can, for example, register further entity classes and jar files, in addition to the metadata
800 * read from {@code persistence.xml}.
801 * </p>
802 *
803 * @param postProcessors one or more post processors to set
804 */
805 public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... postProcessors) {
806 // persistence unit post processors will get applied to the internal factory bean during afterPropertiesSet(),
807 // this allows us to add our own internal post processor to the list)
808 this.persistenceUnitPostProcessors = new ArrayList<PersistenceUnitPostProcessor>(Arrays.asList(postProcessors));
809 }
810
811 /**
812 * A {@link PersistenceUnitPostProcessor} to handle {@link Converter} annotations.
813 */
814 private final class InternalPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
815
816 private final TypeFilter converterAnnotationTypeFilter = new AnnotationTypeFilter(Converter.class);
817 private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
818 private static final String ENTITY_CLASS_RESOURCE_PATTERN = "/**/*.class";
819
820 /**
821 * {@inheritDoc}
822 */
823 @Override
824 public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
825 pui.setExcludeUnlistedClasses(DEFAULT_EXCLUDE_UNLISTED_CLASSES);
826 processConverterPackages(pui);
827 for (String managedClassName : getManagedClassNames()) {
828 pui.addManagedClassName(managedClassName);
829 }
830 }
831
832 /**
833 * Determines whether the managed classes contain {@link Converter} annotations and adds them if necessary.
834 *
835 * @param pui the list of current list of managed classes.
836 */
837 private void processConverterPackages(MutablePersistenceUnitInfo pui) {
838 if (converterPackageNames != null) {
839 for (String converterPackage : converterPackageNames) {
840 // Code below lifted and modified from Spring's DefaultPersistenceUnitManager
841 try {
842 String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
843 + ClassUtils.convertClassNameToResourcePath(converterPackage)
844 + ENTITY_CLASS_RESOURCE_PATTERN;
845 if (LOG.isInfoEnabled()) {
846 LOG.info(getPersistenceUnitName() + ": Scanning for JPA @Converter annotations in: "
847 + pattern);
848 }
849 Resource[] resources = this.resourcePatternResolver.getResources(pattern);
850 MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(
851 this.resourcePatternResolver);
852 for (Resource resource : resources) {
853 if (!resource.isReadable()) {
854 continue;
855 }
856 if (LOG.isDebugEnabled()) {
857 LOG.debug(getPersistenceUnitName() + ": Found Matching Resource: " + resource);
858 }
859 MetadataReader reader = readerFactory.getMetadataReader(resource);
860 String className = reader.getClassMetadata().getClassName();
861 if (!pui.getManagedClassNames().contains(className)
862 && converterAnnotationTypeFilter.match(reader, readerFactory)) {
863 pui.addManagedClassName(className);
864 if (LOG.isDebugEnabled()) {
865 LOG.debug(getPersistenceUnitName()
866 + ": Registering Converter in JPA Persistence Unit: " + className);
867 }
868 }
869 }
870 } catch (IOException ex) {
871 throw new PersistenceException("Failed to scan classpath converters in package: "
872 + converterPackage, ex);
873 }
874 }
875 }
876 }
877 }
878 }