Using mybatis 3.2.8 ,Spring 3.2.9 release,mybatis-spring 1.2.2 to configure my project's DAO and the project need to convenient both mysql and oracle so i use the databaseIdProvider to adapt multi-datasource here is my configuration:
- datasource
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>xxxxx</value>
</property>
</bean>
- vendorProperties
<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>
because mysql's paging is different from oracle's so i wrote two ways of paging and depends on runtime environment to chose to use which.here is the 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>
and use it like this:
<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>
the above is all my configuration. when i am running the query operation or any other DML,the console will print:
org.apache.ibatis.builder.IncompleteElementException: Could not find SQL statement to include with refid 'xxx.xxx.xxx.PagePrefix'
i find this exception was caused by org.apache.ibatis.session.Configuration.sqlFragments did not had the value that the key is 'PagePrefix'. so i following the blow steps to find what is wrong:
- my local environment is oracle,so i modified the mapper.xml, removed the mysql config just left the oracle config:
<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>
but it's also not work,the exception still remained.
- then i thought maybe the mybatis did not recognized my runtime environment,so i looked into the source code to find out the whether the information was set into the mybatis or not.i find the startup class
org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory()
method has:
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
and i toggled breakpoint in here and find the value was right.
- Then i started to find where and when the sqlFragments was assigned value.i found that sqlFragments was assigned when parsing the mapper.xml:
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);
}
}
and the method sqlElement
do the actual assign value on sqlFragments and according the databaseId to find the tag ''
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);
}
}
but in that time the databaseId hasn't been set yet beacuse the parsing phase is before the setDatabaseId operation what i mentioned before. Now i have no idea how to solve this problem.does any people encountered the same situation like me?
Aucun commentaire:
Enregistrer un commentaire