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}