MySQL项目部署Oracle数据源


前言

如何实现MySQL语句转Oracle语句?如何让MySQL项目低成本部署在Oracle数据源上?作者通过AST(抽象语法树)的方式,实现了MySQL到Oracle的适配。


项目源码地址

一、适配点

定义表

CREATE TABLE sachima
(
    id           bigint                    AUTO_INCREMENT PRIMARY KEY,
    name         varchar(255)              NULL,
    is_enabled   bit                       NULL,
    created_date timestamp                 NULL,
    number       int                       NULL,
    size         int                       NULL,
    sex          varchar(20) DEFAULT 'man' NOT NULL,
    remark       longtext                  NULL
);

Keyword Adapt

  • LIMIT ?, ? -> OFFSET ? ROWS FETCH FIRST ? ROWS ONLY
MySQL:  SELECT * FROM sachima LIMIT 1;
Oracle: SELECT * FROM sachima FETCH FIRST 1 ROWS ONLY;
MySQL:  SELECT * FROM sachima LIMIT 500, 10;
Oracle: SELECT * FROM sachima OFFSET 500 ROWS FETCH FIRST 10 ROWS ONLY;
  • ‘[keyword]’ -> “[keyword]”
MySQL:  SELECT number, size FROM sachima;
Oracle: SELECT "NUMBER", "SIZE" FROM sachima;
MySQL:  SELECT id AS number FROM sachima;
Oracle: SELECT id AS "NUMBER" FROM sachima;

Token Adapt

  • " -> ’
