信创时一些国产数据适配的问题

做的项目多了,接触到了不少国产数据库,如达梦,金仓,神龙,瀚高,高斯。现简单说下在代码层面,不同数据库适配的问题和处理方式

1:国产数据库查schema信息的时候语法不同。会导致一些表结构同步、代码生成的时候会有不少问题
解决方法:强行指定为mysql或oracle或者新建补充:

        if (url.contains("kingbase8")) { // 金仓
            this.dbQuery = new KingBaseSqlQuery();
        } else if (url.contains("dm")) {
            this.dbQuery = new DmQuery();
            dataSourceConfig.setTypeConvert(new OracleTypeConvert());// 达梦
        } else if (url.contains("highgo")) {
            this.dbQuery = new DmQuery(); // 瀚高
            dataSourceConfig.setTypeConvert(new OracleTypeConvert());
        } else {
            this.dbQuery = dataSourceConfig.getDbQuery(); // 其他
        }

2: 由于mybatis,PageHelper等都是国外写的,导致一些sql的方言之类等方面自动匹配数据库厂家失效,从而导致报错。
解决办法:需要把DatabaseIdProvider做下替换:

    @Bean
    public DatabaseIdProvider databaseIdProvider() {
        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
        databaseIdProvider.setProperties(getDBTypeProperties());
        return databaseIdProvider;
    }
    
    public static Properties getDBTypeProperties() {
        Properties p = new Properties();
        p.setProperty("Oracle", "oracle");
        p.setProperty("MySQL", "mysql");
        p.setProperty("SQL Server", "sqlserver");
        p.setProperty("DM DBMS", "dm"); // 达梦
        p.setProperty("OSCAR", "st"); // 神通
        p.setProperty("KingbaseES", "mysql"); // 金仓,强制使用mysql语系
        p.setProperty("PostgreSQL", "mysql"); // 瀚高,强制使用mysql语系
        p.setProperty("GaussDB", "mysql"); // 高斯,强制使用mysql语系
        return p;
    }
// 分页插件处理
    public PaginationInterceptor paginationInterceptor(DataSource dataSource) {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        String dialectType = findDialectType(dataSource);
        if (ZYStrUtils.isNotNull(dialectType)) {
            paginationInterceptor.setDialectType(dialectType);
        }
        return paginationInterceptor;
    }
    
 public static String findDialectType(DataSource dataSource) {
        String databaseProductName;
        try {
            databaseProductName = dataSource.getConnection().getMetaData().getDatabaseProductName();
        } catch (SQLException e) {
            return null;
        }
        if (null == databaseProductName) {
            return null;
        }
        Properties dbTypeProperties = getDBTypeProperties();
        return dbTypeProperties.getProperty(databaseProductName);
    }

3:国产数据库函数,尤其是一些生僻的函数要么缺失,要么百度不出来,查文档也查半天。
解决办法:拼接时,做下兼容,禁止使用生避函数

public class IfNull {

    private static final String MYSQL_FUN = " ifNull(%s,%s) ";
    private static final String ORACLE_FUN = " isNull(%s,%s) ";

    public static String spell(String field, Object defaultValue) {
        boolean isString = defaultValue instanceof String;
        defaultValue = isString ? "'" + defaultValue + "'" : defaultValue;
        if (ZYDBUtils.isLikeOracleDB()) {
            return String.format(ORACLE_FUN, field, defaultValue);
        } else {
            return String.format(MYSQL_FUN, field, defaultValue);
        }
    }

    public static String spell(String sql, String field, Object defaultValue) {
        String spell = spell(field, defaultValue);
        return sql.replaceAll("#ifNull", spell);
    }
}

4:部份国产数据库大小写敏感,数据类型敏感。缺失自动数据类型转换
解决办法:
部份可以配置,部份也只能见一处改一处了。改多了慢慢就没了。大小写还好,基本上能全局配置。但缺类型转换就只能对编码人员做要求了。实体类的类型必须和数据库类型一致。

5:国产数据库大体分mysql语系和oracle语系,但也有混用的情况。尤其是做批量新增修改时,mysql语系里必须 使用oracle的语法,oracle语系里,又可以使用mysql关联更新语法。建表语法也有差异。
解决办法:每种语法都写呗

public class MyInsertList extends AbstractMethod {

    private static List<String> oracleSqlGrammar;
    private static List<String> mysqlGrammar;
    private static String productName;

    static {
        oracleSqlGrammar = new ArrayList<>();
        oracleSqlGrammar.add("Oracle");
        oracleSqlGrammar.add("DM DBMS");
        oracleSqlGrammar.add("OSCAR");
        mysqlGrammar = new ArrayList<>();
        mysqlGrammar.add("KingbaseES");
        mysqlGrammar.add("MySQL");
        mysqlGrammar.add("PostgreSQL");
        mysqlGrammar.add("GaussDB");
    }

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        if (null == productName) {
            productName = getProductName();
        }
        String sqlResult;
        if (ZYStrUtils.isNull(productName) || mysqlGrammar.contains(productName)) {
            sqlResult = generateMysqlVersionBatchSql(tableInfo);
        } else if (oracleSqlGrammar.contains(productName)) {
            sqlResult = generateOracleVersionBatchSql(tableInfo);
        } else {
            throw new LocalException("不支持的批量新增数据库版本");
        }
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, new NoKeyGenerator(), tableInfo.getKeyProperty(), tableInfo.getKeyColumn());
    }

    private String generateOracleVersionBatchSql(TableInfo tableInfo) {
        StringBuilder batchSql = new StringBuilder();
        batchSql.append("<script>");
        batchSql.append(" INSERT ALL ");
        batchSql.append("<foreach collection=\"list\" item=\"item\" separator=\" \">");
        batchSql.append(" INTO ").append(tableInfo.getTableName());
        batchSql.append(" ");
        batchSql.append(prepareFieldSql(tableInfo));
        batchSql.append(" VALUES (");
        batchSql.append(prepareOracleValuesSql(tableInfo));
        batchSql.append(") ");
        batchSql.append("</foreach>");
        batchSql.append(" SELECT 1 FROM dual");
        batchSql.append("</script>");
        return batchSql.toString();
    }

    public String generateMysqlVersionBatchSql(TableInfo tableInfo) {
        final String sql = "<script>insert into %s %s values %s</script>";
        final String fieldSql = prepareFieldSql(tableInfo);
        final String valueSql = prepareMysqlValuesSql(tableInfo);
        return String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
    }

    <update id="createLogTable" databaseId="dm">
    <update id="createLogTable" databaseId="mysql">
    <update id="createLogTable" databaseId="st">
    <update id="createLogTable" databaseId="oracle">

6:部份国产数据库迁移工具有些很坑爹的行为,比如用long类型的日期,迁移后,变成了科学计数,导致日期区间查询失效。比如varchar迁移后变成char,后面给你来个空格补全。
解决办法:这个倒是不麻烦。国产数据库大多数自带了一个迁移工具。里面都有个设置类型映射的功能。把那个类型找准了,这个问题不难处理。

怎么说呢,这东西需要长时间的沉积,上述也不能全保护这些数据库的兼容。有时候也处于爆出来就改,没爆出=没问题的状态。等一些适配方案趋于稳定后,其实也还好。主要是前期难受点。
另外友情提醒:日期全部用long! 日期全部用long! 日期全部用long!哈哈哈哈哈哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值