Merge pull request #6543 from vpavic:database-initializers

* pr/6543:
  Polish contribution
  Improve database initializers
This commit is contained in:
Stephane Nicoll 2016-10-10 10:43:29 +02:00
commit d5b2f67b5f
6 changed files with 171 additions and 133 deletions

View File

@ -0,0 +1,88 @@
/*
* Copyright 2012-2016 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;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.util.Assert;
/**
* Base class used for database initialization.
*
* @author Vedran Pavic
* @author Stephane Nicoll
* @since 1.5.0
*/
public abstract class AbstractDatabaseInitializer {
private static final String PLATFORM_PLACEHOLDER = "@@platform@@";
private final DataSource dataSource;
private final ResourceLoader resourceLoader;
protected AbstractDatabaseInitializer(DataSource dataSource, ResourceLoader resourceLoader) {
Assert.notNull(dataSource, "DataSource must not be null");
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.dataSource = dataSource;
this.resourceLoader = resourceLoader;
}
@PostConstruct
protected void initialize() {
if (isEnabled()) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
String schemaLocation = getSchemaLocation();
if (schemaLocation.contains(PLATFORM_PLACEHOLDER)) {
String platform = getDatabaseName();
schemaLocation = schemaLocation.replace(PLATFORM_PLACEHOLDER, platform);
}
populator.addScript(this.resourceLoader.getResource(schemaLocation));
populator.setContinueOnError(true);
DatabasePopulatorUtils.execute(populator, this.dataSource);
}
}
protected abstract boolean isEnabled();
protected abstract String getSchemaLocation();
protected String getDatabaseName() {
try {
String databaseProductName = JdbcUtils.extractDatabaseMetaData(
this.dataSource, "getDatabaseProductName").toString();
databaseProductName = JdbcUtils.commonDatabaseName(databaseProductName);
DatabaseDriver databaseDriver = DatabaseDriver.fromProductName(
databaseProductName);
if (databaseDriver == DatabaseDriver.UNKNOWN) {
throw new IllegalStateException("Unable to detect database type");
}
return databaseDriver.getId();
}
catch (MetaDataAccessException ex) {
throw new IllegalStateException("Unable to detect database type", ex);
}
}
}

View File

@ -40,6 +40,7 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.io.ResourceLoader;
import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -77,8 +78,9 @@ public class BatchAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@ConditionalOnBean(DataSource.class) @ConditionalOnBean(DataSource.class)
public BatchDatabaseInitializer batchDatabaseInitializer() { public BatchDatabaseInitializer batchDatabaseInitializer(DataSource dataSource,
return new BatchDatabaseInitializer(); ResourceLoader resourceLoader) {
return new BatchDatabaseInitializer(dataSource, resourceLoader, this.properties);
} }
@Bean @Bean

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,63 +16,46 @@
package org.springframework.boot.autoconfigure.batch; package org.springframework.boot.autoconfigure.batch;
import javax.annotation.PostConstruct;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.batch.support.DatabaseType; import org.springframework.boot.autoconfigure.AbstractDatabaseInitializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; import org.springframework.util.Assert;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.stereotype.Component;
/** /**
* Initialize the Spring Batch schema (ignoring errors, so should be idempotent). * Initialize the Spring Batch schema (ignoring errors, so should be idempotent).
* *
* @author Dave Syer * @author Dave Syer
* @author Vedran Pavic
*/ */
@Component public class BatchDatabaseInitializer extends AbstractDatabaseInitializer {
public class BatchDatabaseInitializer {
@Autowired private final BatchProperties properties;
private BatchProperties properties;
@Autowired public BatchDatabaseInitializer(DataSource dataSource,
private DataSource dataSource; ResourceLoader resourceLoader, BatchProperties properties) {
super(dataSource, resourceLoader);
@Autowired Assert.notNull(properties, "BatchProperties must not be null");
private ResourceLoader resourceLoader; this.properties = properties;
@PostConstruct
protected void initialize() {
if (this.properties.getInitializer().isEnabled()) {
String platform = getDatabaseType();
if ("hsql".equals(platform)) {
platform = "hsqldb";
}
if ("postgres".equals(platform)) {
platform = "postgresql";
}
if ("oracle".equals(platform)) {
platform = "oracle10g";
}
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
String schemaLocation = this.properties.getSchema();
schemaLocation = schemaLocation.replace("@@platform@@", platform);
populator.addScript(this.resourceLoader.getResource(schemaLocation));
populator.setContinueOnError(true);
DatabasePopulatorUtils.execute(populator, this.dataSource);
}
} }
private String getDatabaseType() { @Override
try { protected boolean isEnabled() {
return DatabaseType.fromMetaData(this.dataSource).toString().toLowerCase(); return this.properties.getInitializer().isEnabled();
} }
catch (MetaDataAccessException ex) {
throw new IllegalStateException("Unable to detect database type", ex); @Override
protected String getSchemaLocation() {
return this.properties.getSchema();
}
@Override
protected String getDatabaseName() {
String databaseName = super.getDatabaseName();
if ("oracle".equals(databaseName)) {
return "oracle10g";
} }
return databaseName;
} }
} }