MySQL:  SELECT * FROM sachima WHERE id IN ("1", "2", "3");
Oracle: SELECT * FROM sachima WHERE id IN ('1', '2', '3');
  • Remove `
MySQL:  SELECT `name` FROM sachima;
Oracle: SELECT name FROM sachima;
  • Remove AS
MySQL:  SELECT * FROM sachima AS sa;
Oracle: SELECT * FROM sachima sa;

Function Adapt

  • CHAR_LENGTH([column]) -> LENGTH([column])
MySQL:  SELECT CHAR_LENGTH(name) FROM sachima WHERE id = 1;
Oracle: SELECT LENGTH(name) FROM sachima WHERE id = 1;
  • CONCAT(?, ?, ?) -> ? || ? || ?
MySQL:  SELECT CONCAT(name, CONCAT('name', 'is'), id) FROM sachima WHERE id = 1;
Oracle: SELECT (name||('name'||'is')||id) FROM sachima WHERE id = 1;
  • IFNULL([column], ?) -> VAL([column], ?)
MySQL:  SELECT IFNULL(name, ?) FROM sachima;
Oracle: SELECT NVL(name, ?) FROM sachima;
  • FIELD(id, ?, ?, ?) -> DECODE(id, ?, 1, ?, 3, ?, 5)
MySQL:  SELECT * FROM sachima WHERE id IN (3, 2, 1) ORDER BY FIELD(id, 3, 2, 1);
Oracle: SELECT * FROM sachima WHERE id IN (3, 2, 1) DECODE(id, 3, 1, 2, 3, 1, 5);
  • NOW() -> SYSDATE
MySQL:  INSERT INTO sachima (created_date) VALUES (NOW());
Oracle: INSERT INTO sachima (created_date) VALUES (SYSDATE);
  • DATE_FORMAT(created_date, ‘%Y-%m-%d %H:%i:%s’) -> TO_CHAR(created_date, ‘yyyy-mm-dd hh24:mi:ss’)
MySQL:  SELECT DATE_FORMAT(created_date, '%Y-%m-%d %H:%i:%s') FROM sachima;
Oracle: SELECT TO_CHAR(created_date, 'yyyy-mm-dd hh24:mi:ss') FROM sachima;
MySQL:  SELECT DATE_FORMAT(created_date, '%Y-%m-%d') FROM sachima;
Oracle: SELECT TO_CHAR(created_date, 'yyyy-mm-dd') FROM sachima;
  • Remove [BINARY] function.
MySQL:  SELECT * FROM sachima WHERE BINARY (name) = 'ZhangSan';
Oracle: SELECT * FROM sachima WHERE name = 'ZhangSan';
  • LEFT([column], ?) -> SUBSTR([column], 0, ?)
MySQL:  SELECT LEFT(name, 10) FROM sachima;
Oracle: SELECT SUBSTR(name, 0, 10) FROM sachima;
  • YEAR(?) -> TO_CHAR(?, ‘yyyy’)
MySQL:  SELECT LEFT(name, 10) FROM sachima;
Oracle: SELECT SUBSTR(name, 0, 10) FROM sachima;
  • MONTH(?) -> TO_CHAR(?, ‘MM’)
MySQL:  SELECT LEFT(name, 10) FROM sachima;
Oracle: SELECT SUBSTR(name, 0, 10) FROM sachima;
  • IF(LENGTH(name)> ?, ?, ?) ->
MySQL:  SELECT IF(LENGTH(name) > 5, 'true', 'false') FROM sachima;
Oracle: SELECT DECODE(SIGN(LENGTH(name) - 5), 1, 'true', 'false') FROM sachima;
MySQL:  SELECT IF(LENGTH(name) >= 5, 'true', 'false') FROM sachima;
Oracle: SELECT DECODE(SIGN(LENGTH(name) - 5), -1, 'false', 'true') FROM sachima;
MySQL:  SELECT IF(LENGTH(name) < 5, 'true', 'false') FROM sachima;
Oracle: SELECT DECODE(SIGN(LENGTH(name) - 5), -1, 'true', 'false') FROM sachima;
MySQL:  SELECT IF(LENGTH(name) <= 5, 'true', 'false') FROM sachima;
Oracle: SELECT DECODE(SIGN(LENGTH(name) - 5), 1, 'false', 'true') FROM sachima;

Syntax Adapt

  • fit: [42000][1795] ORA-01795: 列表中的最大表达式数为 1000
MySQL:  SELECT * FROM sachima WHERE id IN (1, 2, ... 1000, 1001);
Oracle: SELECT * FROM sachima WHERE (1, id) IN ((1, 1), (1, 2), ... (1, 1000), (1, 1001));
  • ‘’ -> IS NULL
MySQL:  SELECT * FROM sachima WHERE name = '';
Oracle: SELECT * FROM sachima WHERE name IS NULL;
MySQL:  SELECT * FROM sachima WHERE name != '';
Oracle: SELECT * FROM sachima WHERE name IS NOT NULL;
  • IN(‘’) -> IS NULL
MySQL:  SELECT * FROM sachima WHERE name IN ('');
Oracle: SELECT * FROM sachima WHERE name IS NULL;
MySQL:  SELECT * FROM sachima WHERE name NOT IN ('');
Oracle: SELECT * FROM sachima WHERE name IS NOT NULL;
  • IN(NULL) -> IS NULL
MySQL:  SELECT * FROM sachima WHERE name IN (NULL);
Oracle: SELECT * FROM sachima WHERE name IS NULL;
MySQL:  SELECT * FROM sachima WHERE name NOT IN (NULL);
Oracle: SELECT * FROM sachima WHERE name IS NOT NULL;
  • Insert values.
MySQL:
    INSERT INTO
        sachima (id, name)
    VALUES
        (1, 'ZhangSan'),
        (2, 'WangWu');
Oracle:
    INSERT ALL 
        INTO sachima (id, name) VALUES (1, 'ZhangSan')
        INTO sachima (id, name) VALUES (2, 'WangWu')
    SELECT 1 FROM DUAL;
  • Insert [date] value.
MySQL:  INSERT INTO sachima (id, created_date) VALUES (1, '2022-12-22 12:22:12');
Oracle: INSERT INTO sachima (id, created_date) VALUES (1, TO_DATE('2022-12-22 12:22:12', 'yyyy-mm-dd hh24:mi:ss'));
  • Insert remove NULL value item. fix:[23000][1400] ORA-01400: 无法将 NULL 插入 (“SACHIMA”.“SACHIMA”.“SEX”)
MySQL:  INSERT INTO sachima (id, sex) VALUES (1, NULL);
Oracle: INSERT INTO sachima (id) VALUES (1);
MySQL:
    INSERT INTO
        sachima (id, sex)
    VALUES
        (1, NULL),
        (2, 'woman');
Oracle:
    INSERT ALL
        INTO sachima (id) VALUES (1)
        INTO sachima (id, name) VALUES (2, 'woman')
    SELECT 1 FROM DUAL;
  • Nclob binary operator.
    @Before
    public void initDataTypeHandler() {
        ArrayList<ColumnTypeHandler.ColumnType> list = new ArrayList<>();
        list.add(new ColumnTypeHandler.ColumnType("sachima", "remark", "NCLOB"));
        ColumnTypeHandler.apply(list);
    }
MySQL:  SELECT * FROM sachima WHERE remark = 'Hello word.';
Oracle: SELECT * FROM sachima WHERE TO_CHAR(remark) = 'Hello word.';
  • Boolean value.
MySQL:  SELECT * FROM sachima WHERE is_enabled = TRUE AND is_deleted != FALSE;
Oracle: SELECT * FROM sachima WHERE is_enabled = 1 AND is_deleted != 0;
  • Compensate for primary keys.
    @Before
    public void initPrimaryTypeHandler() {
        HashMap<String, String> primaryKeyMap = new HashMap<>();
        primaryKeyMap.put("sachima", "id");
        PrimaryKeyHandler.apply(primaryKeyMap, () -> 1000L);
    }
MySQL:  INSERT INTO sachima (name) VALUES ('ZhangSan');
Oracle: INSERT INTO sachima (name, id) VALUES ('ZhangSan', '1000');
MySQL:  INSERT INTO sachima (id, name) VALUES (1, 'ZhangSan');
Oracle: INSERT INTO sachima (id, name) VALUES (1, 'ZhangSan');
  • Update use JOIN
MySQL:
    UPDATE
        sachima AS sa
        INNER JOIN user AS us ON sa.name = us.name
    SET
      sa.sex    = us.sex,
      sa.remark = 'Update from user'
    WHERE
      sa.id = us.id;
Oracle:
    UPDATE (
        SELECT
            sa.sex sa__sex, us.sex us__sex, sa.remark sa__remark
        FROM 
            sachima sa
            INNER JOIN USER us ON sa.name = us.name
        WHERE sa.id = us.id
    )
    SET sa__sex = us__sex, sa__remark = 'Update from user'
    WHERE 1=1;

二、集成方案

1.集成MyBatis-plus

此处待完善;可参考源码地址:README.md

2.集成Liquibase

此处待完善;可参考源码地址:README.md

3.集成Druid

此处待完善;可参考源码地址:README.md


三、实现思想

四、适配效率

1秒4000+条SQL适配效率

待完善

### 回答1: Spring Boot是一种快速开发框架,可以快速、高效地开发和部署应用程序。而Mybatis Plus是一种基于Mybatis的扩展插件,可以简化Mybatis的开发流程,并提供更多的功能。 Spring Boot和Mybatis Plus整合起来,可以实现对两个不同的数据源MySQLOracle)进行动态支持。具体来说,需要在Spring Boot配置文件中配置对应的数据源,并在Mybatis的配置中设置不同的数据源。 首先,需要在配置文件中定义两个数据源,如下所示: ``` spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.secondary.url=jdbc:oracle:thin:@localhost:1521:ORCL spring.datasource.secondary.username=system spring.datasource.secondary.password=123456 ``` 这里定义了两个数据源,一个是MySQL,一个是Oracle。其中,MySQL数据源配置了url、username和password,而Oracle数据源配置了url、username和password。 然后,在Mybatis的配置中,需要使用动态数据源来支持两个数据源。具体来说,可以通过继承AbstractRoutingDataSource类,并重写determineCurrentLookupKey()方法,来动态切换数据源。如下所示: ``` public class DynamicDataSource extends AbstractRoutingDataSource { private static final ThreadLocal<String> dataSource = new ThreadLocal<String>(); public static void setDataSource(String dataSourceType) { dataSource.set(dataSourceType); } @Override protected Object determineCurrentLookupKey() { return dataSource.get(); } } ``` 这里定义了一个DynamicDataSource类,继承了AbstractRoutingDataSource类。通过设置ThreadLocal变量来指定要使用的数据源。当需要切换数据源时,只需调用setDataSource()方法即可。 最后,在Spring Boot启动类中,需要配置Mapper扫描器,并设置数据源路由规则。如下所示: ``` @SpringBootApplication @MapperScan(basePackages = "com.example.mapper") public class DemoApplication { @Bean public DynamicDataSource dataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map<Object,Object> targetDataSources = new HashMap<>(); targetDataSources.put("mysql", DataSourceBuilder.create().url(mysqlUrl).username(mysqlUsername).password(mysqlPassword).build()); targetDataSources.put("oracle", DataSourceBuilder.create().url(oracleUrl).username(oracleUsername).password(oraclePassword).build()); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(targetDataSources.get("mysql")); return dynamicDataSource; } @Bean public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource); sqlSessionFactoryBean.setTypeAliasesPackage("com.example.entity"); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml")); return sqlSessionFactoryBean.getObject(); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 这里定义了一个DynamicDataSource类,并设置了mysqloracle两个数据源。在SqlSessionFactory中,使用DynamicDataSource来替换默认的数据源。同时,设置Mapper扫描器,以便自动装配Mapper接口。这样就可以在Mybatis中动态切换数据源了。 综上所述,Spring Boot和Mybatis Plus整合起来,可以实现对两个不同的数据源MySQLOracle)进行动态支持,可以满足大多数项目的需求。 ### 回答2: Spring Boot和MyBatis Plus是两个非常常用的Java框架。它们的结合为开发者提供了强大的开发工具和框架,可以帮助他们快速地完成应用程序的开发。同时,使用Spring Boot和MyBatis Plus进行动态支持MySQLOracle数据源整合也非常容易。 首先,我们需要在pom.xml文件中引入MySQLOracle数据库驱动程序的依赖。接着,我们需要在application.properties或者application.yml文件中配置数据源的相关信息。在Spring Boot和MyBatis Plus整合中,我们可以使用一个配置类来装配相关的Bean,如数据源,MyBatis的SqlSessionFactory等。需要注意的是,我们要为每一个数据源创建一个SqlSessionFactory对象,以便MyBatis Plus区分不同的数据源。 当我们完成以上步骤后,我们就可以基于mybatis-plus的动态数据源自动切换使用MySQLOracle数据库了。具体地,我们可以使用mybatis-plus提供的AbstractRoutingDataSource和DetermineDataSourceInterceptor类来实现动态数据源的切换。AbstractRoutingDataSource是一个抽象的数据源实现类,它可以根据key值自动切换对应的数据源。而DetermineDataSourceInterceptor则是一个实现MyBatis拦截器接口的类,它可以拦截MyBatis执行SQL语句的操作,根据业务逻辑动态切换数据源。通过以上两个类的配合,我们可以实现在同一应用中动态切换MySQLOracle数据源。 总之,Spring Boot和MyBatis Plus整合可以为开发者提供了非常方便的开发工具和框架,同时也可以轻松地实现动态支持MySQLOracle数据源整合。这对我们实现业务灵活性和数据资源共享都非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值