mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Ensure ddl-auto=none for non-embedded database
A more thorough check is needed to avoid the false assumption that the DataSource is embedded just because an embedded database is on the classpath. You really have to try and look in the connection metadata, so that's what we now do. Fixes gh-621, fixes gh-373
This commit is contained in:
parent
e4c67d6dd8
commit
97adb5c1b3
@ -16,6 +16,14 @@
|
||||
|
||||
package org.springframework.boot.autoconfigure.jdbc;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.ConnectionCallback;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@ -103,6 +111,46 @@ public enum EmbeddedDatabaseConnection {
|
||||
.equals(DERBY.driverClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to determine if a given data source represents an embedded
|
||||
* database type.
|
||||
*
|
||||
* @param dataSource the data source to interrogate
|
||||
* @return true if the data sourceis one of the embedded types
|
||||
*/
|
||||
public static boolean isEmbedded(DataSource dataSource) {
|
||||
boolean embedded = false;
|
||||
try {
|
||||
embedded = new JdbcTemplate(dataSource)
|
||||
.execute(new ConnectionCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInConnection(Connection con)
|
||||
throws SQLException, DataAccessException {
|
||||
String productName = con.getMetaData()
|
||||
.getDatabaseProductName();
|
||||
if (productName == null) {
|
||||
return false;
|
||||
}
|
||||
productName = productName.toUpperCase();
|
||||
if (productName.contains(H2.name())) {
|
||||
return true;
|
||||
}
|
||||
if (productName.contains(HSQL.name())) {
|
||||
return true;
|
||||
}
|
||||
if (productName.contains(DERBY.name())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (DataAccessException e) {
|
||||
// Could not connect, which means it's not embedded
|
||||
}
|
||||
return embedded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most suitable {@link EmbeddedDatabaseConnection} for the given class
|
||||
* loader.
|
||||
|
@ -19,8 +19,8 @@ package org.springframework.boot.autoconfigure.orm.jpa;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
@ -52,22 +52,14 @@ import org.springframework.util.ClassUtils;
|
||||
EnableTransactionManagement.class, EntityManager.class })
|
||||
@Conditional(HibernateEntityManagerCondition.class)
|
||||
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
|
||||
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration implements
|
||||
BeanClassLoaderAware {
|
||||
public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
private RelaxedPropertyResolver environment;
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
public HibernateJpaAutoConfiguration() {
|
||||
this.environment = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
super.setEnvironment(environment);
|
||||
@ -86,19 +78,18 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration implemen
|
||||
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());
|
||||
String ddlAuto = this.environment.getProperty("ddl-auto",
|
||||
getDefaultDdlAuto(entityManagerFactoryBean.getDataSource()));
|
||||
if (!"none".equals(ddlAuto)) {
|
||||
properties.put("hibernate.hbm2ddl.auto", ddlAuto);
|
||||
}
|
||||
}
|
||||
|
||||
private String getDefaultDdlAuto() {
|
||||
EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection
|
||||
.get(this.classLoader);
|
||||
if (embeddedDatabaseConnection == EmbeddedDatabaseConnection.NONE) {
|
||||
return "none";
|
||||
private String getDefaultDdlAuto(DataSource dataSource) {
|
||||
if (EmbeddedDatabaseConnection.isEmbedded(dataSource)) {
|
||||
return "create-drop";
|
||||
}
|
||||
return "create-drop";
|
||||
return "none";
|
||||
}
|
||||
|
||||
static class HibernateEntityManagerCondition extends SpringBootCondition {
|
||||
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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 org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.test.City;
|
||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link HibernateJpaAutoConfiguration}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class CustomHibernateJpaAutoConfigurationTests {
|
||||
|
||||
protected AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
this.context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultDdlAutoForMySql() throws Exception {
|
||||
// Set up environment so we get a MySQL database but don't require server to be
|
||||
// running...
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.driverClassName:com.mysql.jdbc.Driver",
|
||||
"spring.datasource.url:jdbc:mysql://localhost/nonexistent",
|
||||
"spring.datasource.initialize:false", "spring.jpa.database:MYSQL");
|
||||
this.context.register(TestConfiguration.class, DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
LocalContainerEntityManagerFactoryBean bean = this.context
|
||||
.getBean(LocalContainerEntityManagerFactoryBean.class);
|
||||
String actual = (String) bean.getJpaPropertyMap().get("hibernate.hbm2ddl.auto");
|
||||
// No default (let Hibernate choose)
|
||||
assertThat(actual, equalTo(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultDdlAutoForEmbedded() throws Exception {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.initialize:false");
|
||||
this.context.register(TestConfiguration.class,
|
||||
EmbeddedDataSourceConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
LocalContainerEntityManagerFactoryBean bean = this.context
|
||||
.getBean(LocalContainerEntityManagerFactoryBean.class);
|
||||
String actual = (String) bean.getJpaPropertyMap().get("hibernate.hbm2ddl.auto");
|
||||
assertThat(actual, equalTo("create-drop"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@TestAutoConfigurationPackage(City.class)
|
||||
protected static class TestConfiguration {
|
||||
|
||||
}
|
||||
}
|
@ -935,13 +935,22 @@ 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 autoconfiguration 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.
|
||||
|
||||
|
||||
[[howto-use-traditional-persistence-xml]]
|
||||
=== Use a traditional persistence.xml
|
||||
Spring doesn't require the use of XML to configure the JPA provider, and Spring Boot
|
||||
assumes you want to take advantage of that feature. If you prefer to use `persistence.xml`
|
||||
then you need to define your own `@Bean` of type `LocalEntityManagerFactoryBean`, and set
|
||||
the persistence unit name there.
|
||||
then you need to define your own `@Bean` of type `LocalEntityManagerFactoryBean` (with
|
||||
id "entityManagerFactory", and set the persistence unit name there.
|
||||
|
||||
See
|
||||
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java[`JpaBaseConfiguration`]
|
||||
|
@ -1189,7 +1189,6 @@ See the '<<howto.adoc#howto-separate-entity-definitions-from-spring-configuratio
|
||||
how-to.
|
||||
|
||||
|
||||
|
||||
[[boot-features-spring-data-jpa-repositories]]
|
||||
==== Spring Data JPA Repositories
|
||||
Spring Data JPA repositories are interfaces that you can define to access data. JPA
|
||||
@ -1238,9 +1237,15 @@ following to your `application.properties`.
|
||||
|
||||
[indent=0]
|
||||
----
|
||||
spring.jpa.hibernate.ddl-auto="create-drop"
|
||||
spring.jpa.hibernate.ddl-auto=create-drop
|
||||
----
|
||||
|
||||
Note that 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). Also relevant:
|
||||
`spring.jpa.generate-ddl=false` switches off all DDL generation.
|
||||
|
||||
|
||||
[[boot-features-nosql]]
|
||||
|
Loading…
Reference in New Issue
Block a user