溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

啟動Springboot項目后怎么實現自動創建多表關聯的數據庫

發布時間:2021-03-16 15:08:32 來源:億速云 閱讀:640 作者:Leah 欄目:開發技術

這篇文章將為大家詳細講解有關啟動Springboot項目后怎么實現自動創建多表關聯的數據庫,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

sql腳本的語句就是平常的create建表語句,類似如下:

create table ACT_PROCDEF_INFO (
 ID_ varchar(64) not null,
 PROC_DEF_ID_ varchar(64) not null,
 REV_ integer,
 INFO_JSON_ID_ varchar(64),
 primary key (ID_)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;

增加外部主鍵、索引——

create index ACT_IDX_INFO_PROCDEF on ACT_PROCDEF_INFO(PROC_DEF_ID_);

alter table ACT_PROCDEF_INFO
 add constraint ACT_FK_INFO_JSON_BA
 foreign key (INFO_JSON_ID_)
 references ACT_GE_BYTEARRAY (ID_);

alter table ACT_PROCDEF_INFO
 add constraint ACT_FK_INFO_PROCDEF
 foreign key (PROC_DEF_ID_)
 references ACT_RE_PROCDEF (ID_);

alter table ACT_PROCDEF_INFO
 add constraint ACT_UNIQ_INFO_PROCDEF
 unique (PROC_DEF_ID_);

整體就是設計一套符合符合需求場景的sql語句,保存在.sql的腳本文件里,最后統一存放在resource目錄下,類似如下:

啟動Springboot項目后怎么實現自動創建多表關聯的數據庫

接下來,就是實現CommandLineRunner的接口,重寫其run()的bean回調方法,在run方法里開發能自動建庫與建表邏輯的功能。

目前,我已將開發的demo上傳到了我的github,感興趣的童鞋,可自行下載,目前能直接下下來在本地環境運行,可根據自己的實際需求針對性參考使用。

首先,在解決這類需求時,第一個先要解決的地方是,Springboot啟動后如何實現只執行一次建表方法。

這里需要用到一個CommandLineRunner接口,這是Springboot自帶的,實現該接口的類,其重寫的run方法,會在Springboot啟動完成后自動執行,該接口源碼如下:

@FunctionalInterface
public interface CommandLineRunner {

 /**
 *用于運行bean的回調
 */
 void run(String... args) throws Exception;

}

擴展一下,在Springboot中,可以定義多個實現CommandLineRunner接口類,并且可以對這些實現類中進行排序,只需要增加@Order,其重寫的run方法就可以按照順序執行,代碼案例驗證:

@Component
@Order(value=1)
public class WatchStartCommandSqlRunnerImpl implements CommandLineRunner {

 @Override
 public void run(String... args) throws Exception {
 System.out.println("第一個Command執行");
 }


@Component
@Order(value = 2)
public class WatchStartCommandSqlRunnerImpl2 implements CommandLineRunner {
 @Override
 public void run(String... args) throws Exception {
 System.out.println("第二個Command執行");
 }
}

控制臺打印的信息如下:

第一個Command執行
第二個Command執行

根據以上的驗證,因此,我們可以通過實現CommandLineRunner的接口,重寫其run()的bean回調方法,用于在Springboot啟動后實現只執行一次建表方法。實現項目啟動建表的功能,可能還需實現判斷是否已經有相應數據庫,若無,則應先新建一個數據庫,同時,得考慮還沒有對應數據庫的情況,因此,我們通過jdbc第一次連接MySQL時,應連接一個原有自帶存在的庫。每個MySql安裝成功后,都會有一個mysql庫,在第一次建立jdbc連接時,可以先連接它。

啟動Springboot項目后怎么實現自動創建多表關聯的數據庫

代碼如下:

Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://127.0.0.1:3306/mysql?useUnicode=true&characterEncoding=UTF-8&ueSSL=false&serverTimezone=GMT%2B8";
Connection conn= DriverManager.getConnection(url,"root","root");

建立與MySql軟件連接后,先創建一個Statement對象,該對象是jdbc中可用于執行靜態 SQL 語句并返回它所生成結果的對象,這里可以使用它來執行查找庫與創建庫的作用。

//創建Statement對象
 Statement statment=conn.createStatement();
 /**
 使用statment的查詢方法executeQuery("show databases like \"fte\"")
 檢查MySql是否有fte這個數據庫
 **/
 ResultSet resultSet=statment.executeQuery("show databases like \"fte\"");
 //若resultSet.next()為true,證明已存在;
 //若false,證明還沒有該庫,則執行statment.executeUpdate("create database fte")創建庫
 if(resultSet.next()){
 log.info("數據庫已經存在");
 }else {
 log.info("數據庫未存在,先創建fte數據庫");
 if(statment.executeUpdate("create database fte")==1){
 log.info("新建數據庫成功");
 }
 }

在數據庫fte自動創建完成后,就可以在該fte庫里去做建表的操作了。

我將建表的相關方法都封裝到SqlSessionFactory類里,相關建表方法同樣需要用到jdbc的Connection連接到數據庫,因此,需要把已連接的Connection引用變量當做參數傳給SqlSessionFactory的初始構造函數:

public void createTable(Connection conn,Statement stat) throws SQLException {
 try {

  String url="jdbc:mysql://127.0.0.1:3306/fte?useUnicode=true&characterEncoding=UTF-8&ueSSL=false&serverTimezone=GMT%2B8";
  conn=DriverManager.getConnection(url,"root","root");
  SqlSessionFactory sqlSessionFactory=new SqlSessionFactory(conn);
  sqlSessionFactory.schemaOperationsBuild("create");
 } catch (SQLException e) {
  e.printStackTrace();
 }finally {
  stat.close();
  conn.close();
 }
 }

初始化new SqlSessionFactory(conn)后,就可以在該對象里使用已進行連接操作的Connection對象了。

public class SqlSessionFactory{
 private Connection connection ;
 public SqlSessionFactory(Connection connection) {
 this.connection = connection;
 }
......
}

這里傳參可以有兩種情況,即“create”代表創建表結構的功能,“drop”代表刪除表結構的功能:

 sqlSessionFactory.schemaOperationsBuild("create");

進入到這個方法里,會先做一個判斷——

public void schemaOperationsBuild(String type) {
 switch (type){
 case "drop":
  this.dbSchemaDrop();break;
 case "create":
  this.dbSchemaCreate();break;
 }
}

若是this.dbSchemaCreate(),執行建表操作:

/**
 * 新增數據庫表
 */
public void dbSchemaCreate() {

 if (!this.isTablePresent()) {
 log.info("開始執行create操作");
 this.executeResource("create", "act");
 log.info("執行create完成");
 }
}

this.executeResource("create", "act")代表創建表名為act的數據庫表——

public void executeResource(String operation, String component) {
 this.executeSchemaResource(operation, component, this.getDbResource(operation, operation, component), false);
 }

其中 this.getDbResource(operation, operation, component)是獲取sql腳本的路徑,進入到方法里,可見——

public String getDbResource(String directory, String operation, String component) {
 return "static/db/" + directory + "/mysql." + operation + "." + component + ".sql";
 }

接下來,讀取路徑下的sql腳本,生成輸入流字節流:

public void executeSchemaResource(String operation, String component, String resourceName, boolean isOptional) {
 InputStream inputStream = null;

 try {
 //讀取sql腳本數據
 inputStream = IoUtil.getResourceAsStream(resourceName);
 if (inputStream == null) {
  if (!isOptional) {
  log.error("resource '" + resourceName + "' is not available");
  return;
  }
 } else {
  this.executeSchemaResource(operation, component, resourceName, inputStream);
 }
 } finally {
 IoUtil.closeSilently(inputStream);
 }

}

最后,整個執行sql腳本的核心實現在this.executeSchemaResource(operation, component, resourceName, inputStream)方法里——

 /**
 * 執行sql腳本
 * @param operation
 * @param component
 * @param resourceName
 * @param inputStream
 */
 private void executeSchemaResource(String operation, String component, String resourceName, InputStream inputStream) {
 //sql語句拼接字符串
 String sqlStatement = null;
 Object exceptionSqlStatement = null;
 
 try {
  /**
  * 1.jdbc連接mysql數據庫
  */
  Connection connection = this.connection;
 
  Exception exception = null;
  /**
  * 2、分行讀取"static/db/create/mysql.create.act.sql"里的sql腳本數據
  */
  byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
  /**
  * 3.將sql文件里數據分行轉換成字符串,換行的地方,用轉義符“\n”來代替
  */
  String ddlStatements = new String(bytes);
  /**
  * 4.以字符流形式讀取字符串數據
  */
  BufferedReader reader = new BufferedReader(new StringReader(ddlStatements));
  /**
  * 5.根據字符串中的轉義符“\n”分行讀取
  */
  String line = IoUtil.readNextTrimmedLine(reader);
  /**
  * 6.循環讀取的每一行
  */
  for(boolean inOraclePlsqlBlock = false; line != null; line = IoUtil.readNextTrimmedLine(reader)) {
  /**
  * 7.若下一行line還有數據,證明還沒有全部讀取,仍可執行讀取
  */
  if (line.length() > 0) {
   /**
   8.在沒有拼接夠一個完整建表語句時,!line.endsWith(";")會為true,
   即一直循環進行拼接,當遇到";"就跳出該if語句
   **/
   if ((!line.endsWith(";") || inOraclePlsqlBlock) && (!line.startsWith("/") || !inOraclePlsqlBlock)) {
   sqlStatement = this.addSqlStatementPiece(sqlStatement, line);
   } else {
   /**
   9.循環拼接中若遇到符號";",就意味著,已經拼接形成一個完整的sql建表語句,例如
   create table ACT_GE_PROPERTY (
   NAME_ varchar(64),
   VALUE_ varchar(300),
   REV_ integer,
   primary key (NAME_)
   ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin
   這樣,就可以先通過代碼來將該建表語句執行到數據庫中,實現如下:
   **/
   if (inOraclePlsqlBlock) {
    inOraclePlsqlBlock = false;
   } else {
   sqlStatement = this.addSqlStatementPiece(sqlStatement, line.substring(0, line.length() - 1));
   }
   /**
   * 10.將建表語句字符串包裝成Statement對象
   */
   Statement jdbcStatement = connection.createStatement();

   try {
   /**
   * 11.最后,執行建表語句到數據庫中
    */
   log.info("SQL: {}", sqlStatement);
    jdbcStatement.execute(sqlStatement);
   jdbcStatement.close();
   } catch (Exception var27) {
   log.error("problem during schema {}, statement {}", new Object[]{operation, sqlStatement, var27});
   } finally {
   /**
    * 12.到這一步,意味著上一條sql建表語句已經執行結束,
    * 若沒有出現錯誤話,這時已經證明第一個數據庫表結構已經創建完成,
    * 可以開始拼接下一條建表語句,
    */
   sqlStatement = null;
   }
  }
  }
 }

  if (exception != null) {
  throw exception;
  } 
 } catch (Exception var29) {
  log.error("couldn't " + operation + " db schema: " + exceptionSqlStatement, var29);
 }
 }

這部分代碼主要功能是,先用字節流形式讀取sql腳本里的數據,轉換成字符串,其中有換行的地方用轉義符“/n”來代替。接著把字符串轉換成字符流BufferedReader形式讀取,按照“/n”符合來劃分每一行的讀取,循環將讀取的每行字符串進行拼接,當循環到某一行遇到“;”時,就意味著已經拼接成一個完整的create建表語句,類似這樣形式——

create table ACT_PROCDEF_INFO (
 ID_ varchar(64) not null,
 PROC_DEF_ID_ varchar(64) not null,
 REV_ integer,
 INFO_JSON_ID_ varchar(64),
 primary key (ID_)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin;

這時,就可以先將拼接好的create建表字符串,通過 jdbcStatement.execute(sqlStatement)語句來執行入庫了。當執行成功時,該ACT_PROCDEF_INFO表就意味著已經創建成功,接著以BufferedReader字符流形式繼續讀取下一行,進行下一個數據庫表結構的構建。

整個過程大概就是這個邏輯,可以在此基礎上,針對更為復雜的建表結構sql語句進行設計,在項目啟動時,自行執行相應的sql語句,來進行建表。

關于啟動Springboot項目后怎么實現自動創建多表關聯的數據庫就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女