做的项目多了,接触到了不少国产数据库,如达梦,金仓,神龙,瀚高,高斯。现简单说下在代码层面,不同数据库适配的问题和处理方式
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做下替换:
@Beanpublic 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");}@Overridepublic 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!哈哈哈哈哈哈