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