spring boot 項(xiàng)目在啟動(dòng)時(shí)執(zhí)行指定sql文件

提醒:閱讀本文需要具備一定的spring boot項(xiàng)目開(kāi)發(fā)經(jīng)驗(yàn)

1. 啟動(dòng)時(shí)執(zhí)行

當(dāng)有在項(xiàng)目啟動(dòng)時(shí)先執(zhí)行指定的sql語(yǔ)句的需求時(shí),可以在resources文件夾下添加需要執(zhí)行的sql文件,文件中的sql語(yǔ)句可以是DDL腳本或DML腳本,然后在配置加入相應(yīng)的配置即可,如下:

spring:
  datasource:
    schema: classpath:schema.sql # schema.sql中一般存放的是DDL腳本,即通常為創(chuàng)建或更新庫(kù)表的腳本
    data: classpath:data.sql # data.sql中一般是DML腳本,即通常為數(shù)據(jù)插入腳本

2. 執(zhí)行多個(gè)sql文件

spring.datasource.schemaspring.datasource.data都是支持接收一個(gè)列表,所以當(dāng)需要執(zhí)行多個(gè)sql文件時(shí),可以使用如下配置:

spring:
  datasource:
    schema: classpath:schema_1.sql, classpath:schema_2.sql
    data: classpath:data_1.sql, classpath:data_2.sql

或

spring:
  datasource:
    schema: 
      - classpath:schema_1.sql
      - classpath:schema_2.sql
    data: 
      - classpath:data_1.sql
      - classpath:data_2.sql

3. 不同運(yùn)行環(huán)境執(zhí)行不同腳本

一般情況下,都會(huì)有多個(gè)運(yùn)行環(huán)境,比如開(kāi)發(fā)、測(cè)試、生產(chǎn)等。而不同運(yùn)行環(huán)境通常需要執(zhí)行的sql會(huì)有所不同。為解決這個(gè)問(wèn)題,可以使用通配符來(lái)實(shí)現(xiàn)。

創(chuàng)建不同環(huán)境的文件夾

在resources文件夾創(chuàng)建不同環(huán)境對(duì)應(yīng)的文件夾,如dev/、sit/、prod/。

配置

application.yml

spring:
  datasource:
    schema: classpath:${spring.profiles.active:dev}/schema.sql 
    data: classpath:${spring.profiles.active:dev}/data.sql

注:${}通配符支持缺省值。如上面的配置中的${spring.profiles.active:dev},其中分號(hào)前是取屬性spring.profiles.active的值,而當(dāng)該屬性的值不存在,則使用分號(hào)后面的值,即dev。

bootstrap.yml

spring:
  profiles:
    active: dev # dev/sit/prod等。分別對(duì)應(yīng)開(kāi)發(fā)、測(cè)試、生產(chǎn)等不同運(yùn)行環(huán)境。

提醒:spring.profiles.active屬性一般在bootstrap.ymlbootstrap.properties中配置。

4. 支持不同數(shù)據(jù)庫(kù)

因?yàn)椴煌瑪?shù)據(jù)庫(kù)的語(yǔ)法有所差異,所以要實(shí)現(xiàn)同樣的功能,不同數(shù)據(jù)庫(kù)的sql語(yǔ)句可能會(huì)不一樣,所以可能會(huì)有多份不同的sql文件。當(dāng)需要支持不同數(shù)據(jù)庫(kù)時(shí),可以使用如下配置:

spring:
  datasource:
    schema: classpath:${spring.profiles.active:dev}/schema-${spring.datasource.platform}.sql
    data: classpath:${spring.profiles.active:dev}/data-${spring.datasource.platform}.sql
    platform: mysql

提醒:platform屬性的默認(rèn)值是'all',所以當(dāng)有在不同數(shù)據(jù)庫(kù)切換的情況下才使用如上配置,因?yàn)槟J(rèn)值的情況下,spring boot會(huì)自動(dòng)檢測(cè)當(dāng)前使用的數(shù)據(jù)庫(kù)。

