首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

mybatis分页兑现

2013-07-09 
mybatis分页实现最近开始尝试用mybatis,比较郁闷其对分页的实现,看了网上一些例子,还是不满意。最好的应该

mybatis分页实现

最近开始尝试用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来实现。?

?

?

?

?

?

?

?

?

?

?

?

?

?

热点排行