僅改2處配置, 令mybatis相容多種資料庫(非專業方案, 源自官方支援)
愛上mybatis是因為自由自在的sql對映, SQL在手, 一切我有的那種感覺!
然而由於其非完整ORM框架原因, 導致我們掌握了SQL的自由, 卻失去了ORM相容多庫的天然特性
本文章告訴你, 如何使 mybatis 輕量支援資料庫相容?
Beacuse of some reason , I write this line , just like because of some reason , you read this line !
溫馨提示: 由於此方法過於簡單粗暴, 所以面世較晚 , 當你的mybatis版本高於3.1時,才可以用哦!
實現方式
第一處
applicationContext.xml or mybatis-config.xml
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd" >
<context:property-placeholder location="classpath*:jdbc.properties" ignore-unresolvable="true"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="databaseIdProvider" ref="databaseIdProvider" />
<property name="configLocation" value="classpath:SqlMapConfig.xml" />
<property name="mapperLocations" value="classpath*:sqlmap/*.xml" />
</bean>
<bean id="vendorProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="Oracle">oracle</prop>
<prop key="MySQL">mysql</prop>
<prop key="DB2">db2</prop>
<prop key="Adaptive Server Enterprise">sybase</prop>
<prop key="SQL Server">sqlserver</prop>
</props>
</property>
</bean>
<bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
<property name="properties" ref="vendorProperties" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xxx" />
<property name="markerInterface" value="com.xxx.SqlMapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
先配置一個vendorProperties, 儲存productName和其對應的別名, productName為key, 別名為value , 然後配置一個databaseIdProvider, 關聯vendorProperties, 使其拿到配置. 最後再sqlSessionFactory中新增
<property name="databaseIdProvider" ref="databaseIdProvider" />
即可
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL MAP Config 3.1//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="callSettersOnNulls" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle" />
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="DB2" />
</databaseIdProvider>
</configuration>
直接加個<databaseIdProvider type="DB_VENDOR">
節點就行了
友情提示, 使用mybatis-config.xml方式時, 必須確保改配置檔案中定義了environments及其transactionManager和dataSource,否則databaseIdProvider將不生效, 也就是說, 使用spring管理mybatis時, 此方式失效!
第二處
UserMapper.xml
<?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.xxx.UserMapper">
<resultMap id="result" type="com.xxx.UserModel"/>
<select id="findAll" resultMap="result" databaseId="mysql">
SELECT *
FROM users
ORDER BY id ASC
</select>
<select id="findAll" resultMap="result" databaseId="oracle">
SELECT *
FROM users
ORDER BY id ASC
</select>
</mapper>
經過本人實測, mybatis查詢 statement 的邏輯是 : 先找有databaseId的, 是否有對應的, [1] 若有, 則直接呼叫(友情提示:彆強行寫2個相同的databaseId且statement id也相同), [1] 若無, 則查詢是否有 未指定databaseId的 statement , [2] 若有, 則呼叫, [2] 再沒有, 就報錯了 !
也就是說, 你完全可以以一種資料庫為主, 如MySQL為主, Oracle則是某些模組使用, 即寫xml時, 一般都直接不寫databaseId, 唯有Oracle那個模組才寫上 databaseId=”oracle” .比如樓主公司, 就是這種情況! 而另一種情況則是, 整個專案都需要相容, 那修改量就相對大一些, 需要2個statement分別標明不同的databaseId.
原理簡單剖析
經過本人不辭辛苦的檢視原始碼: SqlSessionFactoryBean, 對應的databaseIdProvider程式碼, 最終發現這麼一段程式碼:
Environment var29 = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(var29);
if(this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException var22) {
throw new NestedIOException("Failed getting a databaseId", var22);
}
}
這段程式碼是將dataSource傳給databaseIdProvider,讓databaseIdProvider獲取databaseId,並設定到mybatis中!
下面看databaseIdProvider如何獲取databaseId
private String getDatabaseProductName(DataSource dataSource) throws SQLException {
Connection con = null;
String var4;
try {
con = dataSource.getConnection();
DatabaseMetaData metaData = con.getMetaData();
var4 = metaData.getDatabaseProductName();
} finally {
if(con != null) {
try {
con.close();
} catch (SQLException var11) {
;
}
}
}
return var4;
}
原來最終時通過connection.getMetaData.getDatabaseProductName()方法得到一個東東,就是配置中properties的 key , 然後看下面這段程式碼:
private String getDatabaseName(DataSource dataSource) throws SQLException {
String productName = this.getDatabaseProductName(dataSource);
if(this.properties != null) {
Iterator i$ = this.properties.entrySet().iterator();
Entry property;
do {
if(!i$.hasNext()) {
return null;
}
property = (Entry)i$.next();
} while(!productName.contains((String)property.getKey()));
return (String)property.getValue();
} else {
return productName;
}
}
根據connection獲得了當前資料來源真實的productName, 如MySQL, Oracle .而後通過配置進來的properties判斷是否包含這個 key , 若有進行返回! 無則直接返回productName !
以上的原始碼都是分析databaseId時如何獲取, 如何被設定到mybatis中的, 至於mybatis如何根據databaseId判斷對應的statement, 那還不是小意思, 不過本人小白一個, 沒有看過mybatis原始碼, 對其不熟悉, 要找這塊原始碼太費時間, 浮躁的社會我也不能倖免 !