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