家庭财务管理系统实战4- mybatis分页查询功能改进实现
2013-11-22 10:55
666 查看
本篇实现mybatis的分页查询功能,结合easyui的datagrid的pagenation实现列表分页查询功能。本功能实现也是作者通过对网络上各种搜索结果借鉴的结果。
mybatis自带的分页查询功能是对查询结果在内存中进行分页,也就是说,先查询出所有符合条件的结果放到内存,然后再从内存中取出当前页的内容,这对大数据量查询来说存在内存溢出的风险。
本篇实现在数据库进行物理分页,就是只从数据库中查询复合条件的当前页数据。
首先看实体bean:
对应的mapper.xml内容:
上面resultMap标签中定义了bean和库表字段的对应关系,下面定义了两个select语句,queryPage语句定义了查询当前页数据的语句,getTotal语句定义了查询总数的语句。
trim标签中的内容应该都很好理解,if用来判断是否有此过滤条件,prefix中定义了where前缀,prefixOverrides中定义了需要覆盖的串:比如,第一个if不符合,第二个if符合,那么这个sql语句就会是 类似 ...where and ...,所以定义了prefixOverrides=“and”,那么这个and就会被去掉。
准备工作做完了,下面实现将mybatis的内存分页转为数据库的物理分页。原理就是对mybatis生成sql的方法进行拦截,生成我们分页需要的sql,拦截器定义如下(网上查找的,直接拿来了,做了部分注释):
拦截器定义好了,在mybatis配置文件中加入如下内容:
到此,准备工作真的完成了,下面看代码中如何调用。
首先看cotroller的代码:
service的代码:
dao代码:
mapper接口的定义:
下面是jsp文件的内容:
下面看几张效果图:
mybatis自带的分页查询功能是对查询结果在内存中进行分页,也就是说,先查询出所有符合条件的结果放到内存,然后再从内存中取出当前页的内容,这对大数据量查询来说存在内存溢出的风险。
本篇实现在数据库进行物理分页,就是只从数据库中查询复合条件的当前页数据。
首先看实体bean:
package system.homebank.entity; import java.io.Serializable; public class Payments implements Serializable { private static final long serialVersionUID = 1256273897024133599L; private Integer id; private String value; private String name; private String paymenttype; private String unit; private String unitname; private String descript; private String day; private String crttime; private String type; private String typename; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPaymenttype() { return paymenttype; } public void setPaymenttype(String paymenttype) { this.paymenttype = paymenttype; } public String getUnit() { return unit; } public void setUnit(String unit) { this.unit = unit; } public String getUnitname() { return unitname; } public void setUnitname(String unitname) { this.unitname = unitname; } public String getDescript() { return descript; } public void setDescript(String descript) { this.descript = descript; } public String getDay() { return day; } public void setDay(String day) { this.day = day; } public String getCrttime() { return crttime; } public void setCrttime(String crttime) { this.crttime = crttime; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getTypename() { return typename; } public void setTypename(String typename) { this.typename = typename; } }
对应的mapper.xml内容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="system.homebank.mapper.PaymentsMapper" > <resultMap id="paymentsResultMap" type="Payments"> <id property="id" column="id"/> <result property="value" column="value"/> <result property="name" column="name"/> <result property="paymenttype" column="paymenttype"/> <result property="unit" column="unit"/> <result property="descript" column="descript"/> <result property="day" column="day"/> <result property="crttime" column="crttime"/> <result property="type" column="type"/> <result property="unitname" column="unitname"/> <result property="typename" column="typename"/> </resultMap> <select id="queryPage" parameterType="Map" resultType="list" resultMap="paymentsResultMap"> select * from vpayments <trim prefix="where" prefixOverrides="and |or"> <if test="paymenttype != null"> paymenttype = #{paymenttype} </if> <if test="name != null"> and name like #{name} </if> <if test="type != null"> and type = #{type} </if> <if test="day != null"> and day = #{day} </if> </trim> order by day desc </select> <select id="getTotal" parameterType="Map" resultType="Integer"> select count(*) from vpayments <trim prefix="where" prefixOverrides="and |or"> <if test="paymenttype != null"> paymenttype = #{paymenttype} </if> <if test="name != null"> and name like #{name} </if> <if test="type != null"> and type = #{type} </if> <if test="day != null"> and day = #{day} </if> </trim> </select> </mapper>
上面resultMap标签中定义了bean和库表字段的对应关系,下面定义了两个select语句,queryPage语句定义了查询当前页数据的语句,getTotal语句定义了查询总数的语句。
trim标签中的内容应该都很好理解,if用来判断是否有此过滤条件,prefix中定义了where前缀,prefixOverrides中定义了需要覆盖的串:比如,第一个if不符合,第二个if符合,那么这个sql语句就会是 类似 ...where and ...,所以定义了prefixOverrides=“and”,那么这个and就会被去掉。
准备工作做完了,下面实现将mybatis的内存分页转为数据库的物理分页。原理就是对mybatis生成sql的方法进行拦截,生成我们分页需要的sql,拦截器定义如下(网上查找的,直接拿来了,做了部分注释):
package system.homebank.interceptor; import java.sql.Connection; import java.util.Properties; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Plugin; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.factory.DefaultObjectFactory; import org.apache.ibatis.reflection.factory.ObjectFactory; import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory; import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.RowBounds; //拦截StatementHandler接口中的参数为Connection的prepare方法 @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})}) public class PageInterceptor implements Interceptor { private String databaseType;//数据库类型 private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory(); private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory(); private static String DEFAULT_PAGE_SQL_ID = ".*Page$"; // 需要拦截的ID正则匹配 @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds"); // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环可以分离出最原始的的目标类) while (metaStatementHandler.hasGetter("h")) { Object object = metaStatementHandler.getValue("h"); metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); } // 分离最后一个代理对象的目标类 while (metaStatementHandler.hasGetter("target")) { Object object = metaStatementHandler.getValue("target"); metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY); } // property在mybatis settings文件内配置 Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration"); // 设置pageSqlId String pageSqlId = ""; Properties p = configuration.getVariables(); if (p != null) pageSqlId = p.getProperty("pageSqlId"); if (null == pageSqlId || "".equals(pageSqlId)) { pageSqlId = DEFAULT_PAGE_SQL_ID; } MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement"); // 只重写需要分页的sql语句。通过MappedStatement的ID匹配,默认重写以Page结尾的MappedStatement的sql if (mappedStatement.getId().matches(pageSqlId)) { BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql"); Object parameterObject = boundSql.getParameterObject(); if (parameterObject == null) { throw new NullPointerException("parameterObject is null!"); } else { String sql = boundSql.getSql(); // 重写sql,变成类似select * from table where ... limit 0,20 if (this.databaseType.equals("mysql")) sql = sql + " LIMIT " + rowBounds.getOffset() + "," + rowBounds.getLimit(); metaStatementHandler.setValue("delegate.boundSql.sql", sql); // 采用物理分页后,就不需要mybatis的内存分页了,所以重置下面的两个参数 metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET); metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT); } } // 将执行权交给下一个拦截器 return invocation.proceed(); } @Override public Object plugin(Object target) { //目标类是StatementHandler的时候才拦截 if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { this.databaseType = properties.getProperty("databaseType"); } }
拦截器定义好了,在mybatis配置文件中加入如下内容:
<plugins> <plugin interceptor="system.homebank.interceptor.PageInterceptor"> <property name="databaseType" value="mysql"/> </plugin> </plugins>
到此,准备工作真的完成了,下面看代码中如何调用。
首先看cotroller的代码:
package system.homebank.controller; import java.util.HashMap; import java.util.Map; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import system.homebank.service.PaymentsService; @Controller @RequestMapping("/paymentsController") public class PaymentsController { @Resource private PaymentsService service; @RequestMapping("/query.do") @ResponseBody public Object query(@RequestParam Map<String,Object> filter) { return this.service.query(filter); } }
service的代码:
package system.homebank.service; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.springframework.stereotype.Service; import system.homebank.dao.PaymentsDao; import system.homebank.entity.Payments; import system.homebank.model.Page; @Service public class PaymentsServiceImpl implements PaymentsService { @Resource private PaymentsDao dao; @Override public Page query(Map<String, Object> filter) { int pageno = Integer.parseInt(filter.get("page").toString()); int rows = Integer.parseInt(filter.get("rows").toString()); int start = (pageno-1)*rows; filter.remove("page"); filter.remove("rows"); Map<String, Object> map = new HashMap<String, Object>(); for (String o : filter.keySet()) { if (filter.get(o) == null || filter.get(o).equals("")) continue; map.put(o, filter.get(o)); } List<Payments> list = this.dao.query(map,start,rows); int total = this.dao.getTotal(map); Page page = new Page(); page.setRows(list); page.setTotal(total); return page; } }
dao代码:
package system.homebank.dao; import java.util.List; import java.util.Map; import javax.annotation.Resource; import org.apache.ibatis.session.RowBounds; import org.springframework.stereotype.Repository; import system.homebank.entity.Payments; import system.homebank.mapper.PaymentsMapper; import system.homebank.model.Page; @Repository public class PaymentsDaoImpl implements PaymentsDao { @Resource private PaymentsMapper maper; @Override public List<Payments> query(Map<String, Object> filter, int start, int rows) { RowBounds rb = new RowBounds(start,rows); return this.maper.queryPage(filter,rb); } @Override public int getTotal(Map<String, Object> map) { return this.maper.getTotal(map); } }
mapper接口的定义:
package system.homebank.mapper; import java.util.List; import java.util.Map; import org.apache.ibatis.session.RowBounds; import system.homebank.entity.Payments; import system.homebank.model.Page; public interface PaymentsMapper extends BaseMapper { public List<Payments> queryPage(Map<String, Object> filter, RowBounds rb); public int getTotal(Map<String, Object> map); }
下面是jsp文件的内容:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path ; %> <script> $(function(){ $('#datagrid_payout').datagrid({ url:'<%=basePath%>/paymentsController/query.do?paymenttype=2',//收入支出数据是一个表,这个参数代表支出 singleSelect:true, height:$('#tabs').height() - 50 - $('#btn').height(),//这个高度根据页面自动计算,好像又点问题,先不管了 pagination:true, pageSize:20, pageList:[10,20,30,50], columns:[[ {field:'id',title:'id',hidden:true}, {field:'name',title:'支出名称',width:250}, {field:'value',title:'支出金额',width:100}, {field:'unitname',title:'单位',width:90}, {field:'typename',title:'类别',width:100}, {field:'day',title:'支出日期',width:80}, {field:'crttime',title:'记录时间',width:130}, {field:'descript',title:'描述',width:250} ]], toolbar: [], onLoadSuccess:function(data){ } }); }); function onSubmit() { $('#datagrid_payout').datagrid('load', {name:'%'+$('#name').val()+'%', type:$('#type').combobox('getValue'), day:$('#day').datebox('getValue').replace(/\-/g, '')} );//点击搜索按钮的时候重新加载datagrid数据,并向后台发送检索参数 } </script> <form id="searchform" method="post"> <span>支出名称:</span><input id="name" name="name" /> <span>支出类型:</span><input id="type" name="type" class="easyui-combobox" data-options=" valueField:'code', textField:'codename', url:'<%=basePath%>/commonController/listDatadictCata.do?catalog=payout'"/> <span>支出日期:</span><input id="day" type="text" class="easyui-datebox" /> <a id="btn" href="#" class="easyui-linkbutton" data-options="iconCls:'icon-search'" onclick="javascript:onSubmit()">查询</a> </form> <div> <table id="datagrid_payout"></table> </div>
下面看几张效果图:
相关文章推荐
- 家庭财务管理系统实战3- 实现列表数据的增删改查功能
- 家庭财务管理系统实战2-easyui界面主框架搭建以及Accordion手风琴菜单实现
- json数组实现图书管理系统——图书管理、查询、前台分页功能
- SpringMVC(14):使用springmvc+spring+jdbc 优化订单管理系统的示例(多条件查询用户列表功能实现)
- 模块管理常规功能自定义系统的设计与实现(37--终级阶段 综合查询[4])
- 用线性表编写通讯录管理系统 实现添加,查询,修改,删除功能
- 病人资料管理系统中高级查询功能的实现
- 模块管理常规功能自定义系统的设计与实现(35--终级阶段 综合查询[2])
- 家庭财务管理系统实战5- 支出信息的增加修改与删除
- 模块管理常规功能自己定义系统的设计与实现(36--终级阶段 综合查询[3])
- 模块管理常规功能自定义系统的设计与实现(39--终级阶段 综合查询[6])
- 模块管理常规功能自定义系统的设计与实现(41--终级阶段 综合查询[8]分类汇总)
- 设计与实现模块管理系统的基本功能定义自己的(38--终极阶段 综合查询[5])
- J2EE项目系列(二)--博客管理系统(Maven+SpringMVC+Hibernate以及附加分页和一对多查询功能)
- hibernate分组查询后,求分组总数的实现方法(管理系统分页)
- 银行管理系统 实现用户注册 登录 存、取款 交易记录查询和修改用户信息等功能
- 06-php雇员管理系统-实现显示雇员信息列表分页改进(可以处理大数据100000条)
- 模块管理常规功能自定义系统的设计与实现(34--终级阶段 综合查询[1])
- 模块管理常规功能自定义系统的设计与实现(36--终级阶段 综合查询[3])
- 家庭财务管理系统实战7-终结,源码