首页 > 其他分享 >设计模式:一个应用理解模板方法模式

设计模式:一个应用理解模板方法模式

时间:2024-06-14 09:32:42浏览次数:22  
标签:return 子类 数据库 理解 void 模板 设计模式 public dataBaseProperties

一个需求

在实际生产开发中,数据库初始化、升级是没办法规避的,一般常见的方案是外挂一套初始化脚本加一堆SQL文件,其实可以把这个过程做到系统里,做到一个程序包内自带数据库的初始化,或者数据库升级,所以需求就是做一个数据库的初始化、升级的java功能,使用过flyway的同学应该能更明白这个需求,简单点说就是手搓一个flyway。

需求的分析与实现

因为这里主要是介绍设计模式,关于这个需求里制作SDK包或者Spring-starter不在这里复述,这里只针对需求的核心部分——调度SQL完成初始化。

根据需求,我们进一步拆解,该功能其实需要两部分,初始化/升级逻辑和预制的SQL文件。

逻辑梳理完毕,用最直接的方式实现,创建一个专门的流程类:

@Slf4j
public  class InitDataBase implements InitDataBase {


    public Connection commConn = null;

    public Connection dbConn = null;

    public JSONArray dbConfigFile = new JSONArray();

    public DataBaseProperties dataBaseProperties;

    public static Boolean flag = false;

    public InitDataBase(DataBaseProperties dataBaseProperties,){
        this.dataBaseProperties = dataBaseProperties;
    }

    public boolean isInitEd() {
        return flag;
    }

    public void startCoreJob() throws SQLException {
        //读取配置文件和SQL文件信息
        reloadConfigFile();
        //建立JDBC链接
        initCommConn();
        //验证链接是否正常
        if(testConnection()){
            Map<String,String> sqlcontent;
            //验证数据库实例是否存在
            if(databaseIsExitd(commConn)) {
                initDbConn();
                //是否存在版本表
                if(!exitDbVersionTable(dbConn)){
                    createDbVersionTable();
                }
                //比对代码配置中所需数据库版本是否大于当前数据库中实际版本
                if(getLatestVersion() > getCurrenDbVersion()) {
                    sqlcontent = getSqlFromFile(getCurrenDbVersion());
                }else {
                    flag = true;
                    return;
                }
            }else{
                //创建数据库实例
                if(createDataBase()){
                    initDbConn();
                    createDbVersionTable();
                }
                sqlcontent = getSqlFromFile(0f);
            }
            //执行SQL
            flag = excuteSQL(sqlcontent);
        }else{
            log.error("建立链接失败!");
        }
        close();
    }

