Mybatis拦截器和存储过程的数据库分页
1.1 Mybatis拦截器实现分页
主要思路:Mybatis拦截器常常会被用来进行分页处理。我们知道要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前也会产生一个包含Sql语句的Statement对象,而且对应的Sql语句是在Statement之前产生的,所以我们就可以在它成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用StatementHandler对象的prepare方法,即调用invocation.proceed()。更改Sql语句这个看起来很简单,而事实上来说的话就没那么直观,因为包括sql等其他属性在内的多个属性都没有对应的方法可以直接取到,它们对外部都是封闭的,是对象的私有属性,所以这里就需要引入反射机制来获取或者更改对象的私有属性的值了。对于分页而言,在拦截器里面我们常常还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。
用到的文件:Page实体 PagePlugin实现拦截器的分页插件 ReflectHelper 反射工具类
1.2 具体代码及说明
Page实体:
package main.java.com.jt.core.util;
import java.util.ArrayList;
import java.util.List;
/**
* 分页类
*/
public class Page<T> {
private Integer page=1;//当前页码
private Integer pagesize=20;//每页显示条数
private Integer total;//总数
private Integer from;//起始索引
private Integer to;//结束索引
private List<T> Rows = new ArrayList<T>();
public List getCommonList() {
return commonList;
}
public void setCommonList(List commonList) {
this.commonList = commonList;
}
private List<T> commonList = new ArrayList<T>();//通用集合,用于多条件查询
private boolean entityOrField; //true:需要分页的地方,传入的参数就是Page实体;false:需要分页的地方,传入的参数所代表的实体拥有Page属性
private String keywords;
private String pid;
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getPagesize() {
return pagesize;
}
public void setPagesize(Integer pagesize) {
this.pagesize = pagesize;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Integer getFrom() {
return (this.page-1)*this.pagesize+1;
}
public void setFrom(Integer from) {
this.from = from;
}
public Integer getTo() {
return this.page*this.pagesize;
}
public void setTo(Integer to) {
this.to = to;
}
public List<T> getRows() {
return Rows;
}
public void setRows(List<T> rows) {
Rows = rows;
}
public boolean isEntityOrField() {
return entityOrField;
}
public void setEntityOrField(boolean entityOrField) {
this.entityOrField = entityOrField;
}
public String getKeywords() {
return keywords;
}
public void setKeywords(String keywords) {
this.keywords = keywords;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
}
目前用到的page实体属性有4个 需要传入的属性2个
用到的属性 :page pagesize total entityOrField 需要传入 page pagesize (有默认值)
ReflectHelper 类:
package main.java.com.jt.core.util;
import java.lang.reflect.Field;
/**
* 说明:反射工具
*/
public class ReflectHelper {
/**
* 获取obj对象fieldName的Field
* @param obj
* @param fieldName
* @return
*/
public static Field getFieldByFieldName(Object obj, String fieldName) {
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass
.getSuperclass()) {
try {
return superClass.getDeclaredField(fieldName);//获取class的所有字段
} catch (NoSuchFieldException e) {
}
}
return null;
}
/**
* 获取obj对象fieldName的属性值
* @param obj
* @param fieldName
* @return
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static Object getValueByFieldName(Object obj, String fieldName)
throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field field = getFieldByFieldName(obj, fieldName);
Object value = null;
if(field!=null){
if (field.isAccessible()) {
value = field.get(obj);
} else {
field.setAccessible(true);
value = field.get(obj);
field.setAccessible(false);
}
}
return value;
}
/**
* 设置obj对象fieldName的属性值
* @param obj
* @param fieldName
* @param value
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void setValueByFieldName(Object obj, String fieldName,
Object value) throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
if (field.isAccessible()) {
field.set(obj, value);
} else {
field.setAccessible(true);
field.set(obj, value);
field.setAccessible(false);
}
}
}
说明:反射获取class的所有私有属性 因为Mybatis的prepare方法的所有属性都是私有的 没有对外提供get和set方法 只能通过运行过程中反射取到和修改属性的值。
PagePlugin 类:
package main.java.com.jt.core.util;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import javax.xml.bind.PropertyException;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
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.property.PropertyTokenizer;
import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
*
* 类名称:分页插件
*/
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PagePlugin implements Interceptor {
private static String dialect = ""; //数据库方言
private static String pageSqlId = ""; //mapper.xml中需要拦截的ID(正则匹配)
//对于StatementHandler其实只有两个实现类,一个是RoutingStatementHandler,另一个是抽象类BaseStatementHandler,
//BaseStatementHandler有三个子类,分别是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler,
//SimpleStatementHandler是用于处理Statement的,PreparedStatementHandler是处理PreparedStatement的,而CallableStatementHandler是
//处理CallableStatement的。Mybatis在进行Sql语句处理的时候都是建立的RoutingStatementHandler,而在RoutingStatementHandler里面拥有一个
//StatementHandler类型的delegate属性,RoutingStatementHandler会依据Statement的不同建立对应的BaseStatementHandler,即SimpleStatementHandler、
//PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。
//我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截StatementHandler接口的prepare方法,又因为Mybatis只有在建立RoutingStatementHandler的时候
//是通过Interceptor的plugin方法进行包裹的,所以我们这里拦截到的目标对象肯定是RoutingStatementHandler对象。
public Object intercept(Invocation ivk) throws Throwable {
// TODO Auto-generated method stub
if(ivk.getTarget() instanceof RoutingStatementHandler){
RoutingStatementHandler statementHandler = (RoutingStatementHandler)ivk.getTarget();
BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler, "delegate");
//通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement");
if(mappedStatement.getId().matches(pageSqlId)){ //拦截需要分页的SQL
BoundSql boundSql = delegate.getBoundSql();
Object parameterObject = boundSql.getParameterObject();//分页SQL<select>中parameterType属性对应的实体参数,即Mapper接口中执行分页方法的参数,该参数不得为空
if(parameterObject==null){
throw new NullPointerException("parameterObject尚未实例化!");
}else{
//拦截到的prepare方法参数是一个Connection对象
Connection connection = (Connection) ivk.getArgs()[0];
//获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
String sql = boundSql.getSql();
//String countSql = "select count(0) from (" + sql+ ") as tmp_count"; //记录统计
String fhsql = sql;
String countSql = "select count(0) from (" + fhsql+ ") tmp_count"; //记录统计 == oracle 加 as 报错(SQL command not properly ended)
//通过connection建立一个countSql对应的PreparedStatement对象。
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(),countSql,boundSql.getParameterMappings(),parameterObject);
//通过parameterHandler给PreparedStatement对象设置参数
setParameters(countStmt,mappedStatement,countBS,parameterObject);
//之后就是执行获取总记录数的Sql语句和获取结果了。
ResultSet rs = countStmt.executeQuery();
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
rs.close();
countStmt.close();
//System.out.println(count);
Page page = null;
if(parameterObject instanceof Page){ //参数就是Page实体
page = (Page) parameterObject;
page.setEntityOrField(true);
page.setTotal(count);
}else{ //参数为某个实体,该实体拥有Page属性
Field pageField = ReflectHelper.getFieldByFieldName(parameterObject,"page");
if(pageField!=null){
page = (Page) ReflectHelper.getValueByFieldName(parameterObject,"page");
if(page==null)
page = new Page();
page.setEntityOrField(false);
page.setTotal(count);
ReflectHelper.setValueByFieldName(parameterObject,"page", page); //通过反射,对实体对象设置分页对象
}else{
throw new NoSuchFieldException(parameterObject.getClass().getName()+"不存在 page 属性!");
}
}
//获取分页Sql语句
String pageSql = generatePageSql(sql,page);
//利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); //将分页sql语句反射回BoundSql.
}
}
}
return ivk.proceed();
}
/**
* 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler
* @param ps
* @param mappedStatement
* @param boundSql
* @param parameterObject
* @throws SQLException
*/
private void setParameters(PreparedStatement ps,MappedStatement mappedStatement,BoundSql boundSql,Object parameterObject) throws SQLException {
//获取对应的BoundSql,这个BoundSql其实跟我们利用StatementHandler获取到的BoundSql是同一个对象。
//delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//通过BoundSql获取对应的参数映射
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
Configuration configuration = mappedStatement.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
MetaObject metaObject = parameterObject == null ? null: configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
PropertyTokenizer prop = new PropertyTokenizer(propertyName);
if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName())) {
value = boundSql.getAdditionalParameter(prop.getName());
if (value != null) {
value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
}
} else {
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null) {
throw new ExecutorException("There was no TypeHandler found for parameter "+ propertyName + " of statement "+ mappedStatement.getId());
}
typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
}
}
}
}
/**
* 根据数据库方言,生成特定的分页sql
* @param sql
* @param page
* @return
*/
private String generatePageSql(String sql,Page page){
if(page!=null && Tools.notEmpty(dialect)){
StringBuffer pageSql = new StringBuffer();
if("mysql".equals(dialect)){
pageSql.append(sql);
pageSql.append(" limit "+(page.getPage()-1)*page.getPagesize()+","+page.getPagesize());
}else if ("oracle".equals(dialect)){
//计算第一条记录的位置,Oracle分页是通过rownum进行的,而rownum是从1开始的
int offset = (page.getPage() - 1) * page.getPagesize() + 1;
pageSql.insert(0, "select u.*, rownum r from (").append(") u where rownum < ").append(offset + page.getPagesize());
pageSql.insert(0, "select * from (").append(") where r >= ").append(offset);
//上面的Sql语句拼接之后大概是这个样子:
//select * from (select u.*, rownum r from (select * from t_user) u where rownum < 31) where r >= 16
}
return pageSql.toString();
}else{
return sql;
}
}
/**
* 最先进入的方法
* @param arg0
* @return
*/
public Object plugin(Object arg0) {
// TODO Auto-generated method stub
return Plugin.wrap(arg0, this);
}
public void setProperties(Properties p) {
dialect = p.getProperty("dialect");
if (isEmpty(dialect)) {
try {
throw new PropertyException("dialect property is not found!");
} catch (PropertyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
pageSqlId = p.getProperty("pageSqlId");
if (isEmpty(pageSqlId)) {
try {
throw new PropertyException("pageSqlId property is not found!");
} catch (PropertyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 检测字符串是否为空(null,"","null")
* @param s
* @return 为空则返回true,不否则返回false
*/
public static boolean isEmpty(String s){
return s==null || "".equals(s) || "null".equals(s);
}
}
具体说明都在注释中
1.3 补充说明
最好让这三个文件同包调用
配置数据库方言和拦截字段在Mybatis-configuration.xml 配置文件中
具体如下:
<plugins>
<plugin interceptor="main.java.com.jt.core.util.PagePlugin">
<property name="dialect" value="mysql"/>
<property name="pageSqlId" value=".*test$"/>
</plugin>
</plugins>
这个操作是注册拦截器
其中,dialect是数据库方言 现在支持mysql和oracle两种数据库的分页 pageSqlId是拦截的sql id 匹配Mapper接口中的方法名 和 Mapper.xml中的select id (使用正则匹配的方式 如例子中的贪婪匹配所有以test结尾的字段)。
最后需要注意除了分页以外的其他数据库独有的写法需要避免。
1.4 实现例子
ZhaoBiaoMapper数据层接口的方法:
//测试mybatis拦截器
ArrayList<ZhaoBiao> zbpagetest(ZhaoBiao zhaoBiao);
映射的Mapper.xml中的实现:
<select id="zbpagetest" parameterType="main.java.com.jt.bid.entity.ZhaoBiao" resultType="main.java.com.jt.bid.entity.ZhaoBiao">
SELECT * FROM t_zb
</select>
业务层:
ArrayList<ZhaoBiao> listzb(ZhaoBiao zhaoBiao);
实现类:
@Override
public ArrayList<ZhaoBiao> listzb(ZhaoBiao zhaoBiao) {
return zhaoBiaoMapper.zbpagetest(zhaoBiao);
}
控制层:
@RequestMapping("/test")
@ResponseBody
public void test(){
Page page = new Page();
page.setPagesize(20);
ZhaoBiao zhaoBiao =new ZhaoBiao();
zhaoBiao.setPage(page);
ArrayList<ZhaoBiao>list = zhaoBiaoService.listzb(zhaoBiao);
Integer total = zhaoBiao.getPage().getTotal();
}
运行测试 结果:
如果没有条件查询的话 可以直接传入Page实体 也就是说替换zhaoBiao实体.
2.1 存储过程实现分页
具体思路:通过提前对数据库构造存储过程,然后传入必要的参数,实现程序中不出现数据库的关键字段。
2.2 高度优化的通用Mysql数据库分页存储过程
use databasename;
delimiter $$
drop procedure if exists prc_page_result $$
create procedure prc_page_result (
in currpage int, #起止页数
in columns varchar(500),#查询字段 没有特殊条件的写"*"
in tablename varchar(500),# 表名称
in sCondition varchar(500),# 查询条件 就是where 不用写where 但是要写and 后期考虑是不是写成where 1=1 后面就直接加and
in order_field varchar(100),# 排序的字段 一定要写 如果没有特殊要求直接写按主键排序 其他字段排序影响性能
in asc_field int,#排序的顺序 1代表的是逆序 desc 其他的代表正序 asc 注意这里只能是int类型
in primary_field varchar(100),# 主键 表格主键 必填 影响后面查询速度
in pagesize int #一页显示数据的条数
)
begin
declare sTemp varchar(1000); #初始化数据 需要的sql语句 下面两个作用相同
declare sSql varchar(4000);
declare sOrder varchar(1000);
if asc_field = 1 then # 排序是1的时候 反向排列
set sOrder = concat(' order by ', order_field, ' desc ');# 设置排序语句
set sTemp = '<(select min'; # 逆序的时候查最小
else
set sOrder = concat(' order by ', order_field, ' asc ');# 正序的order语句
set sTemp = '>(select max';# 正序查最大
end if;
if currpage = 1 then # 第一页
if sCondition <> '' then # 查询条件不为空的时候
set sSql = concat('select ', columns, ' from ', tablename, ' where ');# 拼接查询语句
set sSql = concat(sSql, sCondition, sOrder, ' limit ?'); # select * from t_zz where 1=1 order by zjid limit
else
set sSql = concat('select ', columns, ' from ', tablename, sOrder, ' limit ?');
end if;
else
if sCondition <> '' then
set sSql = concat('select ', columns, ' from ', tablename);
set sSql = concat(sSql, ' where ', sCondition, ' and ', primary_field, sTemp); # select * from t_zb where 1=1 and zbid<(select min
set sSql = concat(sSql, '(', primary_field, ')', ' from (select ');# select * from t_zb where 1=1 and zbid<(select min(zbid) from (select
set sSql = concat(sSql, ' ', primary_field, ' from ', tablename, sOrder);#select * from t_zb where 1=1 and zbid<(select min(zbid) from (select zbid from t_zb order by zjid desc
set sSql = concat(sSql, ' limit ', (currpage-1)*pagesize, ') as tabtemp)', sOrder);#select * from t_zb where 1=1 and zbid<(select min(zbid) from (select zbid from t_zb order by zjid desc limit 20) as tabtemp) order by zbid desc
set sSql = concat(sSql, ' limit ?');#select * from t_zb where 1=1 and zbid<(select min(zbid) from (select zbid from t_zb order by zjid desc limit 20) as tabtemp) order by zbid desc limit ?
else
set sSql = concat('select ', columns, ' from ', tablename);
set sSql = concat(sSql, ' where ', primary_field, sTemp);
set sSql = concat(sSql, '(', primary_field, ')', ' from (select ');
set sSql = concat(sSql, ' ', primary_field, ' from ', tablename, sOrder);
set sSql = concat(sSql, ' limit ', (currpage-1)*pagesize, ') as tabtemp)', sOrder);
set sSql = concat(sSql, ' limit ?');
end if;
end if;
set @iPageSize = pagesize; # 设置动态数据
set @sQuery = sSql; # 查询语句
prepare stmt from @sQuery; # 预查询
execute stmt using @iPageSize;#动态塞入参数 mysql5.1版本以前不支持
end;
$$
delimiter ;
解析出来:
需要提前在数据库命令符界面 复制代码并输入这段存储过程
使用Mybatis进行操作:
数据层接口:
ArrayList<ZhaoBiao> cqgc(HashMap map);
映射:
<select id="cqgc" statementType="CALLABLE" parameterType="HashMap" resultType="main.java.com.jt.bid.entity.ZhaoBiao">
call prc_page_result(
#{currpage,jdbcType=INTEGER,mode = IN},
#{columns,jdbcType=VARCHAR,mode =IN},
#{tablename,jdbcType=VARCHAR,mode = IN},
#{sCondition,jdbcType=VARCHAR,mode = IN},
#{order_field,jdbcType=VARCHAR, mode = IN},
#{asc_field,jdbcType=INTEGER,mode=IN},
#{primary_field,jdbcType = VARCHAR , mode =IN},
#{pagesize,jdbcType = INTEGER , mode =IN}
)
</select>
业务层:
Integer ocunttest();//前面返回参数没有意义,仅仅测试
实现类:
@Override
public Integer ocunttest() {
HashMap map = new HashMap();
map.put("currpage",1);
map.put("columns","*");
map.put("tablename","t_zb");
map.put("sCondition","");
map.put("order_field","zbid");
map.put("asc_field",1);
map.put("primary_field","zbid");
map.put("pagesize",20);
ArrayList<ZhaoBiao> list = zhaoBiaoMapper.cqgc(map);
System.out.println(list.toArray());
return null;
}
控制层:
@RequestMapping("/test1")
@ResponseBody
public void tese1(){
zhaoBiaoService.ocunttest();
}
测试结果:
2.3 简化后的通用Mysql存储过程
use databasename;
delimiter $$
drop procedure if exists prc_page_onetable $$
create procedure prc_page_onetable (
in currpage int, #起止页数
in columns varchar(500),#查询字段 没有特殊条件的写"*"
in tablename varchar(500),# 表名称
in sCondition varchar(500),# 查询条件 就是where 不用写where 但是要写and 后期考虑是不是写成where 1=1 后面就直接加and
in order_field varchar(100),# 排序的字段 一定要写 如果没有特殊要求直接写按主键排序 其他字段排序影响性能
in pagesize int #一页显示数据的条数
)
begin
declare sSql varchar(4000);
if sCondition <> '' then
set sSql = concat('select ', columns, ' from ', tablename);
set sSql = concat(sSql, ' where ',sCondition);
set sSql = concat(sSql,' ',order_field,' limit ',(currpage-1)*pagesize,',',pagesize);
else
set sSql = concat('select ', columns, ' from ', tablename);
set sSql = concat(sSql,' ',order_field,' limit ',(currpage-1)*pagesize,',',pagesize);
end if;
set @sQuery = sSql; # 查询语句
prepare stmt from @sQuery; # 预查询
execute stmt ;
end;
$$
delimiter ;
Mapper接口中的方法:
ArrayList<ZhaoBiao> cqgcforone(HashMap map);
映射:
<select id="cqgcforone" statementType="CALLABLE" parameterType="HashMap" resultType="main.java.com.jt.bid.entity.ZhaoBiao">
call prc_page_onetable(
#{currpage,jdbcType=INTEGER,mode = IN},
#{columns,jdbcType=VARCHAR,mode =IN},
#{tablename,jdbcType=VARCHAR,mode = IN},
#{sCondition,jdbcType=VARCHAR,mode = IN},
#{order_field,jdbcType=VARCHAR, mode = IN},
#{pagesize,jdbcType = INTEGER , mode =IN}
)
</select>
业务层:
void ccgc();
实现类:
@Override
public void ccgc() {
String mc = "b";
HashMap map = new HashMap();
map.put("currpage",1);
map.put("columns","*");
map.put("tablename","t_zb");
map.put("sCondition","mc like CONCAT('%','"+mc+"','%')");
map.put("order_field","");
map.put("pagesize",20);
ArrayList list = zhaoBiaoMapper.cqgcforone(map);
System.out.println(list.toArray());
}
测试结果:(控制层和上面的存储过程的类似)
最后总结 放上去的项目是一个没有被使用的项目作为演示
两种分页方式呢 个人认为 拦截器的应用可能会更加适合现在的行情 现在的很多思路都是做成拦截器 像现在很火的权限框架shiro 内部也需要根据业务去实现很多自定义的拦截器
而关于存储过程 个人认为可以将大数据的优化sql写成存储过程 这样也可以减少人力的投入 也实现了sql 的复用