多数据库支持
bind 标签并不能解决更换数据库带来的所有问题,那么还可以通过什么方式支持不同的数据库呢?这需要用到 if 标签以及由 MyBatis 提供的 databaseIdProvider 数据库厂商标识配置。
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性的。MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 配置即可。
<databaseIdProvider type="DB_VENDOR"/>
这里的 DB_VENDOR 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置。由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以通常会通过设置属性别名来使其变短,代码如下。
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
<property name="MySQL" value="mysql" />
<property name="PostgreSQL" value="postgresql" />
<property name="Derby" value="derby" />
<property name="HSQL" value="hsqldb" />
<property name="H2" value="h2" />
</databaseIdProvider>
上面列举了常见的数据库产品名称,在有 property 配置时,databaseId 将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性则会被设置为null。在这个例子中,如果 getDatabaseProductName() 返回Microsoft SQL Server,databaseId 将被设置为 sqlserver。
DB_VENDOR 的匹配策略为,DatabaseMetaData#getDatabaseProductName() 返回的字符串包含 property 中 name 部分的值即可匹配,所以虽然 SQL Server 的产品全名一般为 Microsoft SQL Server,但这里只要设置为 SQL Server 就可以匹配。 |
数据库产品名一般由所选择的当前数据库的JDBC驱动所决定,只要找到对应数据库 DatabaseMetaData 接口的实现类,一般在 getDatabaseProductName() 方法中就可以直接找到该值。任何情况下都可以通过调用 DatabaseMetaData#getDatabaseProductName() 方法获取具体的值。
除了增加上面的配置外,映射文件也是要变化的,关键在于以下几个映射文件的标签中含有的 databaseId 属性。
-
select
-
insert
-
delete
-
update
-
selectKey
-
sql
举一个简单例子来介绍 databaseId 属性如何使用。针对 MySQL 和 Oracle 数据库提供下面两个不同版本的 like 查询方法。
<select id="selectByUser" databaseId="mysql" resultType="tk.mybatis.simple.model.SysUser">
select * from sys_userand where user_name like concat('%', #{userName}, '%')
</select>
<select id="selectByUser" databaseId="oracle" resultType="tk.mybatis.simple.model.SysUser">
select * from sys_userand where user_name like '%'||#{userName}||'%'
</select>
当基于不同数据库运行时,MyBatis 会根据配置找到合适的 SQL 去执行,用法就是这么简单。
数据库的更换可能只会引起某个 SQL 语句的部分不同,所以也没有必要使用上面的写法,而可以使用 if 标签配合默认的上下文中的 _databaseId 参数这种写法去实现。这样可以避免大量重复的 SQL 出现,方便修改。
selectByUser 方法可以修改如下。这样就可以针对局部来适配不同的数据库了。
<select id="selectByUser" resultMap="baseResultMap">
select
<include refid="baseColumnQuery"/>
from sys_user
<where>
<if test="userName != null and userName != ''">
<if test="_databaseId == 'mysql'">
and user_name like concat('%',#{userName},"%")
</if>
<if test="_databaseId == 'oracle'">
and user_name like '%'||#{userName}||'%'
</if>
</if>
<if test="userEmail != null and userEmail != ''">
and user_email = #{userEmail}
</if>
</where>
</select>