Mybatis-Plus entry series (20) - compatible with multiple databases

If there is a way but no technique, the technique can still be sought; if there is a technique but no way, it will stop at the technique.

Preface

In the process of actually developing software products, the type of database may not be certain, and some customers may require which database must be used. For example, many government agencies require the use of domestic databases , so we need to adapt to a variety of databases during development.

MySQL, Oracle, PostgreSQL, Dameng and other databases are based on the standards set by the American National Bureau of Standards when performing additions, deletions, modifications, and searches , SQLsuch as SQL-92.SQL-99

However, the actual SQLdifferences between each database manufacturer will be small, that is, the database dialect. The most well-known is MySQLthe use of paging limitand Oraclethe use of paging rownum.

MyBatis-PlusSupports various standard SQLdatabases. Next we will actually demonstrate how to use MyBatis-Plusand adapt to various databases.
Insert image description here

case analysis

1. Pagination

Many databases SQLuse paging in different ways. MyBatis-PlusThe built-in paging plug-in PaginationInnerInterceptorsupports a variety of databases. The official website explains:
Insert image description here

When using the built-in paging plug-in , you can set the type of database:

@Configuration
@MapperScan("com.pearl.pay.mapper") //持久层扫描
@EnableTransactionManagement //启用事务管理
public class MybatisPlusConfig {
    
    
    @Bean
    @ConditionalOnMissingBean(MybatisPlusInterceptor.class)
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
    
    
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }
}

When the built-in paging plug-in is executed SQL, it will obtain the paging dialect based on the current database type .
Insert image description here
In the paging dialect factory classDialectFactory , you can see the specific dialect acquisition logic. Databases such as mysql, mariadb, clickhouse, oceanbaseetc. all use mysqldialects.
Insert image description here
oracle, 达梦数据库using oracledialect:
Insert image description here
MYSQLDatabase paging statement uses LIMITassembly:
Insert image description here
ORACLEDatabase paging statement uses ROWNUM、ROW_IDassembly:
Insert image description here
In summary: When paging, to adapt to multiple databases, you only need to set the database type in the paging plug-in.

2. XML custom SQL

When calling , such as when MPadding API, deleting, modifying, or checking xxMpper.selectList(), because the basic standards are used during MPconstruction , there is generally no compatibility problem. SQLBut when we XMLwrite it ourselves in the file SQL, we need to pay attention to various database matching and compatibility issues.

MybatisIt has already provided multi-database support. You only need to tell the framework which database is used, and different statements can be executed according to different database vendors.

MybatisThe DatabaseIdProvider (Database Vendor Identity Provider) interface declares a method for obtaining the vendor identity. The identity can be used to later build different queries for each database type. This mechanism supports multiple vendors or versions.

public interface DatabaseIdProvider {
    
    

  default void setProperties(Properties p) {
    
    
    // NOP
  }
  // 根据数据源获取数据库厂商标识
  String getDatabaseId(DataSource dataSource) throws SQLException;
}

MybatisVendorDatabaseIdProviderImplementation classes are also provided :

public class VendorDatabaseIdProvider implements DatabaseIdProvider {
    
    
  
  // 支持的数据库厂商(需要自己定义),比如: Oracle=》oracle
  private Properties properties;
  // 获取数据库厂商标识ID(databaseId),eg:oracle
  @Override
  public String getDatabaseId(DataSource dataSource) {
    
    
    if (dataSource == null) {
    
    
      throw new NullPointerException("dataSource cannot be null");
    }
    try {
    
    
      return getDatabaseName(dataSource);
    } catch (Exception e) {
    
    
      LogHolder.log.error("Could not get a databaseId from dataSource", e);
    }
    return null;
  }

  @Override
  public void setProperties(Properties p) {
    
    
    this.properties = p;
  }
  // 根据产品名称,获取对应的databaseId。
  private String getDatabaseName(DataSource dataSource) throws SQLException {
    
    
    String productName = getDatabaseProductName(dataSource);
    if (this.properties != null) {
    
    
      for (Map.Entry<Object, Object> property : properties.entrySet()) {
    
    
        if (productName.contains((String) property.getKey())) {
    
    
          return (String) property.getValue();
        }
      }
      // no match, return null
      return null;
    }
    return productName;
  }
  // 从数据源中获取数据库产品名称,比如: Oracle
  private String getDatabaseProductName(DataSource dataSource) throws SQLException {
    
    
    try (Connection con = dataSource.getConnection()) {
    
    
      DatabaseMetaData metaData = con.getMetaData();
      return metaData.getDatabaseProductName();
    }
  }
}

MybatisWhen XMLwriting in , SQLthere is an databaseIdattribute that can specify which database type the current statement block belongs to, such as:

<mapper namespace="org.pearl.mybatis.demo.dao.UserMapper">
    <select id="selectOneById" resultType="org.pearl.mybatis.demo.pojo.entity.User" databaseId="mysql">
    select * from user where user_id = #{id}
  </select>
</mapper>

To sum up: we only need to configure DatabaseIdProvider which databases are supported in the database, and then XMLwrite query statements for each database dialect in the database, and add databaseIdattributes. MybatisWe will get the type of database used by the data source at startup, and then execute the statement corresponding to the current database configured.

