diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java index 266fcecd63e..d97e906601d 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java @@ -154,7 +154,7 @@ public class DataSourceAutoConfiguration { @Bean public DataSource dataSource() { // @formatter:off - DataSourceFactory factory = DataSourceFactory + DataSourceBuilder factory = DataSourceBuilder .create(this.properties.getClassLoader()) .driverClassName(this.properties.getDriverClassName()) .url(this.properties.getUrl()) @@ -209,7 +209,7 @@ public class DataSourceAutoConfiguration { * the driver class can actually be loaded by the data source. */ private ClassLoader getDataSourceClassLoader(ConditionContext context) { - Class dataSourceClass = new DataSourceFactory(context.getClassLoader()) + Class dataSourceClass = new DataSourceBuilder(context.getClassLoader()) .findType(); if (dataSourceClass == null) { return null; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceFactory.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java similarity index 86% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceFactory.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java index 139691b5f34..def4184327b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceFactory.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBuilder.java @@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils; * * @author Dave Syer */ -public class DataSourceFactory { +public class DataSourceBuilder { private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { "org.apache.tomcat.jdbc.pool.DataSource", @@ -51,15 +51,15 @@ public class DataSourceFactory { private Map properties = new HashMap(); - public static DataSourceFactory create() { - return new DataSourceFactory(null); + public static DataSourceBuilder create() { + return new DataSourceBuilder(null); } - public static DataSourceFactory create(ClassLoader classLoader) { - return new DataSourceFactory(classLoader); + public static DataSourceBuilder create(ClassLoader classLoader) { + return new DataSourceBuilder(classLoader); } - public DataSourceFactory(ClassLoader classLoader) { + public DataSourceBuilder(ClassLoader classLoader) { this.classLoader = classLoader; } @@ -82,27 +82,27 @@ public class DataSourceFactory { return new MutablePropertyValues(this.properties); } - public DataSourceFactory type(Class type) { + public DataSourceBuilder type(Class type) { this.type = type; return this; } - public DataSourceFactory url(String url) { + public DataSourceBuilder url(String url) { this.properties.put("url", url); return this; } - public DataSourceFactory driverClassName(String driverClassName) { + public DataSourceBuilder driverClassName(String driverClassName) { this.properties.put("driverClassName", driverClassName); return this; } - public DataSourceFactory username(String username) { + public DataSourceBuilder username(String username) { this.properties.put("username", username); return this; } - public DataSourceFactory password(String password) { + public DataSourceBuilder password(String password) { this.properties.put("password", password); return this; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilder.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilder.java new file mode 100644 index 00000000000..7933bc3c620 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryBuilder.java @@ -0,0 +1,163 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.orm.jpa; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.sql.DataSource; + +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; +import org.springframework.util.ClassUtils; + +/** + * Convenient builder for JPA EntityManagerFactory instances. Collects common + * configuration when constructed and then allows you to create one or more + * {@link LocalContainerEntityManagerFactoryBean} through a fluent builder pattern. The + * most common options are covered in the builder, but you can always manipulate the + * product of the builder if you need more control, before returning it from a + * @Bean definition. + * + * @author Dave Syer + */ +public class EntityManagerFactoryBuilder { + + private JpaVendorAdapter jpaVendorAdapter; + + private PersistenceUnitManager persistenceUnitManager; + + private JpaProperties properties; + + /** + * Create a new instance passing in the common pieces that will be shared if multiple + * EntityManagerFactory instances are created. + * + * @param jpaVendorAdapter a vendor adapter + * @param properties common configuration options, including generic map for JPA + * vendor properties + * @param persistenceUnitManager optional source of persistence unit information (can + * be null) + */ + public EntityManagerFactoryBuilder(JpaVendorAdapter jpaVendorAdapter, + JpaProperties properties, PersistenceUnitManager persistenceUnitManager) { + this.jpaVendorAdapter = jpaVendorAdapter; + this.persistenceUnitManager = persistenceUnitManager; + this.properties = properties; + } + + public Builder dataSource(DataSource dataSource) { + return new Builder(dataSource); + } + + /** + * A fluent builder for a LocalContainerEntityManagerFactoryBean. + * + * @author Dave Syer + */ + public class Builder { + + private DataSource dataSource; + + private String[] packagesToScan; + + private String persistenceUnit; + + private Builder(DataSource dataSource) { + this.dataSource = dataSource; + } + + /** + * The names of packages to scan for @Entity annotations. + * + * @param packagesToScan packages to scan + * @return the builder for fluent usage + */ + public Builder packages(String... packagesToScan) { + this.packagesToScan = packagesToScan; + return this; + } + + /** + * The classes whose packages should be scanned for @Entity + * annotations. + * + * @param basePackageClasses the classes to use + * @return the builder for fluent usage + */ + public Builder packages(Class... basePackageClasses) { + Set packages = new HashSet(); + for (Class type : basePackageClasses) { + packages.add(ClassUtils.getPackageName(type)); + } + this.packagesToScan = packages.toArray(new String[0]); + return this; + } + + /** + * The name of the persistence unit. If only building one EntityManagerFactory you + * can omit this, but if there are more than one in the same application you + * should give them distinct names. + * + * @param persistenceUnit the name of the persistence unit + * @return the builder for fluent usage + */ + public Builder persistenceUnit(String persistenceUnit) { + this.persistenceUnit = persistenceUnit; + return this; + } + + /** + * Generic properties for standard JPA or vendor-specific configuration. These + * properties override any values provided in the {@link JpaProperties} used to + * create the builder. + * + * @param properties the properties to use + * @return the builder for fluent usage + */ + public Builder properties(Map properties) { + EntityManagerFactoryBuilder.this.properties.getProperties() + .putAll(properties); + return this; + } + + public LocalContainerEntityManagerFactoryBean build() { + LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); + if (EntityManagerFactoryBuilder.this.persistenceUnitManager != null) { + entityManagerFactoryBean + .setPersistenceUnitManager(EntityManagerFactoryBuilder.this.persistenceUnitManager); + } + if (this.persistenceUnit != null) { + entityManagerFactoryBean.setPersistenceUnitName(this.persistenceUnit); + } + entityManagerFactoryBean + .setJpaVendorAdapter(EntityManagerFactoryBuilder.this.jpaVendorAdapter); + entityManagerFactoryBean.setDataSource(this.dataSource); + entityManagerFactoryBean.setPackagesToScan(this.packagesToScan); + entityManagerFactoryBean.getJpaPropertyMap().putAll( + EntityManagerFactoryBuilder.this.properties.getProperties()); + entityManagerFactoryBean.getJpaPropertyMap().putAll( + EntityManagerFactoryBuilder.this.properties + .getHibernateProperties(this.dataSource)); + return entityManagerFactoryBean; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java index 9fb3f14049c..2d059bb912e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java @@ -16,10 +16,7 @@ package org.springframework.boot.autoconfigure.orm.jpa; -import java.util.Map; - import javax.persistence.EntityManager; -import javax.sql.DataSource; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -27,14 +24,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.HibernateEntityManagerCondition; -import org.springframework.boot.bind.RelaxedPropertyResolver; -import org.springframework.boot.orm.jpa.SpringNamingStrategy; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; @@ -54,44 +47,11 @@ import org.springframework.util.ClassUtils; @AutoConfigureAfter(DataSourceAutoConfiguration.class) public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { - private RelaxedPropertyResolver environment; - - public HibernateJpaAutoConfiguration() { - this.environment = null; - } - - @Override - public void setEnvironment(Environment environment) { - super.setEnvironment(environment); - this.environment = new RelaxedPropertyResolver(environment, - "spring.jpa.hibernate."); - } - @Override protected AbstractJpaVendorAdapter createJpaVendorAdapter() { return new HibernateJpaVendorAdapter(); } - @Override - protected void configure( - LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) { - Map properties = entityManagerFactoryBean.getJpaPropertyMap(); - properties.put("hibernate.ejb.naming_strategy", this.environment.getProperty( - "naming-strategy", SpringNamingStrategy.class.getName())); - String ddlAuto = this.environment.getProperty("ddl-auto", - getDefaultDdlAuto(entityManagerFactoryBean.getDataSource())); - if (!"none".equals(ddlAuto)) { - properties.put("hibernate.hbm2ddl.auto", ddlAuto); - } - } - - private String getDefaultDdlAuto(DataSource dataSource) { - if (EmbeddedDatabaseConnection.isEmbedded(dataSource)) { - return "create-drop"; - } - return "none"; - } - static class HibernateEntityManagerCondition extends SpringBootCondition { private static String[] CLASS_NAMES = { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 10114be5d34..4d30acaf372 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -30,11 +30,10 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.bind.RelaxedPropertyResolver; -import org.springframework.context.EnvironmentAware; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; +import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -42,7 +41,6 @@ import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; -import org.springframework.orm.jpa.vendor.Database; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @@ -54,19 +52,19 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter * @author Dave Syer * @author Oliver Gierke */ -public abstract class JpaBaseConfiguration implements BeanFactoryAware, EnvironmentAware { +@EnableConfigurationProperties(JpaProperties.class) +public abstract class JpaBaseConfiguration implements BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; - private RelaxedPropertyResolver environment; + @Autowired + private DataSource dataSource; @Autowired(required = false) private PersistenceUnitManager persistenceUnitManager; - @Override - public void setEnvironment(Environment environment) { - this.environment = new RelaxedPropertyResolver(environment, "spring.jpa."); - } + @Autowired + private JpaProperties jpaProperties; @Bean @ConditionalOnMissingBean(PlatformTransactionManager.class) @@ -75,47 +73,35 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware, Environm } @Bean - @ConditionalOnMissingBean(name = "entityManagerFactory") - public LocalContainerEntityManagerFactoryBean entityManagerFactory( - JpaVendorAdapter jpaVendorAdapter) { - LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); - if (this.persistenceUnitManager != null) { - entityManagerFactoryBean - .setPersistenceUnitManager(this.persistenceUnitManager); - } - entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter); - entityManagerFactoryBean.setDataSource(getDataSource()); - entityManagerFactoryBean.setPackagesToScan(getPackagesToScan()); - entityManagerFactoryBean.getJpaPropertyMap().putAll( - this.environment.getSubProperties("properties.")); - configure(entityManagerFactoryBean); - return entityManagerFactoryBean; - } - - @Bean - @ConditionalOnMissingBean(JpaVendorAdapter.class) + @ConditionalOnMissingBean public JpaVendorAdapter jpaVendorAdapter() { AbstractJpaVendorAdapter adapter = createJpaVendorAdapter(); - adapter.setShowSql(this.environment.getProperty("show-sql", Boolean.class, true)); - adapter.setDatabasePlatform(this.environment.getProperty("database-platform")); - adapter.setDatabase(this.environment.getProperty("database", Database.class, - Database.DEFAULT)); - adapter.setGenerateDdl(this.environment.getProperty("generate-ddl", - Boolean.class, false)); + adapter.setShowSql(this.jpaProperties.isShowSql()); + adapter.setDatabase(this.jpaProperties.getDatabase()); + adapter.setDatabasePlatform(this.jpaProperties.getDatabasePlatform()); + adapter.setGenerateDdl(this.jpaProperties.isGenerateDdl()); return adapter; } - protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter(); - - protected DataSource getDataSource() { - try { - return this.beanFactory.getBean("dataSource", DataSource.class); - } - catch (RuntimeException ex) { - return this.beanFactory.getBean(DataSource.class); - } + @Bean + @ConditionalOnMissingBean + public EntityManagerFactoryBuilder entityManagerFactoryBuilder( + JpaVendorAdapter jpaVendorAdapter) { + EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder( + jpaVendorAdapter, this.jpaProperties, this.persistenceUnitManager); + return builder; } + @Bean + @Primary + @ConditionalOnMissingBean + public LocalContainerEntityManagerFactoryBean entityManagerFactory( + EntityManagerFactoryBuilder factory) { + return factory.dataSource(this.dataSource).packages(getPackagesToScan()).build(); + } + + protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter(); + protected String[] getPackagesToScan() { List basePackages = AutoConfigurationPackages.get(this.beanFactory); return basePackages.toArray(new String[basePackages.size()]); diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java new file mode 100644 index 00000000000..73abbb8bd40 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java @@ -0,0 +1,157 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.orm.jpa; + +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.orm.jpa.SpringNamingStrategy; +import org.springframework.orm.jpa.vendor.Database; + +/** + * External configuration properties for a JPA EntityManagerFactory created by Spring. + * + * @author Dave Syer + */ +@ConfigurationProperties(prefix = "spring.jpa") +public class JpaProperties { + + private Map properties = new HashMap(); + + private String databasePlatform; + + private Database database = Database.DEFAULT; + + private boolean generateDdl = false; + + private boolean showSql = false; + + private Hibernate hibernate = new Hibernate(); + + public Map getProperties() { + return this.properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public String getDatabasePlatform() { + return this.databasePlatform; + } + + public void setDatabasePlatform(String databasePlatform) { + this.databasePlatform = databasePlatform; + } + + public Database getDatabase() { + return this.database; + } + + public void setDatabase(Database database) { + this.database = database; + } + + public boolean isGenerateDdl() { + return this.generateDdl; + } + + public void setGenerateDdl(boolean generateDdl) { + this.generateDdl = generateDdl; + } + + public boolean isShowSql() { + return this.showSql; + } + + public void setShowSql(boolean showSql) { + this.showSql = showSql; + } + + public Hibernate getHibernate() { + return this.hibernate; + } + + public void setHibernate(Hibernate hibernate) { + this.hibernate = hibernate; + } + + public Map getHibernateProperties(DataSource dataSource) { + return this.hibernate.getAdditionalProperties(this.properties, dataSource); + } + + public static class Hibernate { + + private Class namingStrategy; + + private static Class DEFAULT_NAMING_STRATEGY = SpringNamingStrategy.class; + + private String ddlAuto; + + public Class getNamingStrategy() { + return this.namingStrategy; + } + + public void setNamingStrategy(Class namingStrategy) { + this.namingStrategy = namingStrategy; + } + + public String getDdlAuto() { + return this.ddlAuto; + } + + public void setDdlAuto(String ddlAuto) { + this.ddlAuto = ddlAuto; + } + + private Map getAdditionalProperties(Map existing, + DataSource dataSource) { + Map result = new HashMap(); + if (!isAlreadyProvided(existing, "ejb.naming_strategy") + && this.namingStrategy != null) { + result.put("hibernate.ejb.naming_strategy", this.namingStrategy.getName()); + } + else if (this.namingStrategy == null) { + result.put("hibernate.ejb.naming_strategy", + DEFAULT_NAMING_STRATEGY.getName()); + } + String ddlAuto = this.ddlAuto != null ? this.ddlAuto + : getDefaultDdlAuto(dataSource); + if (!isAlreadyProvided(existing, "hbm2ddl.auto") && !"none".equals(ddlAuto)) { + result.put("hibernate.hbm2ddl.auto", ddlAuto); + } + return result; + } + + private boolean isAlreadyProvided(Map existing, String key) { + return existing.containsKey("hibernate." + key); + } + + private String getDefaultDdlAuto(DataSource dataSource) { + if (EmbeddedDatabaseConnection.isEmbedded(dataSource)) { + return "create-drop"; + } + return "none"; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AdhocTestSuite.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AdhocTestSuite.java index 944ed6fcb13..6d8dfdca3c4 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AdhocTestSuite.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AdhocTestSuite.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure; +import org.junit.Ignore; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @@ -30,7 +31,7 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat @RunWith(Suite.class) @SuiteClasses({ HibernateJpaAutoConfigurationTests.class, LiquibaseAutoConfigurationTests.class }) -// @Ignore +@Ignore public class AdhocTestSuite { } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/CommonsDataSourceConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/CommonsDataSourceConfigurationTests.java index a94fe4841a5..77a5e25792c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/CommonsDataSourceConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/CommonsDataSourceConfigurationTests.java @@ -94,7 +94,7 @@ public class CommonsDataSourceConfigurationTests { @Bean @ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX) public DataSource dataSource() { - return DataSourceFactory.create().type(BasicDataSource.class).build(); + return DataSourceBuilder.create().type(BasicDataSource.class).build(); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java index 7afe94aec9a..d564a0e4b74 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java @@ -105,7 +105,7 @@ public class HikariDataSourceConfigurationTests { @Bean @ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX) public DataSource dataSource() { - return DataSourceFactory.create().type(HikariDataSource.class).build(); + return DataSourceBuilder.create().type(HikariDataSource.class).build(); } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java index 5820a20cc32..3cd8dfefa3d 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java @@ -137,7 +137,7 @@ public class TomcatDataSourceConfigurationTests { @Bean @ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX) public DataSource dataSource() { - return DataSourceFactory.create() + return DataSourceBuilder.create() .type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index f3a987231ac..cd05fce0dab 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -21,6 +21,7 @@ import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; /** @@ -39,7 +40,7 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura @Test public void testCustomNamingStrategy() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, - "spring.jpa.hibernate.namingstrategy:" + "spring.jpa.hibernate.namingStrategy:" + "org.hibernate.cfg.EJB3NamingStrategy"); setupTestConfiguration(); this.context.refresh(); @@ -50,4 +51,20 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura assertThat(actual, equalTo("org.hibernate.cfg.EJB3NamingStrategy")); } + @Test + public void testCustomNamingStrategyViaJpaProperties() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.jpa.properties.hibernate.ejb.naming_strategy:" + + "org.hibernate.cfg.EJB3NamingStrategy"); + setupTestConfiguration(); + this.context.refresh(); + LocalContainerEntityManagerFactoryBean bean = this.context + .getBean(LocalContainerEntityManagerFactoryBean.class); + String actual = (String) bean.getJpaPropertyMap().get( + "hibernate.ejb.naming_strategy"); + // You can't override this one from spring.jpa.properties because it has an + // opinionated default + assertThat(actual, not(equalTo("org.hibernate.cfg.EJB3NamingStrategy"))); + } + } diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index 0265f0389cd..ba4707f857d 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -925,12 +925,55 @@ action. [[howto-configure-a-datasource]] === Configure a DataSource -To override the default settings just define a `@Bean` of your own of type `DataSource`. +To override the default settings just define a `@Bean` of your own of type `DataSource`. +Spring Boot provides a utility builder class `DataSourceBuilder` that can be used +to create one of the standard ones (if it is on the classpath), or you can just create +your own, and bind it to a set of `Environment` properties e.g. + +[source,java,indent=0,subs="verbatim,quotes,attributes"] +---- + @Bean + @ConfigurationProperties(prefix="datasource.mine") + public DataSource dataSource() { + return new FancyDataSource(); + } +---- + +[source,properties,indent=0] +---- + datasource.mine.jdbcUrl=jdbc:h2:mem:mydb + datasource.mine.user=sa + datasource.mine.poolSize=30 +---- + + See '<>' in the ``Spring Boot features'' section and the {sc-spring-boot-autoconfigure}/jdbc/DataSourceAutoConfiguration.{sc-ext}[`DataSourceAutoConfiguration`] class for more details. +[[howto-two-datasources]] +=== Configure Two DataSources +Creating more than one data source works the same as creating the first one. +You might want to mark one of them as `@Primary` if you are using the default +auto-configuration for JDBC or JPA (then that one will be picked up by any +`@Autowired` injections). + +[source,java,indent=0,subs="verbatim,quotes,attributes"] +---- + @Bean + @Primary + @ConfigurationProperties(prefix="datasource.primary") + public DataSource primaryDataSource() { + return new FancyDataSource(); + } + + @Bean + @ConfigurationProperties(prefix="datasource.secondary") + public DataSource secondaryDataSource() { + return new FancyDataSource(); + } +---- [[howto-use-spring-data-repositories]] @@ -998,15 +1041,57 @@ and {sc-spring-boot-autoconfigure}/orm/jpa/JpaBaseConfiguration.{sc-ext}[`JpaBas for more details. - [[howto-use-custom-entity-manager]] === Use a custom EntityManagerFactory To take full control of the configuration of the `EntityManagerFactory`, you need to add -a `@Bean` named "entityManagerFactory". To avoid eager initialization of JPA -infrastructure, Spring Boot auto-configuration does not switch on its entity manager -based on the presence of a bean of that type. Instead it has to do it by name. +a `@Bean` named "entityManagerFactory". Spring Boot auto-configuration switches off its entity manager +based on the presence of a bean of that type. +[[howto-use-two-entity-managers]] +=== Use Two EntityManagers + +Even if the default `EntityManagerFactory` works fine, you will need +to define a new one because otherwise the presence of the second bean +of that type will switch off the default. To make it easy to do that +you can use the convenient `EntityManagerBuilder` provided by Spring +Boot, or if you prefer you can just use the +`LocalContainerEntityManagerFactoryBean` directly from Spring ORM. +Example: + +[source,java,indent=0,subs="verbatim,quotes,attributes"] +---- + // add two data sources configured as above + + @Bean + public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory( + EntityManagerFactoryBuilder builder) { + return builder + .dataSource(customerDataSource()) + .packages(Customer.class) + .persistenceUnit("customers") + .build(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory( + EntityManagerFactoryBuilder builder) { + return builder + .dataSource(orderDataSource()) + .packages(Order.class) + .persistenceUnit("orders") + .build(); + } + +---- + +The configuration above almost works on its own. To complete the +picture you need to configure `TransactionManagers` for the two +`EntityManagers` as well. One of them could be picked up by the +default `JpaTransactionManager` in Spring Boot if you mark it as +`@Primary`. The other would have to be explicitly injected into a new +instance. Or you might be able to use a JTA transaction manager +spanning both. [[howto-use-traditional-persistence-xml]] === Use a traditional persistence.xml diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 7b059968bbc..a1c715c208b 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1392,7 +1392,7 @@ following to your `application.properties`. NOTE: Hibernate's own internal property name for this (if you happen to remember it better) is `hibernate.hbm2ddl.auto`. You can set it, along with other Hibernate native properties, using `spring.jpa.properties.*` (the prefix is stripped before adding them -to the entity manager). Alternatively, `spring.jpa.generate-ddl=false` switches off all +to the entity manager). Also, `spring.jpa.generate-ddl=false` switches off all DDL generation. diff --git a/spring-boot-starters/spring-boot-starter-parent/pom.xml b/spring-boot-starters/spring-boot-starter-parent/pom.xml index 2d3b4ba3c39..35a39d69349 100644 --- a/spring-boot-starters/spring-boot-starter-parent/pom.xml +++ b/spring-boot-starters/spring-boot-starter-parent/pom.xml @@ -127,7 +127,7 @@ true yyyy-MM-dd'T'HH:mm:ssZ true - src/main/resources/git.properties + ${basedir}/src/main/resources/git.properties