     /**
     * 初始化数据库公共连接
     */
    public  void initCommConn(){
         try {
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:mysql://" + dataBaseProperties.getHost() + ":" + dataBaseProperties.getDbport() + "/mysql?characterEncoding=utf8&serverTimezone=GMT%2B8";
            commConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getRootUser(), dataBaseProperties.getRootPass());
        }catch (Exception e){
           e.printStackTrace()
        }
    }

    /**
     * 初始化对应数据库连接
     */
    public  void initDbConn(){
        try{
            String jdbcUrl = "jdbc:mysql://"+ dataBaseProperties.getHost()+":"+ dataBaseProperties.getDbport()+"/"+ dataBaseProperties.getDbName()+"?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=GMT%2B8";
            dbConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getUsername(), dataBaseProperties.getPassword());
        }catch (Exception e){
            e.printStackTrace()
        }
    }

    /**
     * 验证数据库实例/模式是否存在
     */
    public boolean databaseIsExitd(Connection connection){
        try {
            Statement stmt = connection.createStatement();
            ResultSet res = stmt.executeQuery("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name= \""+ dataBaseProperties.getDbName()+"\"");
            if(res.next() && res.getInt(1) == 0){
                stmt.close();
                return false;
            }
            return true;
        }catch (Exception e){
           e.printStackTrace()
        }
        return false;
    }

    /**
     * 建立指定数据库链接
     */
    public void  initDbConn(){
         try {
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:mysql://" + dataBaseProperties.getHost() + ":" + dataBaseProperties.getDbport() + "/"+dataBaseProperties.getDbName()+"?characterEncoding=utf8&serverTimezone=GMT%2B8";
            commConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getRootUser(), dataBaseProperties.getRootPass());
        }catch (Exception e){
           e.printStackTrace()
        }
    }
    
    /**
     * 数据库链接是否正常
     */
    public boolean testConnection() {
       return null != commConn;
    }


    /**
     * 获取程序配置中最新版本号
     * @return Float
     */
    private Float getLatestVersion() {
        int item = dbConfigFile.size()-1;
        JSONObject json = dbConfigFile.getJSONObject(item);
        return Float.parseFloat(json.getStr("version"));
    }
 

    public Float getCurrenDbVersion(){
        float version = 0f;
        //读取数据库中存放版本的表中的版本号
        return version;
    }

    public boolean excuteSQL(Map<String,String> sqlcontent) throws SQLException {
        // 批量执行sqlcontent中的SQL语句
        return false;
    }

    public boolean createDataBase() {
        //创建数据库实例
        return true;
    }

    public void updateDbVersion(Object version) throws SQLException {
        //更新版本表中的版本号state.execute("UPDATE "+conscriptProperties.getTableName()+" SET version = "+version+" , create_time = '"+ DateUtil.now()+"'");
        state.close();
    }

     /**
     * 确认版本表是否存在
     * @param connection 连接通道
     * @return Boolean
     */
    private Boolean exitDbVersionTable(Connection connection) throws SQLException {
        //确认版本表是否存在
    }

    public void close() throws SQLException{
        if(null != commConn){
            commConn.close();
            commConn = null;
        }
        if(null != dbConn){
            dbConn.close();
            dbConn = null;
        }
    }

    /**
     * 创建版本库表
     */
    private void createDbVersionTable(){
        //创建版本表
    }

    /**
     * 根据版本号获取需要执行的sql文件
     * @param ver 数据库内版本号
     * @return Map<String,String>
     */
    private Map<String,String> getSqlFromFile(Float ver) {
        //根据版本号,获取需要执行的SQL文件集合
    }

    /**
     * 加载数据库升级相关配置
     */
    private void reloadConfigFile() {
        String content = readFileContent(dataBaseProperties.getDbConfigFileUrl());
        if(null != content) {
            dbConfigFile = JSONUtil.parseArray(content);
        }
    }

}

接下来需求扩展,不同现场的数据库服务不同,有的使用Mysql,有的使用国产数据库、有的使用Oracle数据库……

为了响应需求的改变,开始分析不同类型数据库的差异,例如,建立JDBC的方式不同,数据库实例、模式概念的差异,权限的差异,SQL语句的差异等,与之对应的原本的逻辑就不得不进行修改调整,逻辑就变成了

针对此需求,最简单粗暴的就是再新建一个类,来实现新的数据库类型:

@Slf4j
public  class InitDataBaseForKingBase implements InitDataBase {


    public Connection commConn = null;

    public Connection dbConn = null;

    public JSONArray dbConfigFile = new JSONArray();

    public DataBaseProperties dataBaseProperties;

    public InitDataBaseForKingBase(DataBaseProperties dataBaseProperties,){
        this.dataBaseProperties = dataBaseProperties;
    }

    public void startCoreJob() throws SQLException {
        //读取配置文件和SQL文件信息
        reloadConfigFile();
        //建立JDBC链接
        initCommConn();
        //验证链接是否正常
        if(testConnection()){
            Map<String,String> sqlcontent;
            //验证数据库模式是否存在
            if(schemaIsExitd(commConn)) {
                initDbConn();
                //是否存在版本表
                if(!exitDbVersionTable(dbConn)){
                    createDbVersionTable();
                }
                //比对代码配置中所需数据库版本是否大于当前数据库中实际版本
                if(getLatestVersion() > getCurrenDbVersion()) {
                    sqlcontent = getSqlFromFile(getCurrenDbVersion());
                }else {
                    flag = true;
                    return;
                }
            }else{
                //创建数据库实例
                if(createDataBase()){
                    initDbConn();
                    createDbVersionTable();
                }
                sqlcontent = getSqlFromFile(0f);
            }
            //执行SQL
            flag = excuteSQL(sqlcontent);
        }else{
            log.error("建立链接失败!");
        }
        close();
    }

     /**
     * 初始化数据库公共连接
     */
    public  void initCommConn(){
          try{
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:kingbase8://" + dataBaseProperties.getHost() + ":"+dataBaseProperties.getDbport();
            commConn = DriverManager.getConnection(jdbcUrl,dataBaseProperties.getRootUser(),dataBaseProperties.getRootPass());
            commConn.setAutoCommit(true);
        }catch (Exception e){
            e.printStackTrace()
        }
    }

