在小項目中,給用戶部署環境比較煩人, 數據庫創建、導入能省則省。 設計初衷:
1.用戶安裝數據庫后系統自動創建數據庫。
2.數據庫自動導入。
3.數據庫創建完成后,數據庫連接池保持可用。
A.先來熟悉一下springboot 中datasource.schema配置:
# 數據庫配置 #spring.datasource.driver-class-name=org.sqlite.JDBC ##jdbc:mysql://172.16.102.85:3306/version_manage?characterEncoding=utf8&useSSL=false #spring.datasource.url=jdbc:sqlite:Server/sqlite/sql.db #spring.datasource.username= #spring.datasource.password= #spring.datasource.initialization-mode= always ##spring.datasource.schema= classpath:sql/schema.sql #啟動執行腳本
這個配置是項目啟動后自動執行schema.sql ,增量數據變動比較好 ,不適合項目初始化用。
B. springboot中自己編寫實現過程,具體邏輯實現為:
加載Datasource之前檢查數據庫DB1是否可用 。
如果DB1不存在則判斷數據庫連接下是否有mysql數據庫
存在mysql庫就連接mysql,用mysql庫新建系統需要的數據庫DB1
創建完數據庫DB1后執行init.sql ,初始化數據庫中的表。
全部完成后初始化DataSource bean
DB1的配置文件jdbc.properties :有需求的話可以添加一個操作界面,項目啟動后讓用戶自己頁面操作后寫入文件
dbtype=mysql ip=127.0.0.1 port=3306 dbname=DB1 username=root password=123456 init=init.sql
@Configuration主要業務邏輯,此處只處理簡單的mysql,sqlite兩種數據庫
package cn.sigutech.DataBase.config; import cn.sigutech.DataBase.Mysql.*; import cn.sigutech.DataBase.MysqlRstData; import cn.sigutech.DataBase.Utils.PropertiesUtils; import cn.sigutech.utils.Utils; import com.alibaba.fastjson.JSONObject; import org.apache.commons.dbcp.BasicDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration public class MysqlConfig { static final Logger logger = LoggerFactory.getLogger(MysqlConfig.class); //數據庫正常標志 public static boolean dbConnectFlag = false ; public static JSONObject mysqlPro ; static { String basePath = System.getProperty("user.dir") ; String dirName = basePath + "/conf/jdbc.properties"; mysqlPro = PropertiesUtils.parse(dirName); } @Bean public DataSource createDatesource() { if(mysqlPro.containsKey("dbtype") ){ if("mysql".equalsIgnoreCase(mysqlPro.get("dbtype").toString())){ if(!mysqlPro.containsKey("ip") || !mysqlPro.containsKey("dbname") ||!mysqlPro.containsKey("username") ||!mysqlPro.containsKey("password") ){ return null ; } IDBcontrolled mc = createIDBcontrolled(); MysqlRstData mysqlRstData = mc.testConn(mysqlPro) ;
//數據庫存在,不存在dbname的庫 if( mysqlRstData.getCode() .equals(MysqlRstData.ResultCode.NO_DB)){ if(!mysqlPro.containsKey("init")){ return null ; } mysqlRstData =mc.createDB(mysqlPro); if( mysqlRstData.getCode() .equals(MysqlRstData.ResultCode.SUCCESS) ){ mysqlRstData = mc.importDB(mysqlPro); } } if( !mysqlRstData.getCode() .equals(MysqlRstData.ResultCode.SUCCESS)){ return null ; } BasicDataSource dataSource = new BasicDataSource(); dataSource.setUrl( String.format(MysqlDBUtil.baseurl,mysqlPro.getString("ip"), mysqlPro.getString("port"),mysqlPro.getString("dbname")) ); dataSource.setDriverClassName(MysqlDBUtil.driver); dataSource.setUsername(mysqlPro.getString("username") ); dataSource.setPassword(mysqlPro.getString("password") ); dataSource.setRemoveAbandoned(false); dataSource.setInitialSize(Utils.getInt(mysqlPro.getString("initialSize"), 5));//初始化的連接數 dataSource.setMaxActive(Utils.getInt(mysqlPro.getString("maxActive"), 10));//最大連接數量 dataSource.setMaxIdle(Utils.getInt(mysqlPro.getString("maxIdle"), 10));//最大空閑數 dataSource.setMinIdle(Utils.getInt(mysqlPro.getString("minIdle"), 5));//最小空閑 dataSource.setMaxWait(Utils.getInt(mysqlPro.getString("maxWait"), 3000)); dataSource.setRemoveAbandoned(Utils.getBoolean(mysqlPro.getString("removeAbandoned"))); dataSource.setRemoveAbandonedTimeout(Utils.getInt(mysqlPro.getString("removeAbandonedTimeout"), 180)); dataSource.setValidationQuery(mysqlPro.getString("validationQuery")); dataSource.setTestOnBorrow(Utils.getBoolean(mysqlPro.getString("testOnBorrow"), true)); dataSource.setLogAbandoned(Utils.getBoolean(mysqlPro.getString("logAbandoned"), true)); this.dbConnectFlag = true ; return dataSource; } } return null ; }
//識別數據庫,各數據庫特殊類型字段轉換函數不一樣,返回各數據庫特殊字段處理類! @Bean("IDbColumnConvert") public IDbColumnConvert createIDbColumnConvert(){ if(mysqlPro.containsKey("dbtype") ) { if ("mysql".equalsIgnoreCase(mysqlPro.get("dbtype").toString())) { return new MysqlDbColumnConvert(); } else { return null ; } }else{ return null ; } } //不同數據庫不同的實現代碼 public IDBcontrolled createIDBcontrolled(){ if(mysqlPro.containsKey("dbtype") ) { if ("mysql".equalsIgnoreCase(mysqlPro.get("dbtype").toString())) { return new MysqlController(); } else { return null ; } }else{ return null ; } } }
接口就不寫, 直接實現代碼
package cn.sigutech.DataBase.Mysql; import cn.sigutech.DataBase.MysqlRstData; import com.alibaba.fastjson.JSONObject; public class MysqlController implements IDBcontrolled{ /** * 測試db是否可用 * @param database * @return */ public MysqlRstData testConn(JSONObject database ) { String ip = database.getString("ip"); String port = database.getString("port"); String dbname = database.getString("dbname"); String username = database.getString("username"); String password = database.getString("password"); MysqlRstData mysqlRstData = MysqlDBUtil.testMysqlConn(ip,port,dbname,username,password); return mysqlRstData ; } /** * 根據參數新建數據庫 ,並腳本導入 * @param database * @return */ public MysqlRstData createConnect(JSONObject database ) { String ip = database.getString("ip"); String port = database.getString("port"); String dbname = database.getString("dbname"); String username = database.getString("username"); String password = database.getString("password"); MysqlRstData mysqlRstData = MysqlDBUtil.testMysqlConn(ip,port,dbname,username,password); if(mysqlRstData.getCode() .equals(MysqlRstData.ResultCode.NO_DB)){ mysqlRstData = createDB(database); if( mysqlRstData.getCode() .equals(MysqlRstData.ResultCode.SUCCESS) ){ mysqlRstData = importDB(database); } } return mysqlRstData ; } /** * 新建數據庫 * @param database * @return */ public MysqlRstData createDB(JSONObject database) { String ip = database.getString("ip"); String port = database.getString("port"); String dbname = database.getString("dbname"); String username = database.getString("username"); String password = database.getString("password"); MysqlRstData mysqlRstData = MysqlDBUtil.creatDB(ip,port,dbname,username,password); return mysqlRstData ; } /** * 導入數據 * @param database * @return */ public MysqlRstData importDB(JSONObject database) { String ip = database.getString("ip"); String port = database.getString("port"); String dbname = database.getString("dbname"); String username = database.getString("username"); String password = database.getString("password"); String init = database.getString("init"); MysqlRstData mysqlRstData = MysqlDBUtil.importDB(ip,port,dbname,username,password ,init); return mysqlRstData ; } }
package cn.sigutech.DataBase.Mysql; import cn.sigutech.DataBase.MysqlRstData; import cn.sigutech.utils.ExceptionUtil; import cn.sigutech.utils.FileUtil; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; public class MysqlDBUtil { static final Logger logger = LoggerFactory.getLogger(MysqlDBUtil.class); static String mysqlDriver = "com.mysql.jdbc.Driver"; public static String baseurl = "jdbc:mysql://%s:%s/%s?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true"; public static String driver ="com.mysql.jdbc.Driver"; /** * 測試數據庫是否可以連接 * @param ip * @param port * @param dbname * @param username * @param password * @return */ public static MysqlRstData testMysqlConn(String ip, String port , String dbname, String username, String password ) { String url = String.format(baseurl,ip,port,dbname); Connection conn = getMysqlConn( url, username, password ); if (conn != null) { try { conn.close(); } catch (SQLException e) { logger.error(ExceptionUtil.getMessage((Exception) e) ); } return new MysqlRstData(MysqlRstData.ResultCode.SUCCESS, "") ; }else { String newUrl = String.format(baseurl,ip,port,"mysql"); Connection newConn = getMysqlConn( newUrl, username, password ); if (newConn != null) { try { newConn.close(); } catch (SQLException e) { logger.error(ExceptionUtil.getMessage((Exception) e) ); } return new MysqlRstData(MysqlRstData.ResultCode.NO_DB, "未找到名稱為【" +dbname+"】的數據庫"); }else { return new MysqlRstData(MysqlRstData.ResultCode.NO_CONNECT, "后台無法連接到ip為:【"+ip+"】,port為:【"+port+"】的數據庫"); } } } /** * 獲取數據庫連接,調用完成后需要關閉 * @param ip * @param port * @param dbname * @param username * @param password * @return */ public static Connection getMysqlConn(String ip,String port , String dbname,String username,String password ) { String url = String.format(baseurl,ip,port,dbname); Connection conn = getMysqlConn( url, username, password ); return conn; } /** * 獲取數據庫連接,調用完成后需要關閉 * @param url * @param username * @param password * @return */ public static Connection getMysqlConn(String url,String username,String password ) { Connection conn = null; Connection newConn = null; try { Class.forName(mysqlDriver); } catch (ClassNotFoundException e) { logger.error(ExceptionUtil.getMessage((Exception) e) ); return null; } try { conn = DriverManager.getConnection(url, username, password); if (conn != null) { return conn ; }else{ return null; } } catch (SQLException e ) { if (e.getLocalizedMessage().contains("Unknown database")) { logger.error(ExceptionUtil.getMessage((Exception) e) ); //連接成功 : 數據庫不存在,將自動創建 return null ; } else { logger.error(ExceptionUtil.getMessage((Exception) e) ); //連接錯誤 return null ; } } } /** * 登錄mysql后,建立目標數據庫 * @param ip * @param port * @param dbname * @param username * @param password * @return */ public static MysqlRstData creatDB(String ip, String port , String dbname, String username, String password) { String databaseSql = "create database " + dbname + " default character set utf8 COLLATE utf8_general_ci "; Connection conn = null; MysqlRstData mysqlRstData = new MysqlRstData( ); try { conn = getMysqlConn(ip, port, "mysql", username, password); Statement smt = conn.createStatement(); smt.executeUpdate(databaseSql); return mysqlRstData ; }catch (SQLException e){ logger.error(ExceptionUtil.getMessage((Exception) e) ); mysqlRstData.setCode(MysqlRstData.ResultCode.CREATE_ERROR); mysqlRstData.setMsg(e.toString()); return mysqlRstData ; }finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { logger.error(ExceptionUtil.getMessage((Exception) e) ); e.printStackTrace(); } } } } /** * 將mysql 配置文件寫到jdbc.properties中 * @param ip * @param port * @param dbname * @param username * @param password * @return */ // public static void writeJDBCProperties(String ip, String port , String dbname, String username, String password) { // String url = String.format(baseurl,ip,port,dbname) ; // String basePath = System.getProperty("user.dir") ; // String dirName = basePath + "/conf/"; // FileUtil.pkgDirName(dirName); // StringBuffer content = new StringBuffer(); // content.append("jdbc.driver=").append("com.mysql.jdbc.Driver"); // content.append("\r\n"); // content.append("jdbc.url=" ).append(url); // content.append("\r\n"); // content.append("jdbc.user=").append(username); // content.append("\r\n"); // content.append("jdbc.password=").append(password); // content.append("\r\n"); // FileUtil.fileWriter(dirName , "jdbc.properties" , content ); // // } /** * 獲取數據庫腳本文件 * @return * @throws IOException */ public static String[] getDbInitScript(String init) throws IOException { // File script = ResourceUtils.getFile("classpath:sql/init.sql"); String basePath = System.getProperty("user.dir") ; String fileName = basePath +"/" + "/conf/" + init ; File script = new File(fileName); List<?> LS = FileUtils.readLines(script, "utf-8"); List<String> SQLS = new ArrayList<>(); StringBuilder SQL = new StringBuilder(); boolean ignoreTerms = false; for (Object L : LS) { String L2 = L.toString().trim(); // NOTE double 字段也不支持 boolean H2Unsupported = L2.startsWith("fulltext"); // Ignore comments and line of blank if (StringUtils.isEmpty(L2) || L2.startsWith("--") || H2Unsupported) { continue; } if (L2.startsWith("/*") || L2.endsWith("*/")) { ignoreTerms = L2.startsWith("/*"); continue; } else if (ignoreTerms) { continue; } SQL.append(L2); if (L2.endsWith(";")) { // SQL ends SQLS.add(SQL.toString().replace(",\n)Engine=", "\n)Engine=")); System.out.println(SQL); SQL = new StringBuilder(); } else { SQL.append('\n'); } } return SQLS.toArray(new String[0]); } /** * 導入數據庫 * @param ip * @param port * @param dbname * @param username * @param password * @param init * @return */ public static MysqlRstData importDB(String ip, String port , String dbname, String username, String password ,String init) { //寫配置文件 Connection conn = null ; Statement stmt = null; MysqlRstData mysqlRstData = new MysqlRstData(); try { conn = getMysqlConn(ip, port, dbname, username, password); stmt = conn.createStatement(); for (String sql : getDbInitScript(init)) { try { logger.info(sql); stmt.execute(sql); }catch (Exception e){ logger.error( ExceptionUtil.getMessage( e )); } } }catch (SQLException e){ logger.error( ExceptionUtil.getMessage( e )); mysqlRstData.setCode(MysqlRstData.ResultCode.IMPORT_ERROR); mysqlRstData.setMsg(e.toString()); return mysqlRstData; } catch (IOException e) { logger.error( ExceptionUtil.getMessage( e )); mysqlRstData.setCode(MysqlRstData.ResultCode.INITFILE_ERROR); mysqlRstData.setMsg(e.toString()); return mysqlRstData; }finally { if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } return mysqlRstData; } }
讀取Properties工具代碼,其它簡單工具代碼自己整理
package cn.sigutech.DataBase.Utils; import com.alibaba.fastjson.JSONObject; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.io.FileUtils; import org.springframework.util.StringUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 讀取Properties文件類容 */ public class PropertiesUtils{ public static JSONObject parse(String filePath){ File file = new File(filePath); JSONObject jsonObject = new JSONObject(); if (file.exists()) { List<?> LS = null; try { LS = FileUtils.readLines(file, "utf-8"); List<String> SQLS = new ArrayList<>(); StringBuilder SQL = new StringBuilder(); boolean ignoreTerms = false; for (Object L : LS) { String param = L.toString().trim(); if(StringUtils.isEmpty(param)){ continue; } String key =param.substring(0,param .indexOf("=")) ; String value =param.substring(param .indexOf("=")+ 1) ; jsonObject.put(key ,value); } } catch ( Exception e) { e.printStackTrace(); } } return jsonObject; } }
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- websocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <!--通用操作組件開始--> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <!--能用操作組件結束--> <!--數據庫開始--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>RELEASE</version> </dependency> <!-- Mysql 驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.29</version> </dependency> <!-- 日志組件開始 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.16</version> </dependency> <!-- 日志組件結束 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <!-- alj JSON --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.37</version> </dependency> <!-- alj JSON END -->