001/* 002 * Copyright 2012-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 * http://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.boot.autoconfigure.orm.jpa; 018 019import java.util.List; 020import java.util.Map; 021 022import javax.persistence.EntityManagerFactory; 023import javax.sql.DataSource; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027 028import org.springframework.beans.BeansException; 029import org.springframework.beans.factory.BeanFactory; 030import org.springframework.beans.factory.BeanFactoryAware; 031import org.springframework.beans.factory.ObjectProvider; 032import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 033import org.springframework.boot.autoconfigure.AutoConfigurationPackages; 034import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 035import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 036import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 037import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 038import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 039import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; 040import org.springframework.boot.autoconfigure.domain.EntityScanPackages; 041import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; 042import org.springframework.boot.context.properties.EnableConfigurationProperties; 043import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; 044import org.springframework.context.annotation.Bean; 045import org.springframework.context.annotation.Configuration; 046import org.springframework.context.annotation.Import; 047import org.springframework.context.annotation.Primary; 048import org.springframework.orm.jpa.JpaTransactionManager; 049import org.springframework.orm.jpa.JpaVendorAdapter; 050import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 051import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; 052import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; 053import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor; 054import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; 055import org.springframework.transaction.PlatformTransactionManager; 056import org.springframework.transaction.jta.JtaTransactionManager; 057import org.springframework.util.ObjectUtils; 058import org.springframework.util.StringUtils; 059import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 060import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 061 062/** 063 * Base {@link EnableAutoConfiguration Auto-configuration} for JPA. 064 * 065 * @author Phillip Webb 066 * @author Dave Syer 067 * @author Oliver Gierke 068 * @author Andy Wilkinson 069 * @author Kazuki Shimizu 070 * @author EddĂș MelĂ©ndez 071 */ 072@Configuration 073@EnableConfigurationProperties(JpaProperties.class) 074@Import(DataSourceInitializedPublisher.Registrar.class) 075public abstract class JpaBaseConfiguration implements BeanFactoryAware { 076 077 private final DataSource dataSource; 078 079 private final JpaProperties properties; 080 081 private final JtaTransactionManager jtaTransactionManager; 082 083 private final TransactionManagerCustomizers transactionManagerCustomizers; 084 085 private ConfigurableListableBeanFactory beanFactory; 086 087 protected JpaBaseConfiguration(DataSource dataSource, JpaProperties properties, 088 ObjectProvider<JtaTransactionManager> jtaTransactionManager, 089 ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { 090 this.dataSource = dataSource; 091 this.properties = properties; 092 this.jtaTransactionManager = jtaTransactionManager.getIfAvailable(); 093 this.transactionManagerCustomizers = transactionManagerCustomizers 094 .getIfAvailable(); 095 } 096 097 @Bean 098 @ConditionalOnMissingBean 099 public PlatformTransactionManager transactionManager() { 100 JpaTransactionManager transactionManager = new JpaTransactionManager(); 101 if (this.transactionManagerCustomizers != null) { 102 this.transactionManagerCustomizers.customize(transactionManager); 103 } 104 return transactionManager; 105 } 106 107 @Bean 108 @ConditionalOnMissingBean 109 public JpaVendorAdapter jpaVendorAdapter() { 110 AbstractJpaVendorAdapter adapter = createJpaVendorAdapter(); 111 adapter.setShowSql(this.properties.isShowSql()); 112 adapter.setDatabase(this.properties.determineDatabase(this.dataSource)); 113 adapter.setDatabasePlatform(this.properties.getDatabasePlatform()); 114 adapter.setGenerateDdl(this.properties.isGenerateDdl()); 115 return adapter; 116 } 117 118 @Bean 119 @ConditionalOnMissingBean 120 public EntityManagerFactoryBuilder entityManagerFactoryBuilder( 121 JpaVendorAdapter jpaVendorAdapter, 122 ObjectProvider<PersistenceUnitManager> persistenceUnitManager, 123 ObjectProvider<EntityManagerFactoryBuilderCustomizer> customizers) { 124 EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder( 125 jpaVendorAdapter, this.properties.getProperties(), 126 persistenceUnitManager.getIfAvailable()); 127 customizers.orderedStream() 128 .forEach((customizer) -> customizer.customize(builder)); 129 return builder; 130 } 131 132 @Bean 133 @Primary 134 @ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, 135 EntityManagerFactory.class }) 136 public LocalContainerEntityManagerFactoryBean entityManagerFactory( 137 EntityManagerFactoryBuilder factoryBuilder) { 138 Map<String, Object> vendorProperties = getVendorProperties(); 139 customizeVendorProperties(vendorProperties); 140 return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan()) 141 .properties(vendorProperties).mappingResources(getMappingResources()) 142 .jta(isJta()).build(); 143 } 144 145 protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter(); 146 147 protected abstract Map<String, Object> getVendorProperties(); 148 149 /** 150 * Customize vendor properties before they are used. Allows for post processing (for 151 * example to configure JTA specific settings). 152 * @param vendorProperties the vendor properties to customize 153 */ 154 protected void customizeVendorProperties(Map<String, Object> vendorProperties) { 155 } 156 157 protected String[] getPackagesToScan() { 158 List<String> packages = EntityScanPackages.get(this.beanFactory) 159 .getPackageNames(); 160 if (packages.isEmpty() && AutoConfigurationPackages.has(this.beanFactory)) { 161 packages = AutoConfigurationPackages.get(this.beanFactory); 162 } 163 return StringUtils.toStringArray(packages); 164 } 165 166 private String[] getMappingResources() { 167 List<String> mappingResources = this.properties.getMappingResources(); 168 return (!ObjectUtils.isEmpty(mappingResources) 169 ? StringUtils.toStringArray(mappingResources) : null); 170 } 171 172 /** 173 * Return the JTA transaction manager. 174 * @return the transaction manager or {@code null} 175 */ 176 protected JtaTransactionManager getJtaTransactionManager() { 177 return this.jtaTransactionManager; 178 } 179 180 /** 181 * Returns if a JTA {@link PlatformTransactionManager} is being used. 182 * @return if a JTA transaction manager is being used 183 */ 184 protected final boolean isJta() { 185 return (this.jtaTransactionManager != null); 186 } 187 188 /** 189 * Return the {@link JpaProperties}. 190 * @return the properties 191 */ 192 protected final JpaProperties getProperties() { 193 return this.properties; 194 } 195 196 /** 197 * Return the {@link DataSource}. 198 * @return the data source 199 */ 200 protected final DataSource getDataSource() { 201 return this.dataSource; 202 } 203 204 @Override 205 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 206 this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; 207 } 208 209 @Configuration 210 @ConditionalOnWebApplication(type = Type.SERVLET) 211 @ConditionalOnClass(WebMvcConfigurer.class) 212 @ConditionalOnMissingBean({ OpenEntityManagerInViewInterceptor.class, 213 OpenEntityManagerInViewFilter.class }) 214 @ConditionalOnProperty(prefix = "spring.jpa", name = "open-in-view", havingValue = "true", matchIfMissing = true) 215 protected static class JpaWebConfiguration { 216 217 // Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when 218 // not on the classpath 219 @Configuration 220 protected static class JpaWebMvcConfiguration implements WebMvcConfigurer { 221 222 private static final Log logger = LogFactory 223 .getLog(JpaWebMvcConfiguration.class); 224 225 private final JpaProperties jpaProperties; 226 227 protected JpaWebMvcConfiguration(JpaProperties jpaProperties) { 228 this.jpaProperties = jpaProperties; 229 } 230 231 @Bean 232 public OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() { 233 if (this.jpaProperties.getOpenInView() == null) { 234 logger.warn("spring.jpa.open-in-view is enabled by default. " 235 + "Therefore, database queries may be performed during view " 236 + "rendering. Explicitly configure " 237 + "spring.jpa.open-in-view to disable this warning"); 238 } 239 return new OpenEntityManagerInViewInterceptor(); 240 } 241 242 @Override 243 public void addInterceptors(InterceptorRegistry registry) { 244 registry.addWebRequestInterceptor(openEntityManagerInViewInterceptor()); 245 } 246 247 } 248 249 } 250 251}