    /**
     * 初始化对应数据库连接
     */
    public  void initDbConn(){
         try{
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:kingbase8://" + dataBaseProperties.getHost() + ":"+dataBaseProperties.getDbport()+"/"+dataBaseProperties.getDbName()+"??currentSchema="+dataBaseProperties.getSchema()+"&characterEncoding=utf-8";
            dbConn = DriverManager.getConnection(jdbcUrl,dataBaseProperties.getUsername(),dataBaseProperties.getPassword());
            dbConn.setAutoCommit(true);
        }catch (Exception e){
            log.error("【Conscript】Database initialization :data base is not connection.....");
        }
    }

    /**
     * 验证数据库实例/模式是否存在
     */
    public boolean schemaIsExitd(Connection connection){
        try {
            Statement stmt = connection.createStatement();
            ResultSet res = stmt.executeQuery("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name= \""+ dataBaseProperties.getDbName()+"\"");
            if(res.next() && res.getInt(1) == 0){
                stmt.close();
                return false;
            }
            return true;
        }catch (Exception e){
           e.printStackTrace()
        }
        return false;
    }

    /**
     * 建立指定数据库链接
     */
    public void  initDbConn(){
         try {
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:mysql://" + dataBaseProperties.getHost() + ":" + dataBaseProperties.getDbport() + "/"+dataBaseProperties.getDbName()+"?characterEncoding=utf8&serverTimezone=GMT%2B8";
            commConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getRootUser(), dataBaseProperties.getRootPass());
        }catch (Exception e){
           e.printStackTrace()
        }
    }
    
    /**
     * 数据库链接是否正常
     */
    public boolean testConnection() {
       return null != commConn;
    }


    /**
     * 获取程序配置中最新版本号
     * @return Float
     */
    private Float getLatestVersion() {
        int item = dbConfigFile.size()-1;
        JSONObject json = dbConfigFile.getJSONObject(item);
        return Float.parseFloat(json.getStr("version"));
    }
 

    public Float getCurrenDbVersion(){
        float version = 0f;
        //读取数据库中存放版本的表中的版本号
        return version;
    }

    public boolean excuteSQL(Map<String,String> sqlcontent) throws SQLException {
        // 批量执行sqlcontent中的SQL语句
        return false;
    }

    public boolean createDataBase() {
        try {
            Statement stmt = commConn.createStatement();
            //这里的创建数据库语句和MYSQL有不同
            stmt.execute("CREATE DATABASE "+dataBaseProperties.getDbName()+" OWNER "+dataBaseProperties.getUsername()+" encoding 'utf8' CONNECTION LIMIT 10");
            stmt.execute(this.createDataBaseSQL());
            stmt.close();
        }catch (Exception e){
            return false;
        }
        return true;
       
    }

    public void updateDbVersion(Object version) throws SQLException {
        //更新版本表中的版本号state.execute("UPDATE "+conscriptProperties.getTableName()+" SET version = "+version+" , create_time = '"+ DateUtil.now()+"'");
        state.close();
    }

     /**
     * 确认版本表是否存在
     * @param connection 连接通道
     * @return Boolean
     */
    private Boolean exitDbVersionTable(Connection connection) throws SQLException {
        //确认版本表是否存在
    }

    public void close() throws SQLException{
        if(null != commConn){
            commConn.close();
            commConn = null;
        }
        if(null != dbConn){
            dbConn.close();
            dbConn = null;
        }
    }

    /**
     * 创建版本库表
     */
    private void createDbVersionTable(){
        //创建版本表
    }

    /**
     * 根据版本号获取需要执行的sql文件
     * @param ver 数据库内版本号
     * @return Map<String,String>
     */
    private Map<String,String> getSqlFromFile(Float ver) {
        //根据版本号,获取需要执行的SQL文件集合
    }

    /**
     * 加载数据库升级相关配置
     */
    private void reloadConfigFile() {
        String content = readFileContent(dataBaseProperties.getDbConfigFileUrl());
        if(null != content) {
            dbConfigFile = JSONUtil.parseArray(content);
        }
    }

}

