设为首页 收藏本站
查看: 726|回复: 0

[经验分享] mybatis分页实现

[复制链接]

尚未签到

发表于 2016-11-25 02:10:15 | 显示全部楼层 |阅读模式
  最近开始尝试用mybatis,比较郁闷其对分页的实现,看了网上一些例子,还是不满意。最好的应该是rapid-framework里的实现了,但没实现分页参数的参数化,参数都是硬编码在sql里,在oracle这样的数据库里,性能影响还是有的。
  下面是我的实现,但还是觉得有些复杂。
  StatementHandlerInterceptor.java 主要是负责修改sql,在rapid-framework,其实就只有这样一个Interceptor,但如果要设置参数,就不止了。
  @Intercepts({
  @Signature(type = StatementHandler.class, method =  prepare , args = { Connection.class }),
  @Signature(type = StatementHandler.class, method =  parameterize , args = { Statement.class }) })
public class StatementHandlerInterceptor extends
  AbstractStatementHandlerInterceptor implements Interceptor {
 
 private Object prepare(Invocation invocation) throws Throwable {
  StatementHandler statement = getStatementHandler(invocation);
  
  if (statement instanceof SimpleStatementHandler
    || statement instanceof PreparedStatementHandler) {
  RowBounds rowBounds = getRowBounds(statement);
  
   if (hasBounds(rowBounds)) {
    BoundSql boundSql = statement.getBoundSql();
    String sql = boundSql.getSql();
    
    if (statement instanceof SimpleStatementHandler) {
     sql = dialect.getLimitString(sql, rowBounds.getOffset(),
       rowBounds.getLimit());
    }
    else if (statement instanceof PreparedStatementHandler) {
     sql = dialect.getLimitString(sql, rowBounds.getOffset()   0);
    }
    FieldUtils.setFieldValue(boundSql,  sql , sql);
   }
  }
  return invocation.proceed();
 }
 
 private Object parameterize(Invocation invocation) throws Throwable {
  Statement statement = (Statement) invocation.getArgs()[0];
  Object rtn = invocation.proceed();
  if (statement instanceof PreparedStatement) {
   PreparedStatement ps = (PreparedStatement) statement;
  StatementHandler statementHandler = getStatementHandler(invocation);
   RowBounds rowBounds = getRowBounds(statementHandler);
  if (hasBounds(rowBounds)) {
    BoundSql boundSql = statementHandler.getBoundSql();
    int parameterSize = boundSql.getParameterMappings().size();
    dialect.setLimitParamters(ps, parameterSize,
      rowBounds.getOffset(), rowBounds.getLimit());
   }
  }
  return rtn;
 }
  @Override
 public Object intercept(Invocation invocation) throws Throwable {
  Method m = invocation.getMethod();
  if ( prepare .equals(m.getName())) {
   return prepare(invocation);
  }
  else if ( parameterize .equals(m.getName())) {
   return parameterize(invocation);
  }
  return invocation.proceed();
 }
  @Override
 public Object plugin(Object target) {
  return Plugin.wrap(target, this);
 }
  @Override
 public void setProperties(Properties properties) {
 }
  }
 
  prepare和parameterize是在两个不同点调用的,一个负责生成Statement,一个负责设置参数。本来是想分开写成两个Interceptor,但后来发现多次针对同一个类进行拦截,是无法获取其内部属性的,Plugin.wrap(target, this)是生成一个代理类,下一个拦截器就是在这个基础上继续做代理。这个和struts2之类的拦截器是不一样的。
  好像这样是可以了,但实际还是不够的。因为StatementHandler和FastResultSetHandler是同时生成,而且都接引用了RowBounds参数,在FastResultSetHandler还是分页形式存在,如果不修正,会导致mybatis用游标移动后取数据。
  ResultSetHandlerInterceptor.java 负责修正RowBounds
  @Intercepts({ @Signature(type = ResultSetHandler.class, method =  handleResultSets , args = { Statement.class }) })
