系统mysql、sqlserver数据库兼容方案
一、技术选型
springboot2.4+、mybatisplus3.4+、mysql5.7+、redis3.0+
二、编写目的
如果产品开发默认数据库采用mysql,但是当客户提出数据库需要采用Sqlserver或者Oracle以及其他国产数据库时,程序中就不能出现方言性的sql语句,否则程序在个性化的sql上做不到数据库兼容,因此要想产品能兼容多种数据类型,则在编码时就要考虑将个性化的方言sql抽离出来。
三、数据库兼容方案
实现思路:
(1)应用启动时要根据数据源知道当前链接的数据库类型,并存到全局变量
(2)根据数据库类型自动设置mybatis-plus分页方言
(3)个性化方言实现不要在主业务中耦合(如:if mysql … else if sqlserver…)
(4)个性化方言实现可以多种数据库类型复用(如mysql、mariadb可以用一种实现)
(5)个性化方言实现类能根据数据库类型自动注入
代码实现:
根据url获取数据库类型
import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;public class Dialect { private static DbType dbType = null; private static final String DB_URL = "spring.datasource.url"; public synchronized static DbType getDbType(){ if(dbType == null){ String url = PropUtil.getValue(DB_URL); dbType = JdbcUtils.getDbType(url); } return dbType; }}
自定义注解(实现方言实现的选择注入)
import com.baomidou.mybatisplus.annotation.DbType;import org.springframework.context.annotation.Conditional;import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE, ElementType.METHOD })@Documented@Conditional(DbTypeCondition.class)public @interface ConditionalOnDbType { DbType[] types() default DbType.OTHER;}
注解条件实现
import com.baomidou.mybatisplus.annotation.DbType;import org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.type.AnnotatedTypeMetadata;public class DbTypeCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { DbType[] dbTypes = (DbType[]) metadata.getAnnotationAttributes(ConditionalOnDbType.class.getName()).get("types"); for(DbType dbType : dbTypes){ if(Dialect.getDbType().equals(dbType)){ return true; } } return false; }}
1、mybatisplus配置
分页方言配置
@Configurationpublic class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //添加分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(Dialect.getDbType())); return interceptor; }}
2、数据库YAML配置
数据源配置上注意驱动和URL的替换。
mysql配置
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://ip:3306/db?serverTimezone=Asia/Shanghai&useUnicode=true username: username password: password hikari: ## 最小空闲连接数 minimum-idle: 5 ## 最大连接数 maximum-pool-size: 20 ## 自动提交 auto-commit: true ## 连接池名称 pool-name: TmcHikariCP ## 超时时间(ms) connection-timeout: 30000
sqlserver配置
spring: datasource: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://ip:1433;databasename=db_schema;trustServerCertificate=true;integratedSecurity=false; username: username password: password hikari: ## 最小空闲连接数 minimum-idle: 5 ## 最大连接数 maximum-pool-size: 20 ## 自动提交 auto-commit: true ## 连接池名称 pool-name: TmcHikariCP ## 超时时间(ms) connection-timeout: 30000
3、应用举例
方言抽离接口
public interface DialectService<T> { void conditon(QueryWrapper<T> queryWrapper, Map<String, Clazz> attrMap,Object k,Object v);}
Mysql方言实现
import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.util.Map;@Slf4j@Service@ConditionalOnDbType(types = {DbType.MYSQL,DbType.MARIADB})public class MysqlFormDataDialectServiceImpl implements FormDataDialectService<FormDataEntity> { @Override public void conditon(QueryWrapper<FormDataEntity> queryWrapper, Map<String, Clazz> attrMap, Object k, Object v) { try { //替换时间类型判断(时间查询为时间段) Clazz.Format format = attrMap.get(k).getFormat(); if(Clazz.Format.DateTime.equals(format) || Clazz.Format.Date.equals(format)){ String[] dateArray = ObjectMapperHelper.mapper().readValue(v.toString(), String[].class); queryWrapper.ge("attr_json->'$." + k + "'", dateArray[0]); queryWrapper.lt("attr_json->'$." + k + "'", dateArray[1]); }else { queryWrapper.eq("attr_json->'$." + k + "'", v); } }catch (Exception e){ log.error("form data query k:{},v:{} error:{}",k,v,e.getMessage()); } }}
Sqlserver方言实现
import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.util.Map;@Slf4j@Service@ConditionalOnDbType(types = {DbType.SQL_SERVER2005,DbType.SQL_SERVER})public class SqlServerFormDataDialectServiceImpl implements FormDataDialectService<FormDataEntity> { @Override public void conditon(QueryWrapper<FormDataEntity> queryWrapper, Map<String, Clazz> attrMap, Object k, Object v) { try { //替换时间类型判断(时间查询为时间段) Clazz.Format format = attrMap.get(k).getFormat(); if(Clazz.Format.DateTime.equals(format) || Clazz.Format.Date.equals(format)){ String[] dateArray = ObjectMapperHelper.mapper().readValue(v.toString(), String[].class); queryWrapper.ge("JSON_VALUE(attr_json, '$." + k + "')", dateArray[0]); queryWrapper.lt("JSON_VALUE(attr_json, '$." + k + "')", dateArray[1]); }else { queryWrapper.eq("JSON_VALUE(attr_json, '$." + k + "')", v); } }catch (Exception e){ log.error("form data query k:{},v:{} error:{}",k,v,e.getMessage()); } }}
4、数据库脚本转换
最后注意数据库脚本从mysql到sqlserver的转换,可以用微软的工具Microsoft SQL Server Migration Assistant for MySQL,
可参照官网实现。
下载链接:https://www.microsoft.com/en-us/download/details.aspx?id=54257&6B49FDFB-8E5B-4B07-BC31-15695C5A2143=1
来源地址:https://blog.csdn.net/little_pig_lxl/article/details/130485475
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341