又或者不在乎开闭原则,直接采用面向过程的方式垒逻辑,对关键差异的地方进行if else:

 /**
     * 初始化数据库公共连接
     */
    public  void initCommConn(){
         if("mysql".equals(dataBaseProperties.getDbType())){   
            try {
                Class.forName(dataBaseProperties.getDriverClassName());
                String jdbcUrl = "jdbc:mysql://" + dataBaseProperties.getHost() + ":" + dataBaseProperties.getDbport() + "/mysql?characterEncoding=utf8&serverTimezone=GMT%2B8";
                commConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getRootUser(), dataBaseProperties.getRootPass());
            }catch (Exception e){
            e.printStackTrace()
            }
         }else if("dm".equals(dataBaseProperties.getDbType())) {
            //对应的类型链接
         }
    }

    /**
     * 初始化对应数据库连接
     */
    public  void initDbConn(){
       if("mysql".equals(dataBaseProperties.getDbType())){    
            try{
                String jdbcUrl = "jdbc:mysql://"+ dataBaseProperties.getHost()+":"+ dataBaseProperties.getDbport()+"/"+ dataBaseProperties.getDbName()+"?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=GMT%2B8";
                dbConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getUsername(), dataBaseProperties.getPassword());
            }catch (Exception e){
                e.printStackTrace()
            }
       }else if("dm".equals(dataBaseProperties.getDbType())) {
            //对应的类型链接
         }
    }

    /**
     * 验证数据库实例/模式是否存在
     */
    public boolean databaseIsExitd(Connection connection){
        if("mysql".equals(dataBaseProperties.getDbType())){    
            try {
                Statement stmt = connection.createStatement();
                ResultSet res = stmt.executeQuery("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name= \""+ dataBaseProperties.getDbName()+"\"");
                if(res.next() && res.getInt(1) == 0){
                    stmt.close();
                    return false;
                }
                return true;
            }catch (Exception e){
            e.printStackTrace()
            }
        }else if("dm".equals(dataBaseProperties.getDbType())) {
            //对应的类型链接
         }
        return false;
    }

    /**
     * 建立指定数据库链接
     */
    public void  initDbConn(){
        if("mysql".equals(dataBaseProperties.getDbType())){  
            try {
                Class.forName(dataBaseProperties.getDriverClassName());
                String jdbcUrl = "jdbc:mysql://" + dataBaseProperties.getHost() + ":" + dataBaseProperties.getDbport() + "/"+dataBaseProperties.getDbName()+"?characterEncoding=utf8&serverTimezone=GMT%2B8";
                commConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getRootUser(), dataBaseProperties.getRootPass());
            }catch (Exception e){
            e.printStackTrace()
            }
        }else if("dm".equals(dataBaseProperties.getDbType())) {
            //对应的类型链接
         }
    }

其实不论哪种实现方式,都在代码量和扩展性或者设计原则上有问题:

新开一个类,虽然不违背开闭原则,但是会发现两种数据库初始化太相似了,现在完全分开,当作两个独立的类来实现,如果以后需要扩展其他能力,比如对SQL进行语法校验,那么两个类都需要再次修改,这是很麻烦的。

而if else的写法不用多说,违背开闭原则、代码臃肿、维护性降低,随着需求的越来越多,if else 会以熵增式的暴涨,最后沦为依托答辩。

模板模式

模板模式的定义:定义一个操作中的算法的骨架,而将一些步骤延展到子类中,模板方法使得子类可以不改变一个算法的结构就可以重定义该算法的某些特定步骤。

下面是模板模式的类图结构:

AbstractClass:抽象类,用来定义算法骨架和原始操作,由子类通过重新定义这些原始操作来实现一个算法的各个步骤。类中还可以提供算法中通用的实现

ConcreteClass: 具体实现类,用来实现算法骨架中的某些步骤,完成特定子类相关功能

代码样例:

//携带算法骨架的抽象父类
public abstract class AbstractClass{
   
   //操作流程1
   abstract void startJob();

   //操作流程2
   abstract void endJob();


   public void runJob(){
      //定义算法流程
      startJob();
      endJob();
   }   
   

   public void commOp(){
      //公共操作具体代码
   }

}

//具体子类
public class ConcreteClass extend AbstractClass {
   
   
   public void startJob(){
      System.out.print("操作1")
   }

   
   public void endJob(){
      System.out.print("操作2")
   }


}

模板模式实现数据库升级

根据需求,对于不同类型数据库来看,数据库初始化和升级流程是一致的,而差异性主要体现在

  •  初始化JDBC链接
  • 数据库实例判定
  • 创建数据库实例

可以在父类中定义不变的流程,而差异性让子类去实现,通过模板模式建立的类图关系:

而数据库的差异性体现在:

     /**
     * 初始化数据库公共连接
     */
    public abstract void initCommConn();

    /**
     * 初始化对应数据库连接
     */
    public abstract void initDbConn();

    /**
     * 建库语句
     * @return String
     */
    public abstract String createDataBaseSQL();
 
    /**
     * 数据库实例是否存在
     * @return String
     */
    public abstract  boolean databaseIsExitd(Connection connection);

