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