修改 ibatis 分页机制(ORACLE物理分页)
针对ibatis 分页机制的修改例子网上也有很多,本人也是参考后做的修改.如下:
1、首先增加配置文件
- <bean id="sqlExecutor" class="com.ibatis.sqlmap.engine.execution.XSqlExecutor">
- <property name="dialect">
- <bean class="com.ibatis.sqlmap.engine.imp.DialectImp" />
- </property>
- <property name="enableLimit">
- <value>true</value>
- </property>
- </bean>
此类是继承地层分页代码从而实现修改SQL语句
- package com.ibatis.sqlmap.engine.execution;
- import java.sql.Connection;
- import java.sql.SQLException;
- import com.ibatis.sqlmap.engine.inter.Dialect;
- import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;
- import com.ibatis.sqlmap.engine.scope.StatementScope;
- /**
- *
- * @author chenyanji
- *
- * @project Vstsoft newsx code.
- * @datetime Mar 15, 2009 12:07:18 AM
- */
- public class XSqlExecutor extends SqlExecutor{
- private Dialect dialect;
- private boolean enableLimit = true;
- public Dialect getDialect() {
- return dialect;
- }
- public void setDialect(Dialect dialect) {
- this.dialect = dialect;
- }
- public boolean isEnableLimit() {
- return enableLimit;
- }
- public void setEnableLimit(boolean enableLimit) {
- this.enableLimit = enableLimit;
- }
- /**
- * 重写SqlExecutor.executeQuery方法 实现ORACLE的SQL物理分页 see OracleDialect
- */
- public void executeQuery(StatementScope request, Connection conn,
- String sql, Object[] parameters, int skipResults, int maxResults,
- RowHandlerCallback callback) throws SQLException {
- if (isLimit(sql, skipResults, maxResults)) {// 有分页信息、可物理分页SQL
- sql = dialect.getOracleLimit(sql, skipResults, maxResults);// 获得物理分页SQL
- skipResults = NO_SKIPPED_RESULTS;// 设置skipResults为SqlExecutor不分页
- maxResults = NO_MAXIMUM_RESULTS;// 设置maxResults为SqlExecutor不分页
- }
- super.executeQuery(request, conn, sql, parameters, skipResults,
- maxResults, callback);// 使用不分页机制调用SqlExecutor查询方法
- }
- /**
- * 是否允许执行分页
- *
- * @param sql
- * @param skipResults
- * @param maxResults
- * @return
- */
- private boolean isLimit(String sql, int skipResults, int maxResults) {
- return (skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS)
- && enableLimit && isSelect(sql);
- }
- /**
- * 是否可物理分页SQL
- *
- * @param sql
- * @return
- */
- private boolean isSelect(String sql) {
- if (sql.toLowerCase().indexOf("select") >= 0) {
- if (sql.toLowerCase().indexOf("rownum") >= 0) {
- return false;
- }
- return true;
- }
- return false;
- }
- }
修改语句接口
- package com.ibatis.sqlmap.engine.inter;
- /**
- *
- * @author chenyanji
- *
- * @project Vstsoft newsx code.
- * @datetime Mar 15, 2009 12:07:24 AM
- */
- public interface Dialect {
- /**
- * 获得分页SQL
- *
- * @param sql
- * @param offset
- * @param limit
- * @return
- */
- public String getOracleLimit(String sql, int offset, int limit);
- }
实现类
- package com.ibatis.sqlmap.engine.imp;
- import com.ibatis.sqlmap.engine.inter.Dialect;
- /**
- *
- * @author chenyanji
- *
- * @project Vstsoft newsx code.
- * @datetime Mar 15, 2009 12:06:18 AM
- */
- public class DialectImp implements Dialect {
- protected static final String SQL_END_DELIMITER = ";";
- public String getOracleLimit(String sql, int offset, int limit) {
- if (offset == 1) {
- offset = 0;
- }
- StringBuffer pageStr = new StringBuffer();
- pageStr
- .append("select * from ( select row_limit.*, rownum rownum_ from (");
- pageStr.append(this.trim(sql));
- pageStr.append(" ) row_limit where rownum <= ");
- pageStr.append(limit + offset);
- pageStr.append(" ) where rownum_ >");
- pageStr.append(offset);
- return pageStr.toString();
- }
- /**
- * 去掉当前SQL 后分号
- *
- * @param sql
- * @return
- */
- private String trim(String sql) {
- sql = sql.trim();
- if (sql.endsWith(SQL_END_DELIMITER)) {
- sql = sql.substring(0, sql.length() - 1
- - SQL_END_DELIMITER.length());
- }
- return sql;
- }
- }
2、增加继承类(ibatis调用地层类)
- package org.springside.core.dao.support;
- import java.util.TimeZone;
- import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
- import org.springside.core.utils.ReflectUtil;
- import com.ibatis.sqlmap.client.SqlMapClient;
- import com.ibatis.sqlmap.engine.execution.SqlExecutor;
- import com.ibatis.sqlmap.engine.execution.XSqlExecutor;
- import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
- import com.vstsoft.csi.core.log.Logger;
- /**
- *
- * @author chenyanji
- *
- * @project Vstsoft newsx code.
- * @datetime Mar 15, 2009 12:07:11 AM
- */
- public abstract class XSqlMapClientDaoSupport extends SqlMapClientDaoSupport {
- /**
- * chenyanji.修正JDK时区问题
- */
- public XSqlMapClientDaoSupport(){
- TimeZone.setDefault(TimeZone.getTimeZone("ETC/GMT-8"));
- }
- // protected Logger logger = new Logger(this.getClass());
- private SqlExecutor sqlExecutor;
- public SqlExecutor getSqlExecutor() {
- return sqlExecutor;
- }
- public void setSqlExecutor(SqlExecutor sqlExecutor) {
- this.sqlExecutor = sqlExecutor;
- }
- public void setEnableLimit(boolean enableLimit) {
- if (sqlExecutor instanceof XSqlExecutor) {
- ((XSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);
- }
- }
- /**
- * 将封装过的sqlExecutor对象SET给getSqlMapClientTemplate,从而改变继承sqlExecutor对象。
- *
- * @throws Exception
- */
- @SuppressWarnings("deprecation")
- public void initialized() {
- if (sqlExecutor != null) {
- SqlMapClient sqlMapClient = getSqlMapClientTemplate()
- .getSqlMapClient();
- if (sqlMapClient instanceof ExtendedSqlMapClient) {
- ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)
- .getDelegate(), "sqlExecutor", SqlExecutor.class,
- sqlExecutor);
- // logger.info(((ExtendedSqlMapClient) sqlMapClient).getDelegate()
- // .getClass().getSimpleName()
- // + "'s OracleLimit initialized.");
- } else {
- // logger.info("null OracleLimit initialized.");
- }
- }
- }
- }
本人使用springside作为执行数据库地层类,所以直接修改IBatisGenericDao继承为XSqlMapClientDaoSupport就可以
- public class IBatisGenericDao extends XSqlMapClientDaoSupport {
3、为所有SPRING配置文件增加初始化方法,作用是修改IBATIS地层SqlMapClientDaoSupport的实现类,使用我们自己写的类,以便方便控制于修改SQL
- <beans default-init-method="initialized">
- package org.springside.core.utils;
- import java.lang.reflect.Field;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- /**
- *
- * @author chenyanji
- *
- * @project Vstsoft newsx code.
- * @datetime Mar 15, 2009 12:07:33 AM
- */
- public class ReflectUtil {
- /**
- * 通过反射绕过java的访问控制,向对象中SET属性。
- * @param target
- * @param fname
- * @param ftype
- * @param fvalue
- */
- public static void setFieldValue(Object target, String fname, Class ftype, Object fvalue) {
- if (target == null || fname == null || "".equals(fname)
- || (fvalue != null && !ftype.isAssignableFrom(fvalue.getClass()))) {
- return;
- }
- Class clazz = target.getClass();
- try {
- Method method = clazz.getDeclaredMethod("set" + Character.toUpperCase(fname.charAt(0))
- + fname.substring(1), ftype);
- if (!Modifier.isPublic(method.getModifiers())) {
- method.setAccessible(true);
- }
- method.invoke(target, fvalue);
- } catch (Exception me) {
- try {
- Field field = clazz.getDeclaredField(fname);
- if (!Modifier.isPublic(field.getModifiers())) {
- field.setAccessible(true);
- }
- field.set(target, fvalue);
- } catch (Exception fe) {
- }
- }
- }
- }
到此步,已经完成分页修改,和网上说的改法基本一致。
原理,初始化自己BEAN的同时也会初始化到父类父类。。。默认初始化方法initialized来进行修改IBATIS
经测试出现问题:已知一未初始化SQLMAP,使用分页进行初始化(SQLMAP会多映射一列,ORACLE3层分页导致),在进行不分页查询会出现少列错误,也就是说SQLMAP 多了一个映射列,查询语句无法进行完整匹配。SQLMAP唯一不足的是:SQLMAP映射可以不查询出的列少,但是绝对不允许多,现在我们使用分页初始化就多出一列。本人的解决办法是直接修改JAR包(未找到好的解决办法)
com.ibatis.sqlmap.engine.mapping.result.ResultMap 中 getPrimitiveResultMappingValue方法进行修改
增加过滤条件判断
if(columnName.equals("ROWNUM_"))
value = "";
(直接哪JAVA文件进行修改编译,然后直接替换包中CLASS,如需要可以所要)
最后提一点,也是容易忽略的地方,加了ORACLE分页后,原始SQL不能出现重复列、rownum,否则提示无法映射列错误!如 select * from tablea as a, tableb as b where a.id=b.id 这样语句加上分页就会出现ID无法映射,两表都包含ID外层分页SQL无法解析ID是从那个表来的。
因冲忙没有做详细讲解,如果质疑可以进一步讨论
本人最终实现完全修改JAR包,如果需要JAR包,请加入群(共享)本包只针对ORACLE进行修改,无其他扩展。