将以上方法延申到子类中去执行

首先定义抽象父类,类中定义数据库初始化的核心流程骨架,以及不同类型需要做的具体操作(抽象方法):

@Slf4j
public  class AbstractInitDataBase  implements InitDataBase {


    public Connection commConn = null;

    public Connection dbConn = null;

    public JSONArray dbConfigFile = new JSONArray();

    public DataBaseProperties dataBaseProperties;

    public AbstractInitDataBase (DataBaseProperties dataBaseProperties,){
        this.dataBaseProperties = dataBaseProperties;
    }

    public void startCoreJob() throws SQLException {
        //读取配置文件和SQL文件信息
        reloadConfigFile();
        //建立JDBC链接
        initCommConn();
        //验证链接是否正常
        if(testConnection()){
            Map<String,String> sqlcontent;
            //验证数据库模式是否存在
            if(dataBaseIsExitd(commConn)) {
                initDbConn();
                //是否存在版本表
                if(!exitDbVersionTable(dbConn)){
                    createDbVersionTable();
                }
                //比对代码配置中所需数据库版本是否大于当前数据库中实际版本
                if(getLatestVersion() > getCurrenDbVersion()) {
                    sqlcontent = getSqlFromFile(getCurrenDbVersion());
                }else {
                    flag = true;
                    return;
                }
            }else{
                //创建数据库实例
                if(createDataBase()){
                    initDbConn();
                    createDbVersionTable();
                }
                sqlcontent = getSqlFromFile(0f);
            }
            //执行SQL
            flag = excuteSQL(sqlcontent);
        }else{
            log.error("建立链接失败!");
        }
        close();
    }

     /**
     * 定义初始化JDBC链接,交由具体的数据库类型子类去实现
     */
    public abstract void initCommConn();

    /**
     * 定义初始化数据库连接,交由具体的子类去实现
     */
    public abstract void initDbConn();

    /**
     * 定义数据库实例判定标准,交由具体的子类去实现
     */
    public abstract boolean dataBaseIsExitd(Connection connection);

    /**
     * 定义初始化具体数据库连接,交由具体的子类去实现
     * 
     */
    public abstract void  initDbConn();
    
    /**
     * 数据库链接是否正常
     */
    public boolean testConnection() {
       return null != commConn;
    }


    /**
     * 获取程序配置中最新版本号
     * @return Float
     */
    private Float getLatestVersion() {
        int item = dbConfigFile.size()-1;
        JSONObject json = dbConfigFile.getJSONObject(item);
        return Float.parseFloat(json.getStr("version"));
    }
 

    public Float getCurrenDbVersion(){
        float version = 0f;
        //读取数据库中存放版本的表中的版本号
        return version;
    }

    public boolean excuteSQL(Map<String,String> sqlcontent) throws SQLException {
        // 批量执行sqlcontent中的SQL语句
        return false;
    }

    public boolean createDataBase() {
        try {
            Statement stmt = commConn.createStatement();
            //这里的创建数据库语句和MYSQL有不同
            stmt.execute("CREATE DATABASE "+dataBaseProperties.getDbName()+" OWNER "+dataBaseProperties.getUsername()+" encoding 'utf8' CONNECTION LIMIT 10");
            stmt.execute(this.createDataBaseSQL());
            stmt.close();
        }catch (Exception e){
            return false;
        }
        return true;
       
    }

    public void updateDbVersion(Object version) throws SQLException {
        //更新版本表中的版本号state.execute("UPDATE "+conscriptProperties.getTableName()+" SET version = "+version+" , create_time = '"+ DateUtil.now()+"'");
        state.close();
    }

     /**
     * 确认版本表是否存在
     * @param connection 连接通道
     * @return Boolean
     */
    private Boolean exitDbVersionTable(Connection connection) throws SQLException {
        //确认版本表是否存在
    }

    public void close() throws SQLException{
        if(null != commConn){
            commConn.close();
            commConn = null;
        }
        if(null != dbConn){
            dbConn.close();
            dbConn = null;
        }
    }

    //其他公用方法不变

}


再根据实际的需求,去实现不同类型数据库的子类,由子类去完成因为模式概念、SQL语法、链接语法差异性的地方:

//针对Mysql数据库的子类
@Slf4j
public class MySqlDataBase extends AbstractInitDataBase {

