最近公司有需求,需要对框架做oracle与mysql的兼容。由于框架以前一直是使用的oracle,现在需要做兼容,及是该框架同时支持oracle和mysql版本,oracle和mysql根据用户环境来选择,只需要切换数据库而不需要对代码做任何改动。
1. 自定义sql语句兼容。
这个实际上是mybatis支持的,在mybatismapper.xml中,所有标签都支持databaseId属性,这个属性就是你选择的所属的数据库的ID, 而标签ID不在为唯一属性,需要和databaseId联合唯一。这个比较简单,只需要在spring里自定义DatabaseIdProvider就可以设置databaseId了
/**
* 数据库兼容,在mybatis里设置相应的databaseId即可
*/
@Bean
public DatabaseIdProvider databaseIdProvider(){
DatabaseIdProvider databaseIdProvider=new VendorDatabaseIdProvider();
Properties properties=new Properties();
properties.setProperty("Oracle","oracle");
properties.setProperty("MySQL","mysql");
databaseIdProvider.setProperties(properties);
return databaseIdProvider;
}
2. 使用Mybatisplus
因为mybatisplus的时候都是自动生成sql语句,我们无法去设置选择哪个databaseId,所以这里怎么处理呢,这个只需要在不同环境设置mybatis-plus.global-config.db-config.db-type = oracle/mysql ,就可以了,我使用的配置中心来管理,分别做了一个oracle配置中心和mysql配置中心,当使用哪个时,只需要启动对应的配置中心就可以了,而应用不需要做任何改动。
但是使用mybatisplus时还有个注意的地方,当对某个列我们使用模糊查询时,@TableField(condition=SqlCondition.like),使用mysqlplus这个sqlcondition.like是针对mysql的,使用oracle时会报错,这里可以自定义一个兼容mysql和oracle的常量类
public static final String LIKE="%s LIKE CONCAT(CONCAT('%%',#{%s}),'%%')";
这个like就能同时兼容mysql和oracle了。
3. mysql关键字
迁移数据之后发现,我们在oracle使用过程中我们在某个表中使用到了key,value,这个字段在mysql中是关键字,直接查询会报错,需要做引号处理`key`,`value`这样才能使用。这种情况怎么处理呢,当然,如果不需要做兼容处理,这里很简单就能处理,
@TableField(value = "`value`")
private String value;
直接这样就可以了。
但是我需要做兼容,这样肯定不行,mysql不会存在问题,但是使用oracle就会存在问题了,而annotation只能传入唯一静态常量。
1.然后研究源码,发现在TableFieldInfo中存在这样一段
String columnFormat = dbConfig.getColumnFormat();
if (StringUtils.isNotEmpty(columnFormat)) {
column = String.format(columnFormat, column);
}
设置dbconfig,所以,我们可以在配置中心,mysql版本中设置mybatis-plus.global-config.db-config.columnFormat="`%s`"就可以了。
2.虽然有这种办法,但是这种会把所有mysql的列都会进行替换,那么还有没有其他的方式呢。
最终发现mysqlplus支持动态修改表名的一个接口 ITableNameHandler
public interface ITableNameHandler {
default String process(MetaObject metaObject, String sql, String tableName) {
String dynamicTableName = this.dynamicTableName(metaObject, sql, tableName);
return null != dynamicTableName && !dynamicTableName.equalsIgnoreCase(tableName) ? sql.replaceAll(tableName, dynamicTableName) : sql;
}
String dynamicTableName(MetaObject metaObject, String sql, String tableName);
}
我就想,既然能动态修改表名,那肯定就能动态修改列名了,所以就可以这样做,
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setLimit(1000);
//增加mysql的关键字处理
String datatype=DataTypeConstant.getDataType();
switch (datatype){
case "mysql":
{
DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
dynamicTableNameParser.setTableNameHandlerMap(new HashMap(2) {{
put("CODE", new ITableNameHandler() {
@Override
public String dynamicTableName(MetaObject metaObject, String sql, String tableName) {
return null;
}
@Override
public String process(MetaObject metaObject, String sql, String tableName) {
if("CODE".equalsIgnoreCase(tableName)){
sql=sql.replaceAll("key","`key`");
sql=sql.replaceAll("value","`value`");
}
return sql;
}
});
}});
paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
}
break;
}
return paginationInterceptor;
}
这样就可以了,如果还有其他数据库或者其他关键字,就可以直接在这里进行添加,在mybatisplus执行sql的时候,就会这里来进行替换掉关键字,然后在执行。
我这主要做兼容,导致才会有后面的问题。如果不需要做兼容, @TableField(value = "`value`")这样是最方便的。
有什么问题疑问,可以来进行探讨。