`
icefire
  • 浏览: 125362 次
  • 性别: Icon_minigender_1
  • 来自: 深圳、重庆
社区版块
存档分类
最新评论

mybatis分页实现2

阅读更多
前一篇写了如何实现sql分页
http://icefire.iteye.com/blogs/1028915

引用
本文对直接使用sqlsession的方式无参考价值,本文只针对只写接口和XML文件的方式


这一篇讲,如何实现一个mapping的方法访问,自动返回page数据,也就是自读计算分页总数。

先看成果对比效果
原先
public interface HelloMapping {

	public List<Hello> findAll(RowBounds rowBounds);
	
	public Long findAllCount();
}

public String list(@ModelAttribute Pagination<Hello> pagination,Model model) {

	pagination.setTotal(helloMapping.findAllCount());
	pagination.setList(helloMapping.findAll(pagination.newRowBounds())));
	model.addAttribute("paging", pagination);

	return "/hello/view/list";
}


现在
public interface HelloMapping {

	public Pagination<Hello> findAll(Pagination<Hello> pagination);
}

public String list(@ModelAttribute Pagination<Hello> pagination,Model model) {

	model.addAttribute("paging", helloMapping.findAll(pagination));
		
	return "/hello/view/list";
}


如果看源码,会发现,mapping接口自动代理涉及Configuration,MapperRegistry,MapperProxy,MapperMethod。
其中比较可惜的是,MapperProxy,MapperMethod完全无法重用,只能参考其中代码自己来实现了。还有那个mybatis通过的spring集成类SqlSessionFactoryBean,由于要实现自己Configuration,也导致SqlSessionFactoryBean无法重用。这点上,和spring比较起来,就差太多了。

MyConfiguration
public class MyConfiguration extends Configuration {

	protected MapperRegistry mapperRegistry = new PaginationMapperRegistry(this);
	
	public <T> void addMapper(Class<T> type) {
		mapperRegistry.addMapper(type);
	}

	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
		return mapperRegistry.getMapper(type, sqlSession);
	}

	@SuppressWarnings("rawtypes")
	public boolean hasMapper(Class type) {
		return mapperRegistry.hasMapper(type);
	}
}


PaginationMapperRegistry
public class PaginationMapperRegistry extends MapperRegistry {

	public PaginationMapperRegistry(Configuration config) {
		super(config);
	}

	public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
		if (!hasMapper(type)) {
			throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
		}
		try {
			return PaginationMapperProxy.newMapperProxy(type, sqlSession);
		} catch (Exception e) {
			throw new BindingException("Error getting mapper instance. Cause: " + e, e);
		}
	}

}


PaginationMapperProxy
public class PaginationMapperProxy implements InvocationHandler {

	private static final Set<String> OBJECT_METHODS = new HashSet<String>() {
		private static final long serialVersionUID = -1782950882770203583L;
		{
			add("toString");
			add("getClass");
			add("hashCode");
			add("equals");
			add("wait");
			add("notify");
			add("notifyAll");
		}
	};

	private boolean isObjectMethod(Method method) {
		return OBJECT_METHODS.contains(method.getName());
	}

	private SqlSession sqlSession;

	private <T> PaginationMapperProxy(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if (isObjectMethod(method)) {
			return null;
		}
		final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
		if (Pagination.class.isAssignableFrom(method.getReturnType())) {
			// 分页处理
			return new PaginationMapperMethod(declaringInterface, method, sqlSession).execute(args);
		}
		// 原处理方式
		final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
		final Object result = mapperMethod.execute(args);
		if (result == null && method.getReturnType().isPrimitive()) {
			throw new BindingException(
					"Mapper method '"
							+ method.getName()
							+ "' ("
							+ method.getDeclaringClass()
							+ ") attempted to return null from a method with a primitive return type ("
							+ method.getReturnType() + ").");
		}
		return result;
	}

	private Class<?> findDeclaringInterface(Object proxy, Method method) {
		Class<?> declaringInterface = null;
		for (Class<?> iface : proxy.getClass().getInterfaces()) {
			Method m = ReflectionUtils.findMethod(iface, method.getName(), method.getParameterTypes());
			if (m != null) {
				declaringInterface = iface;
			}
		}
		if (declaringInterface == null) {
			throw new BindingException(
					"Could not find interface with the given method " + method);
		}
		return declaringInterface;
	}

	@SuppressWarnings("unchecked")
	public static <T> T newMapperProxy(Class<T> mapperInterface,
			SqlSession sqlSession) {
		ClassLoader classLoader = mapperInterface.getClassLoader();
		Class<?>[] interfaces = new Class[] { mapperInterface };
		PaginationMapperProxy proxy = new PaginationMapperProxy(sqlSession);
		return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
	}

}


PaginationMapperMethod
public class PaginationMapperMethod {

	private SqlSession sqlSession;
	private Configuration config;

	private SqlCommandType type;
	private String commandName;
	private String commandCountName;

	private Class<?> declaringInterface;
	private Method method;

	private Integer rowBoundsIndex;
	private Integer paginationIndex;
	private List<String> paramNames;
	private List<Integer> paramPositions;

	private boolean hasNamedParameters;

	public PaginationMapperMethod(Class<?> declaringInterface, Method method,
			SqlSession sqlSession) {
		paramNames = new ArrayList<String>();
	    paramPositions = new ArrayList<Integer>();
	    this.sqlSession = sqlSession;
	    this.method = method;
	    this.config = sqlSession.getConfiguration();
	    this.hasNamedParameters = false;
	    this.declaringInterface = declaringInterface;
	    setupFields();
	    setupMethodSignature();
	    setupCommandType();
	    validateStatement();
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object execute(Object[] args) {
		final Object param = getParam(args);
		Pagination<Object> page;
		RowBounds rowBounds;
		if (paginationIndex != null) {
			page = (Pagination) args[paginationIndex];
			rowBounds = page.newRowBounds();
		}
		else if (rowBoundsIndex != null) {
			rowBounds = (RowBounds) args[rowBoundsIndex];
			page = new Pagination<Object>(rowBounds);
		}
		else {
			throw new BindingException("Invalid bound statement (not found rowBounds or pagination in paramenters)");
		}
		page.setTotal(executeForCount(param));
		page.setList(executeForList(param, rowBounds));
		return page;
	}
	
	private long executeForCount(Object param) {
		Number result = (Number) sqlSession.selectOne(commandCountName, param);
		return result.longValue();
	}

	@SuppressWarnings("rawtypes")
	private List executeForList(Object param, RowBounds rowBounds) {
		return sqlSession.selectList(commandName, param, rowBounds);
	}

	private Object getParam(Object[] args) {
		final int paramCount = paramPositions.size();
		if (args == null || paramCount == 0) {
			return null;
		} else if (!hasNamedParameters && paramCount == 1) {
			return args[paramPositions.get(0)];
		} else {
			Map<String, Object> param = new HashMap<String, Object>();
			for (int i = 0; i < paramCount; i++) {
				param.put(paramNames.get(i), args[paramPositions.get(i)]);
			}
			return param;
		}
	}
	
	private void setupMethodSignature() {
		final Class<?>[] argTypes = method.getParameterTypes();
		for (int i = 0; i < argTypes.length; i++) {
			if (Pagination.class.isAssignableFrom(argTypes[i])) {
				paginationIndex = i;
			}
			else if (RowBounds.class.isAssignableFrom(argTypes[i])) {
				rowBoundsIndex = i;
			} else {
				String paramName = String.valueOf(paramPositions.size());
				paramName = getParamNameFromAnnotation(i, paramName);
				paramNames.add(paramName);
				paramPositions.add(i);
			}
		}
	}

	private String getParamNameFromAnnotation(int i, String paramName) {
		Object[] paramAnnos = method.getParameterAnnotations()[i];
		for (Object paramAnno : paramAnnos) {
			if (paramAnno instanceof Param) {
				hasNamedParameters = true;
				paramName = ((Param) paramAnno).value();
			}
		}
		return paramName;
	}

	private void setupFields() {
		commandName = declaringInterface.getName() + "." + method.getName();
		commandCountName = commandName + "Count"; // 命名约定
	}

	private void setupCommandType() {
		MappedStatement ms = config.getMappedStatement(commandName);
		type = ms.getSqlCommandType();
		if (type != SqlCommandType.SELECT) {
			throw new BindingException("Unsupport execution method for: " + commandName);
		}
	}

	private void validateStatement() {
		if (!config.hasStatement(commandName)) {
			throw new BindingException("Invalid bound statement (not found): " + commandName);
		}
		if (!config.hasStatement(commandCountName)) {
			throw new BindingException("Invalid bound statement (not found): " + commandCountName);
		}
	}

}


具体某个查询的SQL配置

<select id="findAll" resultMap="helloResultMap">
	select * from HELLO
</select>

<select id="findAllCount" resultType="long">
	select count(*) from HELLO
</select>


补代码Pagination,分页辅助类
public class Pagination<T> {

	private long total;
	
	private int pagesize;
	
	private List<T> list = Collections.emptyList();
	
	private int offset;
	
	private int limit;
	
	private int page;
	
	public Pagination() {
		this(1, 15);
	}
	
	public Pagination(int page) {
		this(page, 15);
	}
	
	public Pagination(int page, int limit) {
		setPage(page);
		setLimit(limit);
	}
	
	public Pagination(RowBounds rowBounds) {
		this.limit = rowBounds.getLimit();
		this.offset = rowBounds.getOffset();
		this.page = offset / limit + 1;
	}
	
	public void setPage(int page) {
		if (page < 0) {
			page = 1;
		}
		this.page = page;
		onInit();
	}
	
	public void setLimit(int limit) {
		if (limit < 1) {
			limit = 15;
		}
		this.limit = limit;
		onInit();
	}
	
	protected void onInit() {
		offset = (page - 1) * limit;
	}
	
	protected void onSetRowsize() {
		pagesize = (int) (total / limit);
		if (total % limit > 0) {
			pagesize ++;
		}
		if (page > pagesize) {
			page = pagesize;
			onInit();
		}
	}
	
	protected void onSetList() {
		if (list == null || list.isEmpty()) {
			total = 0;
			page = 1;
			offset = 0;
		}
	}

	public long getTotal() {
		return total;
	}

	public void setTotal(long rowsize) {
		this.total = rowsize;
		onSetRowsize();
	}

	public int getPagesize() {
		return pagesize;
	}

	public List<T> getList() {
		return list;
	}
	
	public void setList(List<T> list) {
		this.list = list;
		onSetList();
	}

	public int getOffset() {
		return offset;
	}

	public int getLimit() {
		return limit;
	}

	public int getPage() {
		return page;
	}
	
	public RowBounds newRowBounds() {
		return new RowBounds(getOffset(), getLimit());
	}
}

目前无可避免的需要两段sql。在考虑如何实现自动生成count(*),但目前做到这样,已经达到自己的预期了。暂时先就这样吧!
3
12
分享到:
评论
8 楼 张飞1 2016-01-11  
你好!,看了你这篇文章,然后阅读了一下源码,在XMLConfigBuilder中直接使用的new Configuration,我使用的是spring,我想问一下这个怎么能够调用自己的Configuration
7 楼 anglny 2013-05-24  
你定义一个所有实体对象的父实体。然后在里面定义一个total的变量。在你做分页操作的时候查一次count(*) 然后把值set进父实体。
6 楼 icefire 2013-05-13  
clxy 写道
我也在找这个实现。你这里的方法有些“繁琐”,需要动太多mybatis的类了。

在https://github.com/yfyang/mybatis-pagination这里看到个实现,其他部分优劣不说,

只是觉得自动执行取总数部分还是可以借鉴的。

也供你参考。


粗略看了下,你提供的这个应该会好些,比较适合用到项目中。我这个就自己业余写的demo,实际项目使用还差得多。自己工作也没使用mybatis,也就没继续深入了。
5 楼 clxy 2013-05-09  
我也在找这个实现。你这里的方法有些“繁琐”,需要动太多mybatis的类了。

在https://github.com/yfyang/mybatis-pagination这里看到个实现,其他部分优劣不说,

只是觉得自动执行取总数部分还是可以借鉴的。

也供你参考。
4 楼 mygia 2012-08-23  
学习一下!
3 楼 icefire 2011-11-29  
kelor 写道
看了下源代码,在创建sessionFactory,session对象的时候传入的参数都是Configuration。
如果要用自己的Configuration。是不是也要扩展下XMLConfigBuilder,使用自定义的Configuration来初始化configuration属性?


不知道你的需求是怎样的,如果用spring,就没必要用xml来初始化Configuration了。如果是xml要增加自己的属性,可能需要扩展XMLConfigBuilder吧,具体没研究过了。我使用自己的Configuration是因为我要替换其中的属性,Configuration又没提供set方法,所以别无选择。
2 楼 kelor 2011-11-25  
看了下源代码,在创建sessionFactory,session对象的时候传入的参数都是Configuration。
如果要用自己的Configuration。是不是也要扩展下XMLConfigBuilder,使用自定义的Configuration来初始化configuration属性?
1 楼 guoyi_20082008 2011-10-14  
也按照此方式实现,但发现缺少Pagination类的代码,不知能否附上,谢谢

相关推荐

    06实现mybatis分页插件demo

    06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo...

    mybatis分页插件代码

    【标题】"mybatis分页插件代码"主要涉及到MyBatis框架中的一种增强功能——分页插件的使用。MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。在处理大数据量时,分页查询是必不可少的优化...

    MyBatis分页功能实现

    在实现MyBatis分页功能时,我们需要添加MyBatis、Spring以及它们的整合包(如mybatis-spring)到项目的依赖列表中。确保版本兼容,这样才能让Spring管理和初始化MyBatis的数据源、SqlSessionFactory、Mapper接口等...

    Mybatis分页拦截器

    Mybatis分页拦截器是Mybatis框架中一种实用的扩展机制,用于实现高效的数据库查询分页功能。在Mybatis中,拦截器扮演着插件的角色,可以监听并修改Mybatis执行过程中的某些行为。本话题将深入探讨Mybatis分页拦截器...

    mybatis分页完整的项目

    【标题】"mybatis分页完整的项目"是一个涵盖了MyBatis分页功能的完整Java Web项目,旨在帮助开发者理解和实现MyBatis在实际应用中的分页处理。该项目结合了Spring和MyBatis两大流行框架,提供了从数据库查询数据并...

    MyBatis分页插件.rar

    总的来说,MyBatis分页插件通过jsqlparser和PageHelper的结合,极大地简化了开发过程中的分页实现,提高了代码的可读性和维护性,是MyBatis开发中值得推荐的工具。在实际项目中,正确地使用这个插件可以提高开发效率...

    mybatis分页拦截器(自动封装版)剖析.pdf

    在自动封装版的MyBatis分页拦截器中,开发者通常会创建一个拦截器类,该类会拦截到执行SQL的时机,然后在SQL语句中动态添加分页相关的条件,如LIMIT和OFFSET子句,以实现数据的分页展示。 分页拦截器的核心思想是...

    mybatis分页插件的使用

    ### Mybatis分页插件详解 #### 一、概述 在使用Mybatis处理大量数据时,分页查询是一项常见的需求。传统的做法是在SQL语句中手动加入`LIMIT`和`OFFSET`来实现分页功能,这种方法不仅繁琐而且容易出错。针对这一...

    mybatis分页实现1

    MyBatis 提供了两种主要的分页实现方法:一种是通过在 Mapper XML 文件中直接编写分页 SQL,另一种是利用 MyBatis 的插件机制,如 PageHelper 插件,进行拦截器实现分页。这里我们主要讨论第一种方法,即“普通的...

    mybatis分页jar包

    自己封装的mybatis分页jar包,实现了mybatis的物理分页,目前只支持mysql和oracle两种数据库。

    MyBatis自动分页实现

    MyBatis自动分页实现 MyBatis 是一个流行的持久层框架,它提供了强大的数据访问功能。但是,在实际应用中,分页问题经常困扰开发者。要实现分页,开发者需要手动编写代码来实现分页逻辑,这不仅增加了开发难度,也...

    mybatis分页(struts2+spring+mybatis)

    本项目“mybatis分页(struts2+spring+mybatis)”演示了如何在Java Web应用中实现基于MyBatis的分页功能,结合Struts2和Spring框架,提供了一个完整的解决方案。下面将详细解释这个项目涉及的知识点。 1. **...

    MyBatis分页

    《MyBatis分页详解与实战应用》 在现代Web开发中,数据的分页展示是必不可少的功能,尤其是在处理大量数据时,为了提高用户体验和优化性能,MyBatis提供了强大的分页支持。本文将深入探讨MyBatis的分页原理,并结合...

    mybatis 分页插件jar包

    总的来说,MyBatis分页插件PageHelper是Java Web开发中处理大数据量场景的有效工具,它的出现大大简化了分页实现的复杂度,提升了开发效率,并且保持了代码的整洁和可维护性。在实际项目中,合理运用PageHelper可以...

    ssm整合的jar和mybatis分页代码,mybatis生成工具类

    在本示例中,"ssm整合的jar和mybatis分页代码,mybatis生成工具类" 提供了实现SSM整合时所需的一些关键组件,包括MyBatis的分页拦截器和代码生成工具。 首先,`PaginationInterceptor.java` 文件是MyBatis的分页...

    mybatis分页插件源码

    下面我们将深入探讨MyBatis分页插件的相关知识点。 1. **MyBatis框架基础** MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果...

    MyBatis 分页

    MyBatis分页是数据库操作中的一个重要概念,它允许开发者在查询数据时限制返回结果的数量,从而提高查询效率和用户体验。在大型应用中,如果一次性加载所有数据,不仅会消耗大量内存,还可能导致用户界面反应迟钝。...

    springboot+mybatis分页

    至此,我们就完成了SpringBoot与Mybatis的集成,并利用PageHelper实现了简单的分页查询。在实际项目中,可以根据业务需求进一步定制分页参数,如排序方式、是否包含空结果等。通过这种方式,我们可以有效地管理数据...

    MyBatis 分页源码简单

    本文将深入探讨 MyBatis 的分页源码,理解其背后的实现机制。 首先,我们要明白 MyBatis 中的分页主要依赖于两个核心组件:PageHelper 和自定义分页插件。PageHelper 是一个 MyBatis 的第三方分页插件,它可以自动...

Global site tag (gtag.js) - Google Analytics