插件窝 干货文章 MyBatis 中如何利用数据库厂商标识实现动态 SQL 语句?

MyBatis 中如何利用数据库厂商标识实现动态 SQL 语句?

数据库 select databaseId DatabaseIdProvid 50    来源:    2025-03-31

MyBatis 中利用数据库厂商标识实现动态 SQL

MyBatis 提供了数据库厂商标识(DatabaseIdProvider)功能,允许你根据不同的数据库厂商编写特定的 SQL 语句。这在需要支持多数据库的项目中非常有用。

实现步骤

1. 配置 DatabaseIdProvider

首先需要在 MyBatis 配置文件中配置 DatabaseIdProvider:

<configuration>
    <databaseIdProvider type="VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <property name="PostgreSQL" value="postgresql"/>
        <property name="SQL Server" value="sqlserver"/>
    </databaseIdProvider>
</configuration>

2. 在映射文件中使用 databaseId 属性

在 SQL 映射文件中,可以为不同数据库编写特定的 SQL 语句:

<select id="selectUser" resultType="User" databaseId="mysql">
    SELECT * FROM user LIMIT #{offset}, #{limit}
</select>

<select id="selectUser" resultType="User" databaseId="oracle">
    SELECT * FROM (
        SELECT a.*, ROWNUM rn FROM (
            SELECT * FROM user
        ) a WHERE ROWNUM <= #{end}
    ) WHERE rn >= #{start}
</select>

<select id="selectUser" resultType="User" databaseId="postgresql">
    SELECT * FROM user OFFSET #{offset} LIMIT #{limit}
</select>

3. 动态 SQL 中使用 _databaseId 变量

在动态 SQL 中,可以使用内置的 _databaseId 变量来编写条件分支:

<select id="selectUser" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="_databaseId == 'mysql'">
            LIMIT #{offset}, #{limit}
        </if>
        <if test="_databaseId == 'oracle'">
            AND ROWNUM <= #{end}
        </if>
    </where>
    <if test="_databaseId == 'oracle'">
        OFFSET #{start} ROWS
    </if>
</select>

高级用法

1. 自定义 DatabaseIdProvider

如果需要更复杂的逻辑,可以实现自己的 DatabaseIdProvider:

public class CustomDatabaseIdProvider implements DatabaseIdProvider {
    @Override
    public void setProperties(Properties properties) {
        // 初始化配置
    }

    @Override
    public String getDatabaseId(DataSource dataSource) {
        // 根据数据源返回数据库标识
        try (Connection conn = dataSource.getConnection()) {
            String dbName = conn.getMetaData().getDatabaseProductName();
            if ("MySQL".equals(dbName)) {
                return "mysql";
            } else if ("Oracle".equals(dbName)) {
                return "oracle";
            }
            // 其他数据库...
        } catch (SQLException e) {
            throw new RuntimeException("无法获取数据库标识", e);
        }
        return null;
    }
}

2. 注解方式使用

在注解方式的 Mapper 中也可以使用数据库厂商标识:

public interface UserMapper {
    @SelectProvider(type = UserSqlProvider.class, method = "selectUser")
    List<User> selectUsers(User user);
}

public class UserSqlProvider {
    public String selectUser(User user) {
        return new SQL() {{
            SELECT("*");
            FROM("user");
            if (user.getName() != null) {
                WHERE("name = #{name}");
            }
            if ("mysql".equals(getDatabaseId())) {
                LIMIT("#{offset}, #{limit}");
            }
        }}.toString();
    }

    // 获取当前数据库标识
    private String getDatabaseId() {
        return Configuration.getDatabaseId();
    }
}

注意事项

  1. 如果没有匹配的 databaseId,MyBatis 会使用不带 databaseId 属性的语句
  2. 确保所有数据库厂商的 SQL 语句在功能上保持一致
  3. 测试时需要在不同数据库环境下进行全面测试
  4. 对于复杂的多数据库支持,可能需要结合其他动态 SQL 功能一起使用

通过合理使用数据库厂商标识,可以大大简化多数据库支持的开发工作。