4.6 多数据库支持

bind标签并不能解决更换数据库带来的所有问题,那么还可以通过什么方式支持不同的数据库呢?这需要用到if 标签以及由MyBatis提供的databaseIdProvider数据库厂商标识配置。

MyBatis可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的databaseId属性的。

  • MyBatis会加载不带databaseId属性带有匹配当前数据库databaseId属性的所有语句

  • 如果同时找到带有databaseId和不带databaseId的相同语句,则后者会被舍弃。

为支持多厂商特性,只要像下面这样加入databaseIdProvider配置即可。

private static final Properties DB_VENDOR = new Properties();
static {
  DB_VENDOR.put("SQL Server", "sqlserver");
  DB_VENDOR.put("DB2", "db2");
  DB_VENDOR.put("Oracle", "oracle");
  DB_VENDOR.put("MySQL", "mysql");
  DB_VENDOR.put("PostgreSQL", "postgresql");
  DB_VENDOR.put("Derby", "derby");
  DB_VENDOR.put("HSQL", "hsqldb");
  DB_VENDOR.put("H2", "h2");
}

VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
databaseIdProvider.setProperties(DB_VENDOR);
sqlSessionFactoryBean.setDatabaseIdProvider(databaseIdProvider);

上面列举了常见的数据库产品名称,在存在匹配项时,databaseId将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性则会被设置为null。在这个例子中,如果getDatabaseProductName()返回Microsoft SQL Server,databaseId将被设置为sqlserver。

DB_VENDOR的匹配策略为部分匹配(使用String.contains方法进行判断),所以虽然SQL Server的产品全名一般为Microsoft SQL Server,但这里只要设置为SQL Server就可以匹配。

数据库产品名一般由所选择的当前数据库的JDBC驱动所决定(DatabaseMetaData从connection对象中获取),只要找到对应数据库DatabaseMetaData接口的实现类,一般在getDatabaseProductName()方法中就可以直接找到该值。任何情况下都可以通过调用DatabaseMetaData#getDatabaseProductName()方法获取具体的值。

除了增加上面的配置外,映射文件也是要变化的,关键在于以下几个映射文件的标签中含有的databaseId属性:select、insert、delete、update、selectKey、sql。

<select id="selectByName" resultMap="BaseResultMap" databaseId="mysql">
  select 
  	ID, `NAME`, CODE
  from country
  where 
  	`NAME` like = concat('%', #{name}, '%')
</select>

<select id="selectByName" resultMap="BaseResultMap" databaseId="oracle">
  select 
  	ID, `NAME`, CODE
  from country
  where 
  	`NAME` like = '%'||#{name}||'%'
</select>

当基于不同数据库运行时,MyBatis会根据配置找到合适的SQL去执行。

数据库的更换可能只会引起某个SQL语句的部分不同,所以也没有必要使用上面的写法,而可以使用 if 标签配合默认的上下文中的_databaseId参数这种写法去实现。这样可以避免大量重复的SQL出现,方便修改。

<select id="selectByName" resultMap="BaseResultMap" databaseId="oracle">
  select 
  	ID, `NAME`, CODE
  from country
  <where>
    <if test="_databaseId == 'mysql'">
      `NAME` like = concat('%', #{name}, '%')
    </if>
    <if test="_databaseId == 'oracle'">
      `NAME` like = '%'||#{name}||'%'
    </if>
  </where>
</select>

Last updated