Commit 7454c486 authored by Dave Syer's avatar Dave Syer

Support for @ConfigurationProperties in JPA

Adds JpaProperties to bind to spring.jpa.* (making those
properties easier to reason about and visible in the
/configprops endpoint).

Also allows easy configuration of multiple EntityManagerFactories via new
EntityManagerFactoryBuilder. JpaBaseConfiguration has a @Bean of that type
so users can inject it to create new or additional EntityManagerFactories.
This also simplifies the Hibernate autoconfiguration.

Also renames the DataSourceFactory to DataSourceBuilder (since that's what it
is).
parent f7397f1d
...@@ -154,7 +154,7 @@ public class DataSourceAutoConfiguration { ...@@ -154,7 +154,7 @@ public class DataSourceAutoConfiguration {
@Bean @Bean
public DataSource dataSource() { public DataSource dataSource() {
// @formatter:off // @formatter:off
DataSourceFactory factory = DataSourceFactory DataSourceBuilder factory = DataSourceBuilder
.create(this.properties.getClassLoader()) .create(this.properties.getClassLoader())
.driverClassName(this.properties.getDriverClassName()) .driverClassName(this.properties.getDriverClassName())
.url(this.properties.getUrl()) .url(this.properties.getUrl())
...@@ -209,7 +209,7 @@ public class DataSourceAutoConfiguration { ...@@ -209,7 +209,7 @@ public class DataSourceAutoConfiguration {
* the driver class can actually be loaded by the data source. * the driver class can actually be loaded by the data source.
*/ */
private ClassLoader getDataSourceClassLoader(ConditionContext context) { private ClassLoader getDataSourceClassLoader(ConditionContext context) {
Class<?> dataSourceClass = new DataSourceFactory(context.getClassLoader()) Class<?> dataSourceClass = new DataSourceBuilder(context.getClassLoader())
.findType(); .findType();
if (dataSourceClass == null) { if (dataSourceClass == null) {
return null; return null;
......
...@@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils; ...@@ -38,7 +38,7 @@ import org.springframework.util.ClassUtils;
* *
* @author Dave Syer * @author Dave Syer
*/ */
public class DataSourceFactory { public class DataSourceBuilder {
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
"org.apache.tomcat.jdbc.pool.DataSource", "org.apache.tomcat.jdbc.pool.DataSource",
...@@ -51,15 +51,15 @@ public class DataSourceFactory { ...@@ -51,15 +51,15 @@ public class DataSourceFactory {
private Map<String, String> properties = new HashMap<String, String>(); private Map<String, String> properties = new HashMap<String, String>();
public static DataSourceFactory create() { public static DataSourceBuilder create() {
return new DataSourceFactory(null); return new DataSourceBuilder(null);
} }
public static DataSourceFactory create(ClassLoader classLoader) { public static DataSourceBuilder create(ClassLoader classLoader) {
return new DataSourceFactory(classLoader); return new DataSourceBuilder(classLoader);
} }
public DataSourceFactory(ClassLoader classLoader) { public DataSourceBuilder(ClassLoader classLoader) {
this.classLoader = classLoader; this.classLoader = classLoader;
} }
...@@ -82,27 +82,27 @@ public class DataSourceFactory { ...@@ -82,27 +82,27 @@ public class DataSourceFactory {
return new MutablePropertyValues(this.properties); return new MutablePropertyValues(this.properties);
} }
public DataSourceFactory type(Class<? extends DataSource> type) { public DataSourceBuilder type(Class<? extends DataSource> type) {
this.type = type; this.type = type;
return this; return this;
} }
public DataSourceFactory url(String url) { public DataSourceBuilder url(String url) {
this.properties.put("url", url); this.properties.put("url", url);
return this; return this;
} }
public DataSourceFactory driverClassName(String driverClassName) { public DataSourceBuilder driverClassName(String driverClassName) {
this.properties.put("driverClassName", driverClassName); this.properties.put("driverClassName", driverClassName);
return this; return this;
} }
public DataSourceFactory username(String username) { public DataSourceBuilder username(String username) {
this.properties.put("username", username); this.properties.put("username", username);
return this; return this;
} }
public DataSourceFactory password(String password) { public DataSourceBuilder password(String password) {
this.properties.put("password", password); this.properties.put("password", password);
return this; return this;
} }
......
/*
* 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
* <code>@Bean</code> 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 <code>@Entity</code> 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 <code>@Entity</code>
* annotations.
*
* @param basePackageClasses the classes to use
* @return the builder for fluent usage
*/
public Builder packages(Class<?>... basePackageClasses) {
Set<String> packages = new HashSet<String>();
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<String, Object> 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;
}
}
}
...@@ -16,10 +16,7 @@ ...@@ -16,10 +16,7 @@
package org.springframework.boot.autoconfigure.orm.jpa; package org.springframework.boot.autoconfigure.orm.jpa;
import java.util.Map;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -27,14 +24,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome; ...@@ -27,14 +24,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 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.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.ConditionContext;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
...@@ -54,44 +47,11 @@ import org.springframework.util.ClassUtils; ...@@ -54,44 +47,11 @@ import org.springframework.util.ClassUtils;
@AutoConfigureAfter(DataSourceAutoConfiguration.class) @AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { 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 @Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() { protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter(); return new HibernateJpaVendorAdapter();
} }
@Override
protected void configure(
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
Map<String, Object> 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 { static class HibernateEntityManagerCondition extends SpringBootCondition {
private static String[] CLASS_NAMES = { private static String[] CLASS_NAMES = {
......
...@@ -30,11 +30,10 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; ...@@ -30,11 +30,10 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
...@@ -42,7 +41,6 @@ import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager; ...@@ -42,7 +41,6 @@ import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter; import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor; import org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter; import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
...@@ -54,19 +52,19 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter ...@@ -54,19 +52,19 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
* @author Dave Syer * @author Dave Syer
* @author Oliver Gierke * @author Oliver Gierke
*/ */
public abstract class JpaBaseConfiguration implements BeanFactoryAware, EnvironmentAware { @EnableConfigurationProperties(JpaProperties.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory; private ConfigurableListableBeanFactory beanFactory;
private RelaxedPropertyResolver environment; @Autowired
private DataSource dataSource;
@Autowired(required = false) @Autowired(required = false)
private PersistenceUnitManager persistenceUnitManager; private PersistenceUnitManager persistenceUnitManager;
@Override @Autowired
public void setEnvironment(Environment environment) { private JpaProperties jpaProperties;
this.environment = new RelaxedPropertyResolver(environment, "spring.jpa.");
}
@Bean @Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class) @ConditionalOnMissingBean(PlatformTransactionManager.class)
...@@ -75,47 +73,35 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware, Environm ...@@ -75,47 +73,35 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware, Environm
} }
@Bean @Bean
@ConditionalOnMissingBean(name = "entityManagerFactory") @ConditionalOnMissingBean
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)
public JpaVendorAdapter jpaVendorAdapter() { public JpaVendorAdapter jpaVendorAdapter() {
AbstractJpaVendorAdapter adapter = createJpaVendorAdapter(); AbstractJpaVendorAdapter adapter = createJpaVendorAdapter();
adapter.setShowSql(this.environment.getProperty("show-sql", Boolean.class, true)); adapter.setShowSql(this.jpaProperties.isShowSql());
adapter.setDatabasePlatform(this.environment.getProperty("database-platform")); adapter.setDatabase(this.jpaProperties.getDatabase());
adapter.setDatabase(this.environment.getProperty("database", Database.class, adapter.setDatabasePlatform(this.jpaProperties.getDatabasePlatform());
Database.DEFAULT)); adapter.setGenerateDdl(this.jpaProperties.isGenerateDdl());
adapter.setGenerateDdl(this.environment.getProperty("generate-ddl",
Boolean.class, false));
return adapter; return adapter;
} }
protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter(); @Bean
@ConditionalOnMissingBean
protected DataSource getDataSource() { public EntityManagerFactoryBuilder entityManagerFactoryBuilder(
try { JpaVendorAdapter jpaVendorAdapter) {
return this.beanFactory.getBean("dataSource", DataSource.class); EntityManagerFactoryBuilder builder = new EntityManagerFactoryBuilder(
} jpaVendorAdapter, this.jpaProperties, this.persistenceUnitManager);
catch (RuntimeException ex) { return builder;
return this.beanFactory.getBean(DataSource.class);
} }
@Bean
@Primary
@ConditionalOnMissingBean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder factory) {
return factory.dataSource(this.dataSource).packages(getPackagesToScan()).build();
} }
protected abstract AbstractJpaVendorAdapter createJpaVendorAdapter();
protected String[] getPackagesToScan() { protected String[] getPackagesToScan() {
List<String> basePackages = AutoConfigurationPackages.get(this.beanFactory); List<String> basePackages = AutoConfigurationPackages.get(this.beanFactory);
return basePackages.toArray(new String[basePackages.size()]); return basePackages.toArray(new String[basePackages.size()]);
......
/*
* 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<String, Object> properties = new HashMap<String, Object>();
private String databasePlatform;
private Database database = Database.DEFAULT;
private boolean generateDdl = false;
private boolean showSql = false;
private Hibernate hibernate = new Hibernate();
public Map<String, Object> getProperties() {
return this.properties;
}
public void setProperties(Map<String, Object> 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<String, Object> 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<String, Object> getAdditionalProperties(Map<String, Object> existing,
DataSource dataSource) {
Map<String, Object> result = new HashMap<String, Object>();
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<String, Object> existing, String key) {
return existing.containsKey("hibernate." + key);
}
private String getDefaultDdlAuto(DataSource dataSource) {
if (EmbeddedDatabaseConnection.isEmbedded(dataSource)) {
return "create-drop";
}
return "none";
}
}
}
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import org.junit.Ignore;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
...@@ -30,7 +31,7 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat ...@@ -30,7 +31,7 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ HibernateJpaAutoConfigurationTests.class, @SuiteClasses({ HibernateJpaAutoConfigurationTests.class,
LiquibaseAutoConfigurationTests.class }) LiquibaseAutoConfigurationTests.class })
// @Ignore @Ignore
public class AdhocTestSuite { public class AdhocTestSuite {
} }
...@@ -94,7 +94,7 @@ public class CommonsDataSourceConfigurationTests { ...@@ -94,7 +94,7 @@ public class CommonsDataSourceConfigurationTests {
@Bean @Bean
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX) @ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
public DataSource dataSource() { public DataSource dataSource() {
return DataSourceFactory.create().type(BasicDataSource.class).build(); return DataSourceBuilder.create().type(BasicDataSource.class).build();
} }
} }
......
...@@ -105,7 +105,7 @@ public class HikariDataSourceConfigurationTests { ...@@ -105,7 +105,7 @@ public class HikariDataSourceConfigurationTests {
@Bean @Bean
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX) @ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
public DataSource dataSource() { public DataSource dataSource() {
return DataSourceFactory.create().type(HikariDataSource.class).build(); return DataSourceBuilder.create().type(HikariDataSource.class).build();
} }
} }
......
...@@ -137,7 +137,7 @@ public class TomcatDataSourceConfigurationTests { ...@@ -137,7 +137,7 @@ public class TomcatDataSourceConfigurationTests {
@Bean @Bean
@ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX) @ConfigurationProperties(prefix = DataSourceAutoConfiguration.CONFIGURATION_PREFIX)
public DataSource dataSource() { public DataSource dataSource() {
return DataSourceFactory.create() return DataSourceBuilder.create()
.type(org.apache.tomcat.jdbc.pool.DataSource.class).build(); .type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
} }
......
...@@ -21,6 +21,7 @@ import org.springframework.boot.test.EnvironmentTestUtils; ...@@ -21,6 +21,7 @@ import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
/** /**
...@@ -39,7 +40,7 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura ...@@ -39,7 +40,7 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura
@Test @Test
public void testCustomNamingStrategy() throws Exception { public void testCustomNamingStrategy() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"spring.jpa.hibernate.namingstrategy:" "spring.jpa.hibernate.namingStrategy:"
+ "org.hibernate.cfg.EJB3NamingStrategy"); + "org.hibernate.cfg.EJB3NamingStrategy");
setupTestConfiguration(); setupTestConfiguration();
this.context.refresh(); this.context.refresh();
...@@ -50,4 +51,20 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura ...@@ -50,4 +51,20 @@ public class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigura
assertThat(actual, equalTo("org.hibernate.cfg.EJB3NamingStrategy")); 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")));
}
} }
...@@ -926,11 +926,54 @@ action. ...@@ -926,11 +926,54 @@ action.
[[howto-configure-a-datasource]] [[howto-configure-a-datasource]]
=== 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 '<<spring-boot-features.adoc#boot-features-configure-datasource>>' in the See '<<spring-boot-features.adoc#boot-features-configure-datasource>>' in the
``Spring Boot features'' section and the ``Spring Boot features'' section and the
{sc-spring-boot-autoconfigure}/jdbc/DataSourceAutoConfiguration.{sc-ext}[`DataSourceAutoConfiguration`] {sc-spring-boot-autoconfigure}/jdbc/DataSourceAutoConfiguration.{sc-ext}[`DataSourceAutoConfiguration`]
class for more details. 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]] [[howto-use-spring-data-repositories]]
...@@ -998,15 +1041,57 @@ and {sc-spring-boot-autoconfigure}/orm/jpa/JpaBaseConfiguration.{sc-ext}[`JpaBas ...@@ -998,15 +1041,57 @@ and {sc-spring-boot-autoconfigure}/orm/jpa/JpaBaseConfiguration.{sc-ext}[`JpaBas
for more details. for more details.
[[howto-use-custom-entity-manager]] [[howto-use-custom-entity-manager]]
=== Use a custom EntityManagerFactory === Use a custom EntityManagerFactory
To take full control of the configuration of the `EntityManagerFactory`, you need to add To take full control of the configuration of the `EntityManagerFactory`, you need to add
a `@Bean` named "entityManagerFactory". To avoid eager initialization of JPA a `@Bean` named "entityManagerFactory". Spring Boot auto-configuration switches off its entity manager
infrastructure, Spring Boot auto-configuration does not switch on its entity manager based on the presence of a bean of that type.
based on the presence of a bean of that type. Instead it has to do it by name.
[[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]] [[howto-use-traditional-persistence-xml]]
=== Use a traditional persistence.xml === Use a traditional persistence.xml
......
...@@ -1392,7 +1392,7 @@ following to your `application.properties`. ...@@ -1392,7 +1392,7 @@ following to your `application.properties`.
NOTE: Hibernate's own internal property name for this (if you happen to remember it 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 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 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. DDL generation.
......
...@@ -127,7 +127,7 @@ ...@@ -127,7 +127,7 @@
<verbose>true</verbose> <verbose>true</verbose>
<dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat> <dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
<generateGitPropertiesFile>true</generateGitPropertiesFile> <generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>src/main/resources/git.properties</generateGitPropertiesFilename> <generateGitPropertiesFilename>${basedir}/src/main/resources/git.properties</generateGitPropertiesFilename>
</configuration> </configuration>
</plugin> </plugin>
<!-- Support our own plugin --> <!-- Support our own plugin -->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment