MyBatis 多数据源配置问题
MyBatis multi-datasource configuration issue
使用mybatis 3.2.8,Spring 3.2.9 release,mybatis-spring 1.2.2配置我项目的DAO和项目需要方便的都mysql 和 oracle 所以我使用 databaseIdProvider 来适应多数据源这是我的配置:
- 数据源
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>xxxxx</value>
</property>
</bean>
- 供应商属性
<bean id="vendorProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="SQL Server">sqlserver</prop>
<prop key="DB2">db2</prop>
<prop key="Oracle">oracle</prop>
<prop key="MySQL">mysql</prop>
<prop key="H2">h2</prop>
</props>
</property>
</bean>
- databaseIdProvider
<bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
<property name="properties" ref="vendorProperties" />
</bean>
- sqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="databaseIdProvider" ref="databaseIdProvider" />
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>
因为mysql的分页和oracle的分页不同所以我写了两种分页方式根据运行环境选择使用which.here是mapper.xml:
<sql databaseId="oracle" id="PagePrefix">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<if test="page != null">
select * from ( select row_.*, rownum rownum_ from (
</if>
</sql>
<sql databaseId="mysql" id="PagePrefix">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<if test="page != null">
select * from (
</if>
</sql>
并像这样使用它:
<select id="selectByExample" parameterType="xxx.xxx.xxx" resultMap="BaseResultMap">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<include refid="PagePrefix" />select
<if test="distinct">
distinct
</if>
from xxx
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<include refid="PageSuffix" />
</select>
以上就是我的全部配置。
当我在 运行 查询操作或任何其他 DML 时,控制台将打印:
org.apache.ibatis.builder.IncompleteElementException: Could not find SQL statement to include with refid 'xxx.xxx.xxx.PagePrefix'
我发现这个异常是由于 org.apache.ibatis.session.Configuration.sqlFragments 没有键值 'PagePrefix' 引起的。
所以我按照打击步骤找出问题所在:
- 我的本地环境是oracle,所以我修改了mapper.xml,删除了mysql配置,只剩下oracle配置:
<sql databaseId="oracle" id="PagePrefix">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<if test="page != null">
select * from ( select row_.*, rownum rownum_ from (
</if>
</sql>
还是不行,异常依旧
- 然后我想可能是mybatis没有识别我的运行环境,所以我查看了源代码,看看是mybatis设置了信息还是not.i找到了启动 class
org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory()
方法有:
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
我在这里切换断点,发现值是正确的。
- 然后我开始查找sqlFragments被分配的位置和时间value.i发现在解析mapper.xml时分配了sqlFragments:
org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement()
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
和方法sqlElement
对sqlFragments进行实际赋值并根据databaseId找到标签''
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) sqlFragments.put(id, context);
}
}
但那时还没有设置 databaseId,因为解析阶段在我之前提到的 setDatabaseId 操作之前。
现在我不知道如何解决这个问题problem.does有没有人遇到过和我一样的情况?
我设法创建了一个工作示例。诀窍是为属性获取正确的地图。您必须能够指定
DataSource().getConnection().getMetaData().getDatabaseProductName()
returns 并将其用作 属性 文件中的密钥
我的 postgres 映射是
<bean id="vendorProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="PostgreSQL">postgres</prop>
<prop key="Oracle">oracle</prop>
</props>
</property>
</bean>
PostgresSQL 是
的结果
DataSource().getConnection().getMetaData().getDatabaseProductName()
在我的映射文件中
<sql databaseId="postgres" id="PagePrefix">
select sum(a) from (
</sql>
并且已正确加载
我发现问题是导致我在 mybatis-config.xml 中编写了映射器标签,这样 mybatis 将进行自己的配置,尽管 Spring 的 configure.so 如果你想通过 Spring 使用多数据源,你不能写 mybatis 自己的配置 xml.
使用mybatis 3.2.8,Spring 3.2.9 release,mybatis-spring 1.2.2配置我项目的DAO和项目需要方便的都mysql 和 oracle 所以我使用 databaseIdProvider 来适应多数据源这是我的配置:
- 数据源
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>xxxxx</value>
</property>
</bean>
- 供应商属性
<bean id="vendorProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="SQL Server">sqlserver</prop>
<prop key="DB2">db2</prop>
<prop key="Oracle">oracle</prop>
<prop key="MySQL">mysql</prop>
<prop key="H2">h2</prop>
</props>
</property>
</bean>
- databaseIdProvider
<bean id="databaseIdProvider" class="org.apache.ibatis.mapping.VendorDatabaseIdProvider">
<property name="properties" ref="vendorProperties" />
</bean>
- sqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="databaseIdProvider" ref="databaseIdProvider" />
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>
因为mysql的分页和oracle的分页不同所以我写了两种分页方式根据运行环境选择使用which.here是mapper.xml:
<sql databaseId="oracle" id="PagePrefix">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<if test="page != null">
select * from ( select row_.*, rownum rownum_ from (
</if>
</sql>
<sql databaseId="mysql" id="PagePrefix">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<if test="page != null">
select * from (
</if>
</sql>
并像这样使用它:
<select id="selectByExample" parameterType="xxx.xxx.xxx" resultMap="BaseResultMap">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<include refid="PagePrefix" />select
<if test="distinct">
distinct
</if>
from xxx
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<include refid="PageSuffix" />
</select>
以上就是我的全部配置。 当我在 运行 查询操作或任何其他 DML 时,控制台将打印:
org.apache.ibatis.builder.IncompleteElementException: Could not find SQL statement to include with refid 'xxx.xxx.xxx.PagePrefix'
我发现这个异常是由于 org.apache.ibatis.session.Configuration.sqlFragments 没有键值 'PagePrefix' 引起的。 所以我按照打击步骤找出问题所在:
- 我的本地环境是oracle,所以我修改了mapper.xml,删除了mysql配置,只剩下oracle配置:
<sql databaseId="oracle" id="PagePrefix">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<if test="page != null">
select * from ( select row_.*, rownum rownum_ from (
</if>
</sql>
还是不行,异常依旧
- 然后我想可能是mybatis没有识别我的运行环境,所以我查看了源代码,看看是mybatis设置了信息还是not.i找到了启动 class
org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory()
方法有:
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
我在这里切换断点,发现值是正确的。
- 然后我开始查找sqlFragments被分配的位置和时间value.i发现在解析mapper.xml时分配了sqlFragments:
org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement()
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
和方法sqlElement
对sqlFragments进行实际赋值并根据databaseId找到标签''
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) sqlFragments.put(id, context);
}
}
但那时还没有设置 databaseId,因为解析阶段在我之前提到的 setDatabaseId 操作之前。 现在我不知道如何解决这个问题problem.does有没有人遇到过和我一样的情况?
我设法创建了一个工作示例。诀窍是为属性获取正确的地图。您必须能够指定
DataSource().getConnection().getMetaData().getDatabaseProductName()
returns 并将其用作 属性 文件中的密钥 我的 postgres 映射是
<bean id="vendorProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="PostgreSQL">postgres</prop>
<prop key="Oracle">oracle</prop>
</props>
</property>
</bean>
PostgresSQL 是
的结果DataSource().getConnection().getMetaData().getDatabaseProductName()
在我的映射文件中
<sql databaseId="postgres" id="PagePrefix">
select sum(a) from (
</sql>
并且已正确加载
我发现问题是导致我在 mybatis-config.xml 中编写了映射器标签,这样 mybatis 将进行自己的配置,尽管 Spring 的 configure.so 如果你想通过 Spring 使用多数据源,你不能写 mybatis 自己的配置 xml.