public class ResultSetHandlerInterceptor implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {
  ResultSetHandler resultSet = (ResultSetHandler) invocation.getTarget();
  // 不用浪费性能做属性存在判断
  RowBounds rowBounds = (RowBounds) FieldUtils.getFieldValue(resultSet,
     rowBounds );
  if (rowBounds.getLimit()   0
       rowBounds.getLimit()   RowBounds.NO_ROW_LIMIT) {
   // 强制不允许游标分页
   FieldUtils.setFieldValue(resultSet,  rowBounds , RowBounds.DEFAULT);
  }
  return invocation.proceed();
 }
  public Object plugin(Object target) {
  return Plugin.wrap(target, this);
 }
  public void setProperties(Properties properties) {
 }
}
 
  不过现在想想,用反射直接修改RowBounds里的属性,不就可以不用ResultSetHandlerInterceptor了?哎!又SB了一次。后面改进。
 
  其他辅助类
  public abstract class AbstractStatementHandlerInterceptor implements Interceptor, InitializingBean {
 
 private Class Dialect  dialectClass;
  public void setDialectClass(Class Dialect  dialectClass) {
  this.dialectClass = dialectClass;
 }
 
 protected Dialect dialect;
  public void setDialect(Dialect dialect) {
  this.dialect = dialect;
 }
 
 public void afterPropertiesSet() throws Exception {
  setDialect(dialectClass.newInstance());
 }
  protected StatementHandler getStatementHandler(Invocation invocation) {
  StatementHandler statement = (StatementHandler) invocation.getTarget();
  if (statement instanceof RoutingStatementHandler) {
   statement = (StatementHandler) FieldUtils.getFieldValue(statement,
      delegate );
  }
  return statement;
 }
 
 protected RowBounds getRowBounds(StatementHandler statement) {
  return (RowBounds) FieldUtils.getFieldValue(statement,  rowBounds );
 }
 
 protected boolean hasBounds(RowBounds rowBounds) {
  return (rowBounds != null
       rowBounds.getLimit()   0
       rowBounds.getLimit()   RowBounds.NO_ROW_LIMIT);
 }
  }
 
  public interface Dialect {
 
 public void setLimitParamters(PreparedStatement ps, int parameterSize, int offset, int limit) throws SQLException;
 
 public String getLimitString(String sql, boolean hasOffset);
  public String getLimitString(String sql, int offset, int limit);
}
 
  public abstract class FieldUtils {
 
 public static boolean hasField(Object target, String fieldName, Class ?  type) {
  return ReflectionUtils.findField(target.getClass(), fieldName, type) == null;
 }
  public static Object getFieldValue(Object target, String fieldName) {
  Field field = ReflectionUtils.findField(target.getClass(), fieldName);
  ReflectionUtils.makeAccessible(field);
  return ReflectionUtils.getField(field, target);
 }
 
 public static void setFieldValue(Object target, String fieldName, Object value) {
  Field field = ReflectionUtils.findField(target.getClass(), fieldName);
  ReflectionUtils.makeAccessible(field);
  ReflectionUtils.setField(field, target, value);
 }
}
 
  ReflectionUtils是spring里的。
  spring 集成用到的。增加interceptors集合,直接通过spring注入interceptor.
  public class MyBatisSessionFactoryBean extends SqlSessionFactoryBean {
 
 private List Interceptor  interceptors = Collections.emptyList();
  public void setInterceptors(List Interceptor  interceptors) {
  this.interceptors = interceptors;
 }
  protected SqlSessionFactory buildSqlSessionFactory() throws IOException,
   IllegalAccessException, InstantiationException {
  SqlSessionFactory factory = super.buildSqlSessionFactory();
  Configuration config = factory.getConfiguration();
  for (Interceptor interceptor : interceptors) {
   config.addInterceptor(interceptor);
  }
  return factory;
 }
}
 
  下一次改进方向,现在目前来讲,只是实现了sql能分页,但缺少count的集成。mybatis中两条语句是避免不了,虽然可以用include来避免重复的where语句。
  
 sql id= findAllWhere 
 where mm = #{love}
 /sql
  select id= findAll  resultMap= helloResultMap 
 select * from HELLO
  include refid= findAllWhere /
 order by create_time
 /select
  select id= findAllCount  resultType= long 
 select count(*) from HELLO
  include refid= findAllWhere /
 /select
 
  下次希望是,能在调用findAll的时候,自动调用findAllCount,遵守命名约定或者用注释来提示。由于我打算完全不写dao层,只写xml文件和接口,需要用到SqlSession.getMapper自动生成代理。由于期望是自动调用count方法和返回Page这样的对象。 这个可能就要看,是再代理一层,还是修改代理的实现了。入手点可以是继承Configuration,然后覆盖mapperRegistry来实现。 

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-304951-1-1.html 上篇帖子: mybatis一个莫名的NullPointerException 下篇帖子: Mybatis 二级缓存
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表