spring-boot版本1中初始化sql语句
application.properties配置:
spring.datasource.initialize=true spring.datasource.schema=classpath:schema-mysql.sql spring.datasource.data=classpath:data-mysql.sql
再在resource目录下添加schema-mysql.sql和data-mysql.sql的文件,schema中主要用于创建表的语句,data中主要存放插入数据及更新操作
现在开始源码分析:
这个方法在DataSourceInitializer
private void runSchemaScripts() {
//获取schema位置并转化为Resource对象
List<Resource> scripts = getScripts(this.properties.getSchema(), "schema");
if (!scripts.isEmpty()) {
//这里执行获取的schema
runScripts(scripts);
try {
this.applicationContext
.publishEvent(new DataSourceInitializedEvent(this.dataSource));
if (!this.initialized) {
//这里执行data.sql文件
runDataScripts();
this.initialized = true;
}
}
catch (IllegalStateException ex) {
}
}
}
从这里可以看出它是先进运行schema中语句再运行data中的内容
private void runScripts(List<Resource> resources) {
if (resources.isEmpty()) {
return;
}
//下面主要是把schema中的resource对象封装为popluator对象
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setContinueOnError(this.properties.isContinueOnError());
populator.setSeparator(this.properties.getSeparator());
if (this.properties.getSqlScriptEncoding() != null) {
populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name());
}
for (Resource resource : resources) {
populator.addScript(resource);
}
//执行sql语句方法
DatabasePopulatorUtils.execute(populator, this.dataSource);
}
public static void execute(DatabasePopulator populator, DataSource dataSource) throws DataAccessException {
try {
//获取数据库连接
Connection connection = DataSourceUtils.getConnection(dataSource);
try {
//执行创建表语句也就是schema中的sql语句
populator.populate(connection);
} finally {
if (connection != null) {
DataSourceUtils.releaseConnection(connection, dataSource);
}
}
} catch (Exception var7) {
}
}
public void populate(Connection connection) throws ScriptException {
//从这里可以看出schema文件可以为多个
Iterator var2 = this.getScripts().iterator();
while(var2.hasNext()) {
Resource script = (Resource)var2.next();
ScriptUtils.executeSqlScript(connection, this.encodeScript(script), this.continueOnError, this.ignoreFailedDrops, this.commentPrefix, this.separator, this.blockCommentStartDelimiter, this.blockCommentEndDelimiter);
}
}
public static void executeSqlScript(Connection connection, EncodedResource resource, boolean continueOnError, boolean ignoreFailedDrops, String commentPrefix, String separator, String blockCommentStartDelimiter, String blockCommentEndDelimiter) throws ScriptException {
try {
long startTime = System.currentTimeMillis();
String script;
try {
//这里获取schema中的内容
script = readScript(resource, commentPrefix, separator);
} catch (IOException var27) {
throw new CannotReadScriptException(resource, var27);
}
//这里获取每句的分割符号
if (separator == null) {
separator = ";";
}
if (!"^^^ END OF SCRIPT ^^^".equals(separator) && !containsSqlScriptDelimiters(script, separator)) {
separator = "\n";
}
//这里是执行切割操作把schema中内容切割成单独的sql语句
List<String> statements = new LinkedList();
splitSqlScript(resource, script, separator, commentPrefix, blockCommentStartDelimiter, blockCommentEndDelimiter, statements);
int stmtNumber = 0;
Statement stmt = connection.createStatement();
try {
Iterator var14 = statements.iterator();
while(var14.hasNext()) {
String statement = (String)var14.next();
++stmtNumber;
try {
//遍历执行每条sql语句(schema中的)
stmt.execute(statement);
int rowsAffected = stmt.getUpdateCount();
if (logger.isDebugEnabled()) {
logger.debug(rowsAffected + " returned as updateCount for SQL: " + statement);
}
} catch (SQLException var28) {
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
if (!continueOnError && (!dropStatement || !ignoreFailedDrops)) {
throw new ScriptStatementFailedException(statement, stmtNumber, resource, var28);
}
}
}
} finally {
try {
stmt.close();
} catch (Throwable var26) {
logger.debug("Could not close JDBC Statement", var26);
}
}
long elapsedTime = System.currentTimeMillis() - startTime;
} catch (Exception var30) {
}
}
看到这里就已经知道schema的只需流程,data执行也类型;
补充:我们可以看下它是如何获取schema路径
private List<Resource> getScripts(String locations, String fallback) {
if (locations == null) {
String platform = this.properties.getPlatform();
locations = "classpath*:" + fallback + "-" + platform + ".sql,";
locations += "classpath*:" + fallback + ".sql";
}
return getResources(locations);
}
这里可以看出如果你配置spring.datasource.schema也就是locations的位置它会直接用你配置东西,如果没有配置它会加载俩个东西一个是看你是否配置spring.datasource.platform如果没配置默认为all和直接使用schema.sql所有如果你的名称为schema.sql不需要指定位置。如果你的为schema-test.sql你也可以只需制定spring.datasource.platform=test
spring-boot版本2如何加载sql
application.properties:
spring.datasource.schema=classpath:schema.sql spring.datasource.data=classpath:data.sql spring.datasource.initialization-mode=alawys
源码分析
public void afterPropertiesSet() {
DataSourceInitializer initializer = getDataSourceInitializer();
if (initializer != null) {
//下面这句是进行schema中的初始化
boolean schemaCreated = this.dataSourceInitializer.createSchema();
if (schemaCreated) {
//下面这句是进行data中的初始化
initialize(initializer);
}
}
}
/**
* Create the schema if necessary.
* @return {@code true} if the schema was created
* @see DataSourceProperties#getSchema()
*/
public boolean createSchema() {
List<Resource> scripts = getScripts("spring.datasource.schema",
this.properties.getSchema(), "schema");
if (!scripts.isEmpty()) {
String username = this.properties.getSchemaUsername();
String password = this.properties.getSchemaPassword();
runScripts(scripts, username, password);
}
return !scripts.isEmpty();
}
private void runScripts(List<Resource> resources, String username, String password) {
if (resources.isEmpty()) {
return;
}
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setContinueOnError(this.properties.isContinueOnError());
populator.setSeparator(this.properties.getSeparator());
if (this.properties.getSqlScriptEncoding() != null) {
populator.setSqlScriptEncoding(this.properties.getSqlScriptEncoding().name());
}
for (Resource resource : resources) {
populator.addScript(resource);
}
DataSource dataSource = this.dataSource;
//这里可以看出如果单独配置schema执行的用户名和密码就会产生一个新的datasource
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
dataSource = DataSourceBuilder.create(this.properties.getClassLoader())
.driverClassName(this.properties.determineDriverClassName())
.url(this.properties.determineUrl()).username(username)
.password(password).build();
}
DatabasePopulatorUtils.execute(populator, dataSource);
}
其他的就会版本1差不多了,到这来我们可以知道那个spring-boot版本2可以单独指定初始化schema和data的账号和密码