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}