001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.orm.jpa;
018
019import javax.persistence.EntityManagerFactory;
020import javax.persistence.PersistenceException;
021import javax.persistence.SharedCacheMode;
022import javax.persistence.ValidationMode;
023import javax.persistence.spi.PersistenceProvider;
024import javax.persistence.spi.PersistenceUnitInfo;
025import javax.sql.DataSource;
026
027import org.springframework.beans.BeanUtils;
028import org.springframework.context.ResourceLoaderAware;
029import org.springframework.context.weaving.LoadTimeWeaverAware;
030import org.springframework.core.io.ResourceLoader;
031import org.springframework.instrument.classloading.LoadTimeWeaver;
032import org.springframework.jdbc.datasource.lookup.SingleDataSourceLookup;
033import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
034import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
035import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
036import org.springframework.orm.jpa.persistenceunit.SmartPersistenceUnitInfo;
037import org.springframework.util.Assert;
038import org.springframework.util.ClassUtils;
039
040/**
041 * {@link org.springframework.beans.factory.FactoryBean} that creates a JPA
042 * {@link javax.persistence.EntityManagerFactory} according to JPA's standard
043 * <i>container</i> bootstrap contract. This is the most powerful way to set
044 * up a shared JPA EntityManagerFactory in a Spring application context;
045 * the EntityManagerFactory can then be passed to JPA-based DAOs via
046 * dependency injection. Note that switching to a JNDI lookup or to a
047 * {@link LocalEntityManagerFactoryBean} definition is just a matter of
048 * configuration!
049 *
050 * <p>As with {@link LocalEntityManagerFactoryBean}, configuration settings
051 * are usually read in from a {@code META-INF/persistence.xml} config file,
052 * residing in the class path, according to the general JPA configuration contract.
053 * However, this FactoryBean is more flexible in that you can override the location
054 * of the {@code persistence.xml} file, specify the JDBC DataSources to link to,
055 * etc. Furthermore, it allows for pluggable class instrumentation through Spring's
056 * {@link org.springframework.instrument.classloading.LoadTimeWeaver} abstraction,
057 * instead of being tied to a special VM agent specified on JVM startup.
058 *
059 * <p>Internally, this FactoryBean parses the {@code persistence.xml} file
060 * itself and creates a corresponding {@link javax.persistence.spi.PersistenceUnitInfo}
061 * object (with further configuration merged in, such as JDBC DataSources and the
062 * Spring LoadTimeWeaver), to be passed to the chosen JPA
063 * {@link javax.persistence.spi.PersistenceProvider}. This corresponds to a
064 * local JPA container with full support for the standard JPA container contract.
065 *
066 * <p>The exposed EntityManagerFactory object will implement all the interfaces of
067 * the underlying native EntityManagerFactory returned by the PersistenceProvider,
068 * plus the {@link EntityManagerFactoryInfo} interface which exposes additional
069 * metadata as assembled by this FactoryBean.
070 *
071 * <p><b>NOTE: Spring's JPA support requires JPA 2.0 or higher, as of Spring 4.0.</b>
072 * JPA 1.0 based applications are still supported; however, a JPA 2.0/2.1 compliant
073 * persistence provider is needed at runtime. Spring's persistence unit bootstrapping
074 * automatically detects JPA 2.0 vs 2.1 through checking the JPA API on the classpath.
075 *
076 * @author Juergen Hoeller
077 * @author Rod Johnson
078 * @since 2.0
079 * @see #setPersistenceXmlLocation
080 * @see #setJpaProperties
081 * @see #setJpaVendorAdapter
082 * @see #setLoadTimeWeaver
083 * @see #setDataSource
084 * @see EntityManagerFactoryInfo
085 * @see LocalEntityManagerFactoryBean
086 * @see org.springframework.orm.jpa.support.SharedEntityManagerBean
087 * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory
088 */
089@SuppressWarnings("serial")
090public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean
091                implements ResourceLoaderAware, LoadTimeWeaverAware {
092
093        private PersistenceUnitManager persistenceUnitManager;
094
095        private final DefaultPersistenceUnitManager internalPersistenceUnitManager = new DefaultPersistenceUnitManager();
096
097        private PersistenceUnitInfo persistenceUnitInfo;
098
099
100        /**
101         * Set the PersistenceUnitManager to use for obtaining the JPA persistence unit
102         * that this FactoryBean is supposed to build an EntityManagerFactory for.
103         * <p>The default is to rely on the local settings specified on this FactoryBean,
104         * such as "persistenceXmlLocation", "dataSource" and "loadTimeWeaver".
105         * <p>For reuse of existing persistence unit configuration or more advanced forms
106         * of custom persistence unit handling, consider defining a separate
107         * PersistenceUnitManager bean (typically a DefaultPersistenceUnitManager instance)
108         * and linking it in here. {@code persistence.xml} location, DataSource
109         * configuration and LoadTimeWeaver will be defined on that separate
110         * DefaultPersistenceUnitManager bean in such a scenario.
111         * @see #setPersistenceXmlLocation
112         * @see #setDataSource
113         * @see #setLoadTimeWeaver
114         * @see org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager
115         */
116        public void setPersistenceUnitManager(PersistenceUnitManager persistenceUnitManager) {
117                this.persistenceUnitManager = persistenceUnitManager;
118        }
119
120        /**
121         * Set the location of the {@code persistence.xml} file
122         * we want to use. This is a Spring resource location.
123         * <p>Default is "classpath:META-INF/persistence.xml".
124         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
125         * @param persistenceXmlLocation a Spring resource String
126         * identifying the location of the {@code persistence.xml} file
127         * that this LocalContainerEntityManagerFactoryBean should parse
128         * @see #setPersistenceUnitManager
129         */
130        public void setPersistenceXmlLocation(String persistenceXmlLocation) {
131                this.internalPersistenceUnitManager.setPersistenceXmlLocation(persistenceXmlLocation);
132        }
133
134        /**
135         * Uses the specified persistence unit name as the name of the default
136         * persistence unit, if applicable.
137         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
138         * @see DefaultPersistenceUnitManager#setDefaultPersistenceUnitName
139         */
140        @Override
141        public void setPersistenceUnitName(String persistenceUnitName) {
142                super.setPersistenceUnitName(persistenceUnitName);
143                this.internalPersistenceUnitManager.setDefaultPersistenceUnitName(persistenceUnitName);
144        }
145
146        /**
147         * Set a persistence unit root location for the default persistence unit.
148         * <p>Default is "classpath:", that is, the root of the current classpath
149         * (nearest root directory). To be overridden if unit-specific resolution
150         * does not work and the classpath root is not appropriate either.
151         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
152         * @since 4.3.3
153         * @see DefaultPersistenceUnitManager#setDefaultPersistenceUnitRootLocation
154         */
155        public void setPersistenceUnitRootLocation(String defaultPersistenceUnitRootLocation) {
156                this.internalPersistenceUnitManager.setDefaultPersistenceUnitRootLocation(defaultPersistenceUnitRootLocation);
157        }
158
159        /**
160         * Set whether to use Spring-based scanning for entity classes in the classpath
161         * instead of using JPA's standard scanning of jar files with {@code persistence.xml}
162         * markers in them. In case of Spring-based scanning, no {@code persistence.xml}
163         * is necessary; all you need to do is to specify base packages to search here.
164         * <p>Default is none. Specify packages to search for autodetection of your entity
165         * classes in the classpath. This is analogous to Spring's component-scan feature
166         * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
167         * <p><p>Note: There may be limitations in comparison to regular JPA scanning.</b>
168         * In particular, JPA providers may pick up annotated packages for provider-specific
169         * annotations only when driven by {@code persistence.xml}. As of 4.1, Spring's
170         * scan can detect annotated packages as well if supported by the given
171         * {@link JpaVendorAdapter} (e.g. for Hibernate).
172         * <p>If no explicit {@link #setMappingResources mapping resources} have been
173         * specified in addition to these packages, Spring's setup looks for a default
174         * {@code META-INF/orm.xml} file in the classpath, registering it as a mapping
175         * resource for the default unit if the mapping file is not co-located with a
176         * {@code persistence.xml} file (in which case we assume it is only meant to be
177         * used with the persistence units defined there, like in standard JPA).
178         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
179         * @param packagesToScan one or more base packages to search, analogous to
180         * Spring's component-scan configuration for regular Spring components
181         * @see #setPersistenceUnitManager
182         * @see DefaultPersistenceUnitManager#setPackagesToScan
183         */
184        public void setPackagesToScan(String... packagesToScan) {
185                this.internalPersistenceUnitManager.setPackagesToScan(packagesToScan);
186        }
187
188        /**
189         * Specify one or more mapping resources (equivalent to {@code <mapping-file>}
190         * entries in {@code persistence.xml}) for the default persistence unit.
191         * Can be used on its own or in combination with entity scanning in the classpath,
192         * in both cases avoiding {@code persistence.xml}.
193         * <p>Note that mapping resources must be relative to the classpath root,
194         * e.g. "META-INF/mappings.xml" or "com/mycompany/repository/mappings.xml",
195         * so that they can be loaded through {@code ClassLoader.getResource}.
196         * <p>If no explicit mapping resources have been specified next to
197         * {@link #setPackagesToScan packages to scan}, Spring's setup looks for a default
198         * {@code META-INF/orm.xml} file in the classpath, registering it as a mapping
199         * resource for the default unit if the mapping file is not co-located with a
200         * {@code persistence.xml} file (in which case we assume it is only meant to be
201         * used with the persistence units defined there, like in standard JPA).
202         * <p>Note that specifying an empty array/list here suppresses the default
203         * {@code META-INF/orm.xml} check. On the other hand, explicitly specifying
204         * {@code META-INF/orm.xml} here will register that file even if it happens
205         * to be co-located with a {@code persistence.xml} file.
206         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
207         * @see #setPersistenceUnitManager
208         * @see DefaultPersistenceUnitManager#setMappingResources
209         */
210        public void setMappingResources(String... mappingResources) {
211                this.internalPersistenceUnitManager.setMappingResources(mappingResources);
212        }
213
214        /**
215         * Specify the JPA 2.0 shared cache mode for this persistence unit,
216         * overriding a value in {@code persistence.xml} if set.
217         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
218         * @since 4.0
219         * @see javax.persistence.spi.PersistenceUnitInfo#getSharedCacheMode()
220         * @see #setPersistenceUnitManager
221         */
222        public void setSharedCacheMode(SharedCacheMode sharedCacheMode) {
223                this.internalPersistenceUnitManager.setSharedCacheMode(sharedCacheMode);
224        }
225
226        /**
227         * Specify the JPA 2.0 validation mode for this persistence unit,
228         * overriding a value in {@code persistence.xml} if set.
229         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
230         * @since 4.0
231         * @see javax.persistence.spi.PersistenceUnitInfo#getValidationMode()
232         * @see #setPersistenceUnitManager
233         */
234        public void setValidationMode(ValidationMode validationMode) {
235                this.internalPersistenceUnitManager.setValidationMode(validationMode);
236        }
237
238        /**
239         * Specify the JDBC DataSource that the JPA persistence provider is supposed
240         * to use for accessing the database. This is an alternative to keeping the
241         * JDBC configuration in {@code persistence.xml}, passing in a Spring-managed
242         * DataSource instead.
243         * <p>In JPA speak, a DataSource passed in here will be used as "nonJtaDataSource"
244         * on the PersistenceUnitInfo passed to the PersistenceProvider, as well as
245         * overriding data source configuration in {@code persistence.xml} (if any).
246         * Note that this variant typically works for JTA transaction management as well;
247         * if it does not, consider using the explicit {@link #setJtaDataSource} instead.
248         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
249         * @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
250         * @see #setPersistenceUnitManager
251         */
252        public void setDataSource(DataSource dataSource) {
253                this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(dataSource));
254                this.internalPersistenceUnitManager.setDefaultDataSource(dataSource);
255        }
256
257        /**
258         * Specify the JDBC DataSource that the JPA persistence provider is supposed
259         * to use for accessing the database. This is an alternative to keeping the
260         * JDBC configuration in {@code persistence.xml}, passing in a Spring-managed
261         * DataSource instead.
262         * <p>In JPA speak, a DataSource passed in here will be used as "jtaDataSource"
263         * on the PersistenceUnitInfo passed to the PersistenceProvider, as well as
264         * overriding data source configuration in {@code persistence.xml} (if any).
265         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
266         * @see javax.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
267         * @see #setPersistenceUnitManager
268         */
269        public void setJtaDataSource(DataSource jtaDataSource) {
270                this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(jtaDataSource));
271                this.internalPersistenceUnitManager.setDefaultJtaDataSource(jtaDataSource);
272        }
273
274        /**
275         * Set the PersistenceUnitPostProcessors to be applied to the
276         * PersistenceUnitInfo used for creating this EntityManagerFactory.
277         * <p>Such post-processors can, for example, register further entity
278         * classes and jar files, in addition to the metadata read from
279         * {@code persistence.xml}.
280         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
281         * @see #setPersistenceUnitManager
282         */
283        public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... postProcessors) {
284                this.internalPersistenceUnitManager.setPersistenceUnitPostProcessors(postProcessors);
285        }
286
287        /**
288         * Specify the Spring LoadTimeWeaver to use for class instrumentation according
289         * to the JPA class transformer contract.
290         * <p>It is a not required to specify a LoadTimeWeaver: Most providers will be
291         * able to provide a subset of their functionality without class instrumentation
292         * as well, or operate with their VM agent specified on JVM startup.
293         * <p>In terms of Spring-provided weaving options, the most important ones are
294         * InstrumentationLoadTimeWeaver, which requires a Spring-specific (but very general)
295         * VM agent specified on JVM startup, and ReflectiveLoadTimeWeaver, which interacts
296         * with an underlying ClassLoader based on specific extended methods being available
297         * on it (for example, interacting with Spring's TomcatInstrumentableClassLoader).
298         * <p><b>NOTE:</b> As of Spring 2.5, the context's default LoadTimeWeaver (defined
299         * as bean with name "loadTimeWeaver") will be picked up automatically, if available,
300         * removing the need for LoadTimeWeaver configuration on each affected target bean.</b>
301         * Consider using the {@code context:load-time-weaver} XML tag for creating
302         * such a shared LoadTimeWeaver (autodetecting the environment by default).
303         * <p><b>NOTE: Only applied if no external PersistenceUnitManager specified.</b>
304         * Otherwise, the external {@link #setPersistenceUnitManager PersistenceUnitManager}
305         * is responsible for the weaving configuration.
306         * @see org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver
307         * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
308         * @see org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader
309         */
310        @Override
311        public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
312                this.internalPersistenceUnitManager.setLoadTimeWeaver(loadTimeWeaver);
313        }
314
315        @Override
316        public void setResourceLoader(ResourceLoader resourceLoader) {
317                this.internalPersistenceUnitManager.setResourceLoader(resourceLoader);
318        }
319
320
321        @Override
322        public void afterPropertiesSet() throws PersistenceException {
323                PersistenceUnitManager managerToUse = this.persistenceUnitManager;
324                if (this.persistenceUnitManager == null) {
325                        this.internalPersistenceUnitManager.afterPropertiesSet();
326                        managerToUse = this.internalPersistenceUnitManager;
327                }
328
329                this.persistenceUnitInfo = determinePersistenceUnitInfo(managerToUse);
330                JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
331                if (jpaVendorAdapter != null && this.persistenceUnitInfo instanceof SmartPersistenceUnitInfo) {
332                        ((SmartPersistenceUnitInfo) this.persistenceUnitInfo).setPersistenceProviderPackageName(
333                                        jpaVendorAdapter.getPersistenceProviderRootPackage());
334                }
335
336                super.afterPropertiesSet();
337        }
338
339        @Override
340        protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException {
341                Assert.state(this.persistenceUnitInfo != null, "PersistenceUnitInfo not initialized");
342
343                PersistenceProvider provider = getPersistenceProvider();
344                if (provider == null) {
345                        String providerClassName = this.persistenceUnitInfo.getPersistenceProviderClassName();
346                        if (providerClassName == null) {
347                                throw new IllegalArgumentException(
348                                                "No PersistenceProvider specified in EntityManagerFactory configuration, " +
349                                                "and chosen PersistenceUnitInfo does not specify a provider class name either");
350                        }
351                        Class<?> providerClass = ClassUtils.resolveClassName(providerClassName, getBeanClassLoader());
352                        provider = (PersistenceProvider) BeanUtils.instantiateClass(providerClass);
353                }
354
355                if (logger.isInfoEnabled()) {
356                        logger.info("Building JPA container EntityManagerFactory for persistence unit '" +
357                                        this.persistenceUnitInfo.getPersistenceUnitName() + "'");
358                }
359                EntityManagerFactory emf =
360                                provider.createContainerEntityManagerFactory(this.persistenceUnitInfo, getJpaPropertyMap());
361                postProcessEntityManagerFactory(emf, this.persistenceUnitInfo);
362
363                return emf;
364        }
365
366
367        /**
368         * Determine the PersistenceUnitInfo to use for the EntityManagerFactory
369         * created by this bean.
370         * <p>The default implementation reads in all persistence unit infos from
371         * {@code persistence.xml}, as defined in the JPA specification.
372         * If no entity manager name was specified, it takes the first info in the
373         * array as returned by the reader. Otherwise, it checks for a matching name.
374         * @param persistenceUnitManager the PersistenceUnitManager to obtain from
375         * @return the chosen PersistenceUnitInfo
376         */
377        protected PersistenceUnitInfo determinePersistenceUnitInfo(PersistenceUnitManager persistenceUnitManager) {
378                if (getPersistenceUnitName() != null) {
379                        return persistenceUnitManager.obtainPersistenceUnitInfo(getPersistenceUnitName());
380                }
381                else {
382                        return persistenceUnitManager.obtainDefaultPersistenceUnitInfo();
383                }
384        }
385
386        /**
387         * Hook method allowing subclasses to customize the EntityManagerFactory
388         * after its creation via the PersistenceProvider.
389         * <p>The default implementation is empty.
390         * @param emf the newly created EntityManagerFactory we are working with
391         * @param pui the PersistenceUnitInfo used to configure the EntityManagerFactory
392         * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory
393         */
394        protected void postProcessEntityManagerFactory(EntityManagerFactory emf, PersistenceUnitInfo pui) {
395        }
396
397
398        @Override
399        public PersistenceUnitInfo getPersistenceUnitInfo() {
400                return this.persistenceUnitInfo;
401        }
402
403        @Override
404        public String getPersistenceUnitName() {
405                if (this.persistenceUnitInfo != null) {
406                        return this.persistenceUnitInfo.getPersistenceUnitName();
407                }
408                return super.getPersistenceUnitName();
409        }
410
411        @Override
412        public DataSource getDataSource() {
413                if (this.persistenceUnitInfo != null) {
414                        return (this.persistenceUnitInfo.getJtaDataSource() != null ?
415                                        this.persistenceUnitInfo.getJtaDataSource() :
416                                        this.persistenceUnitInfo.getNonJtaDataSource());
417                }
418                return (this.internalPersistenceUnitManager.getDefaultJtaDataSource() != null ?
419                                this.internalPersistenceUnitManager.getDefaultJtaDataSource() :
420                                this.internalPersistenceUnitManager.getDefaultDataSource());
421        }
422
423}