    public MySqlDataBase(DataBaseProperties dataBaseProperties) {
        super(dataBaseProperties);
    }

    @Override
    public void initCommConn() {
        try {
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:mysql://" + dataBaseProperties.getHost() + ":" + dataBaseProperties.getDbport() + "/mysql?characterEncoding=utf8&serverTimezone=GMT%2B8";
            commConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getRootUser(), dataBaseProperties.getRootPass());
        }catch (Exception e){
            log.error("【Conscript】Database initialization :data base is not connection.....");
        }
    }

    @Override
    public void initDbConn() {
        try{
            String jdbcUrl = "jdbc:mysql://"+ dataBaseProperties.getHost()+":"+ dataBaseProperties.getDbport()+"/"+ dataBaseProperties.getDbName()+"?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=GMT%2B8";
            dbConn = DriverManager.getConnection(jdbcUrl, dataBaseProperties.getUsername(), dataBaseProperties.getPassword());
        }catch (Exception e){
            log.warn("");
        }
    }

    @Override
    public boolean databaseIsExitd(Connection connection){
        try {
            Statement stmt = connection.createStatement();
            ResultSet res = stmt.executeQuery("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name= \""+ dataBaseProperties.getDbName()+"\"");
            if(res.next() && res.getInt(1) == 0){
                stmt.close();
                return false;
            }
            return true;
        }catch (Exception e){
            log.error("【Conscript】Database initialization :database base query is error");
        }
        return false;
    }

    @Override
    public String createDataBaseSQL() {
        return "CREATE DATABASE IF NOT EXISTS "+ dataBaseProperties.getDbName()+" DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci";
    }

}

//kongBase的类型子类
@Slf4j
public class KingBase8DataBase extends AbstractInitDataBase {


    public KingBase8DataBase(DataBaseProperties dataBaseProperties, ConscriptProperties conscriptProperties) {
        super(dataBaseProperties, conscriptProperties);
    }

    @Override
    public void initCommConn() {
        try{
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:kingbase8://" + dataBaseProperties.getHost() + ":"+dataBaseProperties.getDbport()+"/TEST";
            commConn = DriverManager.getConnection(jdbcUrl,dataBaseProperties.getRootUser(),dataBaseProperties.getRootPass());
            commConn.setAutoCommit(true);
        }catch (Exception e){
           e.printStack()
        }
    }

    @Override
    public void initDbConn() {
        try{
            Class.forName(dataBaseProperties.getDriverClassName());
            String jdbcUrl = "jdbc:kingbase8://" + dataBaseProperties.getHost() + ":"+dataBaseProperties.getDbport()+"/"+dataBaseProperties.getDbName()+"??currentSchema="+dataBaseProperties.getSchema()+"&characterEncoding=utf-8";
            dbConn = DriverManager.getConnection(jdbcUrl,dataBaseProperties.getUsername(),dataBaseProperties.getPassword());
            dbConn.setAutoCommit(true);
        }catch (Exception e){
            e.printStack()
        }
    }

    @Override
    public boolean createDataBase() {
        try {
            Statement stmt = commConn.createStatement();
            stmt.execute("CREATE DATABASE "+dataBaseProperties.getDbName()+" OWNER "+dataBaseProperties.getUsername()+" encoding 'utf8' CONNECTION LIMIT 10");
            stmt.execute(this.createDataBaseSQL());
            stmt.close();
        }catch (Exception e){
            return false;
        }
        return true;
    }

    @Override
    public String createDataBaseSQL() {
        return "CREATE SCHEMA \""+dataBaseProperties.getSchema()+"\" AUTHORIZATION \""+dataBaseProperties.getUsername()+"\" ";
    }

    @Override
    public boolean databaseIsExitd(Connection connection) {
        try {
            Statement stmt = connection.createStatement();
            ResultSet res = stmt.executeQuery("SELECT DATNAME FROM sys_database WHERE DATNAME = '"+dataBaseProperties.getDbName()+"'");
            if(!res.next()){
                stmt.close();
                return false;
            }
            return true;
        }catch (Exception e){
            e.printStack()
        }
        return false;
    }
}

此后,如果再新增其他类型数据库,只需要继承AbstractInitDataBase  类,然后分别实现具体的

initCommConndatabaseIsExitd等方法即可完成扩展,如果需要进行一些通用的改变,例如对SQL文件中的SQL语句进行语法验证,则可以修改AbstractInitDataBase 类中的通用方法完成。

