Mybatis配置文件之配置元素解析
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载不带 databaseId 属性和带有匹配当前数据库 databaseId 属性的所有语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。
这里什么意思呢 ,如果你仔细看过mybatis的官方文档中关于XML映射文件章节中的 select
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10000"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">
下面的解释中有一个databaseId属性: 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。新增,修改和删除都有这个属性。例如mysql的获取系统时间函数 NOW() 和oracle的获取系统时间to_char(sysdate,’yyyy-mm-dd hh24:mi:ss’) 是不同的。那么我们可以针对同一个修改可以写两个update语句,他们的databaseId属性不一样。
再比如数据库数据分页见MyBatis 入门(六)–分页查询(1)和MyBatis 入门(六)–分页查询(2) -插件方式他们的语句就是不一样,就可以利用这种模式获取当前数据,再做不同的处理。
为支持多厂商特性只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:
配置
<databaseIdProvider type="DB_VENDOR" />
这里的 DB_VENDOR 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置 见JDBC-基础。 由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短,如下:
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>
在有 properties 时,DB_VENDOR databaseIdProvider 的将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。
你可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider 并在 mybatis-config.xml 中注册来构建自己的 DatabaseIdProvider:
public interface DatabaseIdProvider {
void setProperties(Properties p);
String getDatabaseId(DataSource dataSource) throws SQLException;
}
应用时可以直接通过Configuration的getDatabaseId方法来获取当前数据的供应商。
1. 使用系统默认的规则
mybatis提供一些系统默认的配置规则,如下代码:
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="Oracle" value="oracle"/>
<property name="MySQL" value="mysql"/>
<property name="DB2" value="db2"/>
</databaseIdProvider>
其中type=”DB_VENDOR”的功能是启用mybatis内部注册的策略器,首先mybatis会将配置读入到Configuration类里面,在链接数据库后调用getDatabaseProductName方法获取数据库的信息,方法如下:
private String getDatabaseProductName(DataSource dataSource) throws SQLException {
Connection con = null;
try {
con = dataSource.getConnection();
DatabaseMetaData metaData = con.getMetaData();
return metaData.getDatabaseProductName();
} finally {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
// ignored
}
}
}
}
在上面的方法获取到数据库信息后就用我们配置的name值去做匹配来得到databaseId。我们把这些配置到我们的示例里,因为我们的示例使用的是MySql数据库,这个时如果我们用下面的代码来获取数据库ID的话:
sessionFactory.getConfiguration().getDatabaseId()
则获取的结果为:MySQL。
同样我们也可以指定SQL的数据库执行厂商,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yhl.mybatis.mapper.UserMapper">
<select id="findUserById" resultType="com.yhl.mybatis.model.User" parameterType="java.lang.Long" databaseId="mysql">
select id,user_name as userName,age from t_user where id = #{id,jdbcType=BIGINT}
</select>
上述代码的select中多了一个databaseId属性,这样Mybatis将提供一下规则:
(1)如果没有配置databaseIdProvider,那么databaseId将返回null
(2)如果配置了databaseIdProvider,那么mybatis会使用配置的name去匹配数据库信息,如果匹配上了就返回databaseId,如果没有则还是返回null
(3)如果Configuration的databaseId不为空,则mybatis只会找到配置databaseId的SQL语句
(4)Mybatis会同时加载带不带databaseId属性和匹配的当前数据库的databaseId属性的所有语句,如果同时找到两种语句,则不带databaseId属性的将被抛弃。
2. 使用自定义的规则
mybatis也提供允许自定义规则,只要我们实现databaseIdProvider接口即可,并且实现配置即可,接下来我们看一个简单的实例,首先我们定义好自定义规则类,代码如下:
public class MyDatabaseIdProvider implements DatabaseIdProvider {
private Properties properties=null;
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String getDatabaseId(DataSource dataSource) throws SQLException {
String dbName = dataSource.getConnection().getMetaData().getDatabaseProductName();
String dbId = (String)this.properties.get(dbName);
return dbId;
}
}
然后再配置文件中注册我们的这个provider,如下:
<databaseIdProvider type="com.yhl.mybatis.databaseid.MyDatabaseIdProvider">
<property name="Oracle" value="oracle"/>
<property name="MySQL" value="mysql"/>
<property name="DB2" value="db2"/>
</databaseIdProvider>
这样跟使用默认规则基本一致。
3. mysql与oracle两种实例
在这里我们就实现mysql的获取系统时间函数 NOW() 和oracle的获取系统时间to_char(sysdate,’yyyy-mm-dd hh24:mi:ss’) 的的执行。
首先配置 mybatis-config.xml
<properties resource="jdbc.properties" />
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql" />
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<mappers>
<package name="com.yhl.mybatis.mapper"/>
</mappers>
接口和mapperXML文件
package com.yhl.mybatis.mapper;
public interface TimeMapper {
String selectTime();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yhl.mybatis.mapper.TimeMapper">
<select id="SelectTime" resultType="String" databaseId="mysql">
SELECT NOW() FROM dual
</select>
<select id="SelectTime" resultType="String" databaseId="oracle">
SELECT 'oralce'||to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') FROM dual
</select>
</mapper>
上面的mapper文件具有相同的id,但是他们的databaseId分别是mysql和oralce。
jdbc.properties文件
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://127.0.0.1:3306/my_mybatis?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username = yhl
password = 1234
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:my_mybatis
username=yhl
password=1234
maxActive= 50
这个配置文件以及注销了mysql的链接,启用的是oralce的链接
测试类
public class Application {
public static void main(String[] args) {
SqlSession session = MybatisUtils.getSqlSession();
TimeMapper timeMapper = session.getMapper(TimeMapper.class);
timeMapper.selectTime();
}
}
结果说明
如果当前启用的是oracle则执行databaseId=”oracle”的语句,如果mysql值执行databaseId=”mysql”的语句