springboot中使用sql脚本创建数据库失败原因

版本

spring-boot-starter-parent ---- 2.2.6.RELEASE
druid ---- 1.1.10
mysql-connector-java ---- 8.0.19

问题

目录结构
我在sql文件夹下放几个sql文件用于创建数据表
然后启动springboot
结果发现表并没有创建,并且控制台也没用报错
控制台信息数据库

# application.yml配置
spring:
  datasource:
    #   数据源基本配置
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
    type: com.alibaba.druid.pool.DruidDataSource
    #   数据源其他配置
    schema:
      - classpath:sql/department.sql
      - classpath:sql/employee.sql

解决方案

在 application.yml中加入initialization-mode: always即可

spring:
  datasource:
    #   数据源基本配置
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
    type: com.alibaba.druid.pool.DruidDataSource
    #   数据源其他配置
    schema:
      - classpath:sql/department.sql
      - classpath:sql/employee.sql
    # Initialize the datasource with available DDL and DML scripts.
    initialization-mode: always

原因

(由于实力不够,看不懂源码,只能大概看看然后猜一下)
由于springboot在启动时会AutoConfiguration,因此对于jdbc,就需要进入对应的DataSourceAutoConfiguration
文件目录
点开DataSourceAutoConfiguration 注解中import了两个类,其中一个是 DataSourceInitializationConfiguration 顾名思义 数据源初始化配置的类,点进去

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
	...
}

同样 数据源初始化配置的类import了 DataSourceInitializerInvoker(数据源初始化程序调用程序),继续点进

@Configuration(proxyBeanMethods = false)
@Import({ DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class })
class DataSourceInitializationConfiguration {}

这个类中有一个字段,他的数据类型是 DataSourceInitializer ,继续点入

class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean {
	private DataSourceInitializer dataSourceInitializer;
}

在DataSourceInitializer 这个类中,我挑选了受影响的方法

class DataSourceInitializer {
	boolean createSchema() {
		List<Resource> scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
		if (!scripts.isEmpty()) {
			if (!isEnabled()) {
				logger.debug("Initialization disabled (not running DDL scripts)");
				return false;
			}
			String username = this.properties.getSchemaUsername();
			String password = this.properties.getSchemaPassword();
			runScripts(scripts, username, password);
		}
		return !scripts.isEmpty();
	}
	
	private boolean isEnabled() {
		DataSourceInitializationMode mode = this.properties.getInitializationMode();
		if (mode == DataSourceInitializationMode.NEVER) {
			return false;
		}
		if (mode == DataSourceInitializationMode.EMBEDDED && !isEmbedded()) {
			return false;
		}
		return true;
	}
}


DataSourceInitializer

接下来看一下一下DataSourceInitializer 这个类内部


createSchema()这个方法

boolean createSchema() {
	List<Resource> scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
	if (!scripts.isEmpty()) {
		if (!isEnabled()) {
			logger.debug("Initialization disabled (not running DDL scripts)");
			return false;
		}
		String username = this.properties.getSchemaUsername();
		String password = this.properties.getSchemaPassword();
		runScripts(scripts, username, password);
	}
	return !scripts.isEmpty();
}
  • 先说目标方法,要想创建数据的脚本成功执行,需要执行runScripts()方法
  • 这个方法中,首先执行getScripts()方法,内部会调用getResources()获得一系列的脚本资源文件,如果获得成功,说明scripts变量不为空,进入if语句,否则函数返回 false
  • 当进入循环后,会去执行isEnabled()方法,判断数据库的初始化模型,模型的数据一共有三个,分别是always, embedded,enver,只有当模型为always时,才会返回 false,如果返回true,取反,自然不会执行到runScripts()方法
private boolean isEnabled() {
	DataSourceInitializationMode mode = this.properties.getInitializationMode();
	if (mode == DataSourceInitializationMode.NEVER) {
		return false;
	}
	if (mode == DataSourceInitializationMode.EMBEDDED && !isEmbedded()) {
		return false;
	}
	return true;
}
public enum DataSourceInitializationMode {

	/**
	 * Always initialize the datasource.
	 */
	ALWAYS,

	/**
	 * Only initialize an embedded datasource.
	 */
	EMBEDDED,

	/**
	 * Do not initialize the datasource.
	 */
	NEVER

}

我在这个方法上设置了断点,debug运行发现进入了最内层循环,原因是我一开始并没有指定数据库初始化模型,因此初始化模型默认为embedded,从而isEnabled()返回false,取反返回true,导致了目标方法没有执行到
结果
结果
而且debug信息中也说了,Initialization disabled (not running DDL scripts)禁用初始化(不运行DDL脚本)
因此,如果需要运行DDL脚本,需要配置spring.datasource.initialization-mode: always

spring:
  datasource:
    #   数据源基本配置
    username: root
    password: Root123!
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
    type: com.alibaba.druid.pool.DruidDataSource
    #   数据源其他配置
    schema:
      - classpath:sql/department.sql
      - classpath:sql/employee.sql
    # Initialize the datasource with available DDL and DML scripts.
    initialization-mode: always


版权声明:本文为weixin_44344344原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。