完整项目git代码路径:Conscript: java数据库自动初始化,项目已经实现了stater化且支持达梦、金仓、pg等数据库的兼容,有兴趣可以参考一下。

模板模式的理解

模板模式的核心要义就是固定算法框架,从而让具体的算法实现可扩展,是面向对象中继承用法的标准应用之一,在实际应用中多用在框架级功能的实现:即定义好框架流程,在适合的点让具体的研发人员去扩展。

为什么是抽象类而不是接口?

在工厂方法模式那一篇文章中我一直在说面向接口编程,而这里为什么使用抽象类而不用接口,首先抽象类相比接口,最大的特点在于可以实现具体的方法,这一点在模板的算法框架定义是必须的,单纯的使用接口,那只是定义原子操作,而无法定义流程骨架之类的内容。另一方面,把模板类定义为抽象类,还可以给所有子类提供公共方法,在约束子类的前提下,又进一步的限定了子类的职责和操作范围。

程序设计的一个重要平衡点就是“变与不变”,也就是分析功能中哪些是变化的,哪些是不变的,把不变的部分进行公共实现或者用接口来封装隔离,把变化的分离出去,交给子类,以类文件的形式进行扩展,规避去修改已有类;

模板模式很好的体现了“不要找我们,我们会联系你”,作为父类的模板会在需要的时候,调用子类相应方法,也就是由父类来找子类,而不是让子类来找父类;这其实也是一种反向控制结构,按正常思路是子类找父类才对。也就是应该子类来调用父类的方法,因为父类根本不知道子类,而子类是知道父类的。但是模板模式种是父类根据约束来调度子类的,所以是一种反向控制。其理论依据就是Java的动态绑定采用的是“后期绑定”技术,对于出现子类覆盖父类的方法的情况,在编译时是看数据类型,运行时则是看实际的对象类型,即:new 谁就调用谁的方法。模板模式用的数据类型是模板类型,但是在创建实例的时候是子类的实例。在调用的时候,会被动态的绑定到子类方法上,从而实现反向控制。

Java种典型的模板模式应用:排序

在实际Java中,也存在模板应用的影子,基于Comparator的集合排序大家一定使用过,例如:

public class User {
    private int id;
    private String name;
    private int age;

    // User类的构造函数、getter和setter省略
}

public class Main {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        // 填充User列表
        users.add(new User(1, "Alice", 25));
        users.add(new User(2, "Bob", 30));
        users.add(new User(3, "Charlie", 20));

        // 使用Lambda表达式对User列表按年龄排序
        Collections.sort(users, (u1, u2) -> Integer.compare(u1.getAge(), u2.getAge()));

        // 打印排序后的列表
        for (User user : users) {
            System.out.println(user.getName() + " - " + user.getAge());
        }
    }
}

通过Lambda表达式进行集合的排序,单看这里的语句,可能看不到模板模式的影子,那把这段Lambda表达式的实现原理拆解一下:

public class Main {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        // 填充User列表
        users.add(new User(1, "Alice", 25));
        users.add(new User(2, "Bob", 30));
        users.add(new User(3, "Charlie", 20));

        // Lambda表达式的翻译即使用内部类实现Comparator接口
        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User u1, User u2) {
                return Integer.compare(u1.getAge(), u2.getAge());
            }
        });

        // 打印排序后的列表
        for (User user : users) {
            System.out.println(user.getName() + " - " + user.getAge());
        }
    }
}

再进一步翻译:


//将内部类以传统的JAVA实现摘出来,独立成一个类
public class UserAgeComparator implements Comparator<User> {
    @Override
    public int compare(User u1, User u2) {
        return Integer.compare(u1.getAge(), u2.getAge());
    }
}

public class Main {
    public static void main(String[] args) {
        List<User> users = new ArrayList<>();
        // 填充User列表
        users.add(new User(1, "Alice", 25));
        users.add(new User(2, "Bob", 30));
        users.add(new User(3, "Charlie", 20));

        // 使用自定义的比较器对User列表按年龄排序
        Collections.sort(users, new UserAgeComparator());

        // 打印排序后的列表
        for (User user : users) {
            System.out.println(user.getName() + " - " + user.getAge());
        }
    }
}

看排序会发现,列表究竟依赖什么标准来排序,完全是依赖于Comparator的具体实现,也就是说,排序的算法步骤是固定的,只是进行排序比较这一操作存在差异性,把这一步骤交给子类或者说外部去自行定义,从而实现灵活的排序扩展,这就是策略模式的体现。