注:此時(shí),以dev允許環(huán)境為例,resources/dev/文件夾下必須存在如下文件:schema-mysql.sqldata-mysql.sql。

5. 避坑

5.1 坑

當(dāng)在執(zhí)行的sql文件中存在存儲(chǔ)過(guò)程或函數(shù)時(shí),在啟動(dòng)項(xiàng)目時(shí)會(huì)報(bào)錯(cuò)。

比如現(xiàn)在有這樣的需求:項(xiàng)目啟動(dòng)時(shí),掃描某張表,當(dāng)表記錄數(shù)為0時(shí),插入多條記錄;大于0時(shí),跳過(guò)。

schema.sql文件腳本如下:

-- 當(dāng)存儲(chǔ)過(guò)程`p1`存在時(shí),刪除。
drop procedure if exists p1;

-- 創(chuàng)建存儲(chǔ)過(guò)程`p1`
create procedure p1() 
begin
  declare row_num int;
  select count(*) into row_num from `t_user`;
  if row_num = 0 then
    INSERT INTO `t_user`(`username`, `password`) VALUES ('zhangsan', '123456');
  end if;
end;

-- 調(diào)用存儲(chǔ)過(guò)程`p1`
call p1();
drop procedure if exists p1;

啟動(dòng)項(xiàng)目,報(bào)錯(cuò),原因如下:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'create procedure p1() begin declare row_num int' at line 1

大致的意思是:'create procedure p1() begin declare row_num int'這一句出現(xiàn)語(yǔ)法錯(cuò)誤。剛看到這一句,我一開(kāi)始是懵逼的,嚇得我趕緊去比對(duì)mysql存儲(chǔ)過(guò)程的寫(xiě)法,對(duì)了好久都發(fā)現(xiàn)沒(méi)錯(cuò),最后看到一篇講解spring boot配置啟動(dòng)時(shí)執(zhí)行sql腳本的文章,發(fā)現(xiàn)其中多了一項(xiàng)配置:spring.datasource.separator=$$。然后看源碼發(fā)現(xiàn),spring boot在解析sql腳本時(shí),默認(rèn)是以';'作為斷句的分隔符的。看到這里,不難看出報(bào)錯(cuò)的原因,即:spring boot'create procedure p1() begin declare row_num int'當(dāng)成是一條普通的sql語(yǔ)句。而我們需要的是創(chuàng)建一個(gè)存儲(chǔ)過(guò)程。

5.2 解決方案

修改sql腳本的斷句分隔符。如:spring.datasource.separator=$$。然后把腳本改成:

-- 當(dāng)存儲(chǔ)過(guò)程`p1`存在時(shí),刪除。
drop procedure if exists p1;$$

-- 創(chuàng)建存儲(chǔ)過(guò)程`p1`
create procedure p1() 
begin
  declare row_num int;
  select count(*) into row_num from `t_user`;
  if row_num = 0 then
    INSERT INTO `t_user`(`username`, `password`) VALUES ('zhangsan', '123456');
  end if;
end;$$

-- 調(diào)用存儲(chǔ)過(guò)程`p1`
call p1();$$
drop procedure if exists p1;$$

5.3 不足

因?yàn)?code>sql腳本的斷句分隔符從';'變成'$$',所以可能需要在DDL、DML語(yǔ)句的';'后加'$$',不然可能會(huì)出現(xiàn)將整個(gè)腳本當(dāng)成一條sql語(yǔ)句來(lái)執(zhí)行的情況。比如:

-- DDL
CREATE TABLE `table_name` (
  -- 字段定義
  ... 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;$$

-- DML
INSERT INTO `table_name` VALUE(...);$$

結(jié)語(yǔ)

以上,均為個(gè)人測(cè)試后得出的結(jié)論,若有出錯(cuò)的地方,歡迎指出。另外,若有更好配置方式或解決方案,還請(qǐng)不吝指教。

完?。?!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容