View File

@ -47,9 +47,9 @@ class JdbcSessionConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public JdbcSessionDatabaseInitializer jdbcSessionDatabaseInitializer( public JdbcSessionDatabaseInitializer jdbcSessionDatabaseInitializer(
SessionProperties properties, DataSource dataSource, DataSource dataSource, ResourceLoader resourceLoader,
ResourceLoader resourceLoader) { SessionProperties properties) {
return new JdbcSessionDatabaseInitializer(properties, dataSource, resourceLoader); return new JdbcSessionDatabaseInitializer(dataSource, resourceLoader, properties);
} }
@Configuration @Configuration

View File

@ -16,18 +16,10 @@
package org.springframework.boot.autoconfigure.session; package org.springframework.boot.autoconfigure.session;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.AbstractDatabaseInitializer;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -36,64 +28,25 @@ import org.springframework.util.Assert;
* @author Vedran Pavic * @author Vedran Pavic
* @since 1.4.0 * @since 1.4.0
*/ */
public class JdbcSessionDatabaseInitializer { public class JdbcSessionDatabaseInitializer extends AbstractDatabaseInitializer {
private static Map<String, String> ALIASES; private final SessionProperties.Jdbc properties;
static { public JdbcSessionDatabaseInitializer(DataSource dataSource,
Map<String, String> aliases = new HashMap<String, String>(); ResourceLoader resourceLoader, SessionProperties properties) {
aliases.put("apache derby", "derby"); super(dataSource, resourceLoader);
aliases.put("hsql database engine", "hsqldb");
aliases.put("microsoft sql server", "sqlserver");
ALIASES = Collections.unmodifiableMap(aliases);
}
private SessionProperties properties;
private DataSource dataSource;
private ResourceLoader resourceLoader;
public JdbcSessionDatabaseInitializer(SessionProperties properties,
DataSource dataSource, ResourceLoader resourceLoader) {
Assert.notNull(properties, "SessionProperties must not be null"); Assert.notNull(properties, "SessionProperties must not be null");
Assert.notNull(dataSource, "DataSource must not be null"); this.properties = properties.getJdbc();
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.properties = properties;
this.dataSource = dataSource;
this.resourceLoader = resourceLoader;
} }
@PostConstruct @Override
protected void initialize() { protected boolean isEnabled() {
if (this.properties.getJdbc().getInitializer().isEnabled()) { return this.properties.getInitializer().isEnabled();
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
String schemaLocation = this.properties.getJdbc().getSchema();
schemaLocation = schemaLocation.replace("@@platform@@", getPlatform());
populator.addScript(this.resourceLoader.getResource(schemaLocation));
populator.setContinueOnError(true);
DatabasePopulatorUtils.execute(populator, this.dataSource);
}
} }
private String getPlatform() { @Override
String databaseName = getDatabaseName(); protected String getSchemaLocation() {
if (ALIASES.containsKey(databaseName)) { return this.properties.getSchema();
return ALIASES.get(databaseName);
}
return databaseName;
}
private String getDatabaseName() {
try {
String databaseProductName = JdbcUtils
.extractDatabaseMetaData(this.dataSource, "getDatabaseProductName")
.toString();
return JdbcUtils.commonDatabaseName(databaseProductName).toLowerCase();
}
catch (MetaDataAccessException ex) {
throw new IllegalStateException("Unable to detect database type", ex);
}
} }
} }