标签:return,子类,数据库,理解,void,模板,设计模式,public,dataBaseProperties
From: https://blog.csdn.net/qq_40690073/article/details/139625487

相关文章

  • 深入理解Java中的StringBuffer与StringBuilder:性能、用法与代码样例
    在Java编程中,当我们需要频繁地修改字符串时,使用String类可能会遇到性能问题,因为String是不可变的(immutable)。为了解决这个问题,Java提供了两个可变字符串类:StringBuffer和StringBuilder。这两个类都允许我们在不创建新对象的情况下修改字符串,但它们之间也有一些重要的区别。......
  • 高通SA8295P芯片技术规格详解与原理解析
    高通SA8295P芯片技术规格详解与原理解析高通SA8295P(骁龙8295)是一款专为汽车座舱设计的高性能SoC(系统级芯片),采用最新的5nm工艺,具备强大的计算能力、图形处理能力以及丰富的外设支持。以下是该芯片的详细技术规格和工作原理解析。1.处理器核心(CPU)Snapdragon™SA8295PS......
  • 用最简单的方式理解函数重载
    一、什么是函数重载函数重载的定义:在同一个作用域内,使用不同的函数名,实现不同的函数功能,而且编译器不会发生报错的情况。函数重载的条件以及注意事项:1、在同一个作用域内2、相同的函数名.3、函数参数的个数、顺序、类型不同4、函数重载与返回值无关二、函数重载发生的原......
  • 【Java中常用的设计模式总结】
    文章目录概要1、单例模式(SingletonPattern)2、工厂模式(FactoryPattern)3、建造者模式(BuilderPattern)4、原型模式(PrototypePattern)5、适配器模式(AdapterPattern)6、桥接模式(BridgePattern)7、组合模式(CompositePattern)8、装饰器模式(DecoratorPattern)9、外观模式(Facade......
  • 单细胞RNA测序(scRNA-seq) 理解Seurat对象存储信息含义和基本操作
    单细胞测序技术是在单个细胞水平上,对基因组、转录组和表观基因组水平进行分析测序技术。bulkRNA-seq获得的是组织或器官等大量细胞中表达信号的均值,无法获取细胞之间的差异信息(即丢失了细胞的异质性),而单细胞测序技术可以很好的弥补bulkRNA-seq这一不足,即获取混合样本中......
  • 【最新鸿蒙应用开发】——持久化的理解
    应用数据持久化1.概述应用数据持久化,是指应用将内存中的数据通过文件或数据库的形式保存到设备上。内存中的数据形态通常是任意的数据结构或数据对象,存储介质上的数据形态可能是文本、数据库、二进制文件等。2.分类OpenHarmony标准系统支持典型的存储数据形态,包括用户首......
  • 面试官:谈谈对SpringAI的理解?
    SpringAI已经发布了好长时间了,目前已经更新到1.0版本了,所以身为Java程序员的你,如果还对SpringAI一点都不了解的话,那就有点太落伍了。言归正传,那什么是SpringAI?如何快速进行SpringAI开发呢?1.什么是SpringAI?SpringAI是Spring官方社区项目,旨在简化JavaAI应......
  • 设计模式-桥接模式
    桥接模式桥接模式(bridge)也成为桥梁模式,接口(Interface)模式,柄体(HandleAndBody)模式,是将抽象部分与它的具体实现部分分离,使得它们可以独立的变化,属于结构型模式。桥接模式主要是通过组合的方式建立两个类之间的关系,而不是继承。但又类似于多重继承方案,但是多重继承方案往往违......
  • Beta版会议总结(事后诸葛亮模板)
    **1.*以“事后诸葛亮”为模板总结会议header1、我们的软件要解决什么问题?是否定义的很清楚?是否对典型用户和典型场景有清晰的描述?主要是要方便老师学生的生活,少跑一趟取快递时间可用做其他事情,而取快递的人可以通过拿一次快递,挣一顿饭钱,方便自己方便他人;......
  • c++定义了类在main函数中使用的一个坑现象的解决,让我理解了栈,堆和内存之间关系。
    首先描述一下我的坑是啥?我的坑就是写了一个对集料颗粒进行角度计算的类,在main函数中使用采用了类定义申明,这样使用导致一个坑,这个类中对于集料的数目进行了宏定义,发现数据如果超过20个,编译就报错,当时没有太在意这个坑,没有思考什么原因。也就将就者用了。后来对接同事说,这个颗粒数......