Case presentation

1. Configuration

Building a project, integrating MP, Oracle, Mysqlis very simple, so I won’t go into details here.

In the configuration file, add the corresponding Oracleconnection Mysqladdress:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: oracle.jdbc.OracleDriver
    url: jdbc:oracle:thin:@127.0.0.1:1521:ORCL
    username: root
    password: root
    #driver-class-name: com.mysql.cj.jdbc.Driver
    #url: jdbc:mysql://127.0.0.1:3306/d_account?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    #username: root
    #password: root

Add configuration class in MP:

@Configuration
@MapperScan("com.pearl.pay.mapper") //持久层扫描
@EnableTransactionManagement //启用事务管理
public class MybatisPlusConfig {
    
    
     @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(DataSource dataSource,DatabaseIdProvider databaseIdProvider) throws SQLException {
    
    
        // MP插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 获取当前数据源对应的数据库类型,添加分页插件
        String databaseId = databaseIdProvider.getDatabaseId(dataSource);
        DbType dbType = DbType.getDbType(databaseId);
        paginationInnerInterceptor.setDbType(dbType);
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;
    }

    @Bean
    public DatabaseIdProvider databaseIdProvider() {
    
    
        // 数据库厂商提供者
        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
        Properties p = new Properties();
        p.setProperty("Oracle", "oracle");
        p.setProperty("Mysql", "mysql");
        databaseIdProvider.setProperties(p);
        return databaseIdProvider;
    }
}

2. Simple paging query

Because MyBatis-Plusthe built-in paging plug-in has been adapted, simple (without database dialect) paging query does not need to be adapted by writing your own code.

First add a paging query:

    IPage<User> test(Page<User> page);    
    <select id="test" resultType="com.pearl.entity.User">
        select * from user
    </select>

The data source is configured Oracle, but there is no configuration databaseId. The test SQLstatement is printed as follows:

SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( select * from user) TMP WHERE ROWNUM <=10) WHERE ROW_ID > 0

The data source is configured as follows Mysql, and the test SQLstatement is printed as follows:

select * from user LIMIT 10

3. Paging query with dialects

Requirement : Query data whose time node is less than the current time.

OracleThe function is used at the current time sysdate, and the function Mysqlis used now(). At this time, you need to manually make it compatible.

Write two query statements with the same name, written for different database vendors SQL, and configure the correspondingdatabaseId

    <select id="test" resultType="com.pearl.entity.User" databaseId="mysql">
        select * from user t <![CDATA[ where t.create_time <= now()]]>
    </select>
    
    <select id="test" resultType="com.pearl.entity.User" databaseId="oracle">
        select * from user t  <![CDATA[ where t.create_time  <=  sysdate ]]>
    </select>

You can also use ifstatements to determine the current database type and add different statements (recommended).

    <select id="test" resultType="com.pearl.entity.User">
        select * from user t
        <where>
            <if test="_databaseId == 'mysql'">
                <![CDATA[ AND t.create_time <= now()]]>
            </if>
            <if test="_databaseId == 'oracle'">
                <![CDATA[ AND t.create_time  <=  sysdate ]]>
            </if>
        </where>
    </select>

To switch databases, execute as follows:

select * from user t where t.create_time <= now() LIMIT 10
SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( select * from user t where t.create_time <= sysdate ) TMP WHERE ROWNUM <=10) WHERE ROW_ID > 0

reference

Next, let's briefly understand some of the differences between Oracleand Mysqlto facilitate development.

Data type comparison:

database Comparative item type
MySQL type of data INTEGER、SMALLINT、TINYINT、MEDIUMINT、BIGINT
Oracle type of data number
MySQL date and time date、timestamp、timestamp
Oracle date and time date、timestamp
MySQL Character type char、varchar
Oracle Character type char、varchar、varchar2、nvarchar、nvarchar2
MySQL large field LONGTEXT
Oracle large field clob

Commonly used function comparison:

database Comparative item function
MySQL Get string length char_length(str)
Oracle Get string length length(str)
MySQL Generate random sequence UUID()
Oracle Generate random sequence sys_guid()
MySQL Convert time to string date_format(NOW(),‘%Y-%m-%d’)
Oracle Convert time to string to_char(sysdate, ‘YYYY-MM-DD’)
MySQL Convert string time to time str_to_date(‘2019-01-01’,‘%Y-%m-%d’)
Oracle Convert string time to time to_date(‘2019-01-01’, ‘YYYY-MM-DD’)
MySQL Function conversion containing hours, minutes and seconds date_format(NOW(),‘%Y-%m-%d %H:%i:%s’)
Oracle Function conversion containing hours, minutes and seconds str_to_date(‘2019-01-01’,‘%Y-%m-%d %H:%i:%s’)
MySQL current time now()
Oracle current time sysdate

other:

database Comparative item support
MySQL quotation marks Double quotes and single quotes
Oracle quotation marks Only single quotes are recognized
MySQL String concatenation concat() function
Oracle String concatenation Double vertical bars can be used to connect strings
MySQL Pagination limit
Oracle Pagination rownum

Guess you like

Origin blog.csdn.net/qq_43437874/article/details/129161055