View File

@ -33,77 +33,77 @@ public enum DatabaseDriver {
/** /**
* Unknown type. * Unknown type.
*/ */
UNKNOWN(null, null), UNKNOWN("unknown", null, null),
/** /**
* Apache Derby. * Apache Derby.
*/ */
DERBY("Apache Derby", "org.apache.derby.jdbc.EmbeddedDriver", DERBY("derby", "Apache Derby", "org.apache.derby.jdbc.EmbeddedDriver",
"org.apache.derby.jdbc.EmbeddedXADataSource", "org.apache.derby.jdbc.EmbeddedXADataSource",
"SELECT 1 FROM SYSIBM.SYSDUMMY1"), "SELECT 1 FROM SYSIBM.SYSDUMMY1"),
/** /**
* H2. * H2.
*/ */
H2("H2", "org.h2.Driver", "org.h2.jdbcx.JdbcDataSource", "SELECT 1"), H2("h2", "H2", "org.h2.Driver", "org.h2.jdbcx.JdbcDataSource", "SELECT 1"),
/** /**
* HyperSQL DataBase. * HyperSQL DataBase.
*/ */
HSQLDB("HSQL Database Engine", "org.hsqldb.jdbc.JDBCDriver", HSQLDB("hsqldb", "HSQL Database Engine", "org.hsqldb.jdbc.JDBCDriver",
"org.hsqldb.jdbc.pool.JDBCXADataSource", "org.hsqldb.jdbc.pool.JDBCXADataSource",
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"), "SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"),
/** /**
* SQL Lite. * SQL Lite.
*/ */
SQLITE("SQLite", "org.sqlite.JDBC"), SQLITE("sqlite", "SQLite", "org.sqlite.JDBC"),
/** /**
* MySQL. * MySQL.
*/ */
MYSQL("MySQL", "com.mysql.jdbc.Driver", MYSQL("mysql", "MySQL", "com.mysql.jdbc.Driver",
"com.mysql.jdbc.jdbc2.optional.MysqlXADataSource", "SELECT 1"), "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource", "SELECT 1"),
/** /**
* Maria DB. * Maria DB.
*/ */
MARIADB("MySQL", "org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MariaDbDataSource", MARIADB("maridb", "MySQL", "org.mariadb.jdbc.Driver",
"SELECT 1"), "org.mariadb.jdbc.MariaDbDataSource", "SELECT 1"),
/** /**
* Google App Engine. * Google App Engine.
*/ */
GAE(null, "com.google.appengine.api.rdbms.AppEngineDriver"), GAE("gae", null, "com.google.appengine.api.rdbms.AppEngineDriver"),
/** /**
* Oracle. * Oracle.
*/ */
ORACLE("Oracle", "oracle.jdbc.OracleDriver", ORACLE("oracle", "Oracle", "oracle.jdbc.OracleDriver",
"oracle.jdbc.xa.client.OracleXADataSource", "SELECT 'Hello' from DUAL"), "oracle.jdbc.xa.client.OracleXADataSource", "SELECT 'Hello' from DUAL"),
/** /**
* Postgres. * Postgres.
*/ */
POSTGRESQL("PostgreSQL", "org.postgresql.Driver", "org.postgresql.xa.PGXADataSource", POSTGRESQL("postgresql", "PostgreSQL", "org.postgresql.Driver",
"SELECT 1"), "org.postgresql.xa.PGXADataSource", "SELECT 1"),
/** /**
* jTDS. As it can be used for several databases, there isn't a single product name we * jTDS. As it can be used for several databases, there isn't a single product name we
* could rely on. * could rely on.
*/ */
JTDS(null, "net.sourceforge.jtds.jdbc.Driver"), JTDS("jtds", null, "net.sourceforge.jtds.jdbc.Driver"),
/** /**
* SQL Server. * SQL Server.
*/ */
SQLSERVER("SQL SERVER", "com.microsoft.sqlserver.jdbc.SQLServerDriver", SQLSERVER("sqlserver", "SQL SERVER", "com.microsoft.sqlserver.jdbc.SQLServerDriver",
"com.microsoft.sqlserver.jdbc.SQLServerXADataSource", "SELECT 1"), "com.microsoft.sqlserver.jdbc.SQLServerXADataSource", "SELECT 1"),
/** /**
* Firebird. * Firebird.
*/ */
FIREBIRD("Firebird", "org.firebirdsql.jdbc.FBDriver", FIREBIRD("firebird", "Firebird", "org.firebirdsql.jdbc.FBDriver",
"org.firebirdsql.pool.FBConnectionPoolDataSource", "org.firebirdsql.pool.FBConnectionPoolDataSource",
"SELECT 1 FROM RDB$DATABASE") { "SELECT 1 FROM RDB$DATABASE") {
@ -117,7 +117,7 @@ public enum DatabaseDriver {
/** /**
* DB2 Server. * DB2 Server.
*/ */
DB2("DB2", "com.ibm.db2.jcc.DB2Driver", "com.ibm.db2.jcc.DB2XADataSource", DB2("db2", "DB2", "com.ibm.db2.jcc.DB2Driver", "com.ibm.db2.jcc.DB2XADataSource",
"SELECT 1 FROM SYSIBM.SYSDUMMY1") { "SELECT 1 FROM SYSIBM.SYSDUMMY1") {
@Override @Override
@ -130,7 +130,7 @@ public enum DatabaseDriver {
/** /**
* DB2 AS400 Server. * DB2 AS400 Server.
*/ */
DB2_AS400("DB2 UDB for AS/400", "com.ibm.as400.access.AS400JDBCDriver", DB2_AS400("db2", "DB2 UDB for AS/400", "com.ibm.as400.access.AS400JDBCDriver",
"com.ibm.as400.access.AS400JDBCXADataSource", "com.ibm.as400.access.AS400JDBCXADataSource",
"SELECT 1 FROM SYSIBM.SYSDUMMY1") { "SELECT 1 FROM SYSIBM.SYSDUMMY1") {
@ -144,14 +144,16 @@ public enum DatabaseDriver {
/** /**
* Teradata. * Teradata.
*/ */
TERADATA("Teradata", "com.teradata.jdbc.TeraDriver"), TERADATA("teradata", "Teradata", "com.teradata.jdbc.TeraDriver"),
/** /**
* Informix. * Informix.
*/ */
INFORMIX("Informix Dynamic Server", "com.informix.jdbc.IfxDriver", null, INFORMIX("informix", "Informix Dynamic Server", "com.informix.jdbc.IfxDriver", null,
"select count(*) from systables"); "select count(*) from systables");
private final String id;
private final String productName; private final String productName;
private final String driverClassName; private final String driverClassName;
@ -160,22 +162,32 @@ public enum DatabaseDriver {
private final String validationQuery; private final String validationQuery;
DatabaseDriver(String name, String driverClassName) { DatabaseDriver(String id, String name, String driverClassName) {
this(name, driverClassName, null); this(id, name, driverClassName, null);
} }
DatabaseDriver(String name, String driverClassName, String xaDataSourceClassName) { DatabaseDriver(String id, String name, String driverClassName,
this(name, driverClassName, xaDataSourceClassName, null); String xaDataSourceClassName) {
this(id, name, driverClassName, xaDataSourceClassName, null);
} }
DatabaseDriver(String productName, String driverClassName, DatabaseDriver(String id, String productName, String driverClassName,
String xaDataSourceClassName, String validationQuery) { String xaDataSourceClassName, String validationQuery) {
this.id = id;
this.productName = productName; this.productName = productName;
this.driverClassName = driverClassName; this.driverClassName = driverClassName;
this.xaDataSourceClassName = xaDataSourceClassName; this.xaDataSourceClassName = xaDataSourceClassName;
this.validationQuery = validationQuery; this.validationQuery = validationQuery;
} }
/**
* Return the identifier of this driver.
* @return the identifier
*/
public String getId() {
return this.id;
}
protected boolean matchProductName(String productName) { protected boolean matchProductName(String productName) {
return this.productName != null && this.productName.equalsIgnoreCase(productName); return this.productName != null && this.productName.equalsIgnoreCase(productName);
} }