博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis分页练手
阅读量:5894 次
发布时间:2019-06-19

本文共 9501 字,大约阅读时间需要 31 分钟。

最近碰到个需求,要做个透明的mybatis分页功能,描述如下:

目标:搜索列表的Controller action要和原先保持一样,并且返回的json需要有分页信息,如:

@ResponseBody@RequestMapping(value="/search", method={RequestMethod.POST})public List
search(@RequestBody SearchProjectCommand command){ List
projects=projectFetcher.search(command.getKey(), command.getFrom(), command.getTo()); return projects;}

 

返回信息:

{  "successful": true,  "message": null,  "messages": null,  "dateTime": 1505651777350,  "body": {    "totalCount": 2,    "totalPage": 1,    "records": [      {        "projectId": "1111",        "projectName": "11111111111111",        "title": "11111111111111"      },      {        "projectId": "22222",        "projectName": "222222",        "title": "222222"      }    ]  }}

  

关键点:

  1. 针对Controller方法的aop
  2. Mybatis interceptor && PagingContext保存分页信息
  3. ResponseBodyAdvice(用于在输出json之前加入通用格式)

开始之前,先来看看消息格式,以及某些限制,主要是针对分页pageIndex这种参数的传递:

public abstract class PagingCommand {    private int pageSize;    private int pageIndex;    public PagingCommand getPagingInfo()    {        return this;    }    public int getPageSize() {        if(pageSize<=0)            return Integer.MAX_VALUE;        return pageSize;    }    public void setPageSize(int pageSize) {        this.pageSize = pageSize;    }    public int getPageIndex() {        if(pageIndex<0)            return 0;        return pageIndex;    }    public void setPageIndex(int pageIndex) {        this.pageIndex = pageIndex;    }}public class PagingResponse {    private int totalCount;    private int totalPage;    private List records;    public int getTotalCount() {        return totalCount;    }    public void setTotalCount(int totalCount) {        this.totalCount = totalCount;    }    public List getRecords() {        return records;    }    public int getTotalPage() {        return totalPage;    }    public void setTotalPage(int totalPage) {        this.totalPage = totalPage;    }    public void setRecords(List records) {        this.records = records;    }}

PagingCommand是抽象类,所有的具体Command必须继承这个Command

PagingResponse是分页结果

  

先来看看横切入口AOP类:

1 @Aspect 2 @Component 3 public class PagingAop { 4     private static final Logger logger = LoggerFactory.getLogger(PagingAop.class); 5  6     @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") 7     public void controllerMethodPointcut() { 8     } 9 10     @Around("controllerMethodPointcut()")11     public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable {12 13         logger.info("Paging...");14 15         //找到是否具有PagingCommand的class作为输入参数16         //有,则放入PagingContext中17         for(Object arg:pjp.getArgs())18         {19             if(arg==null)20                 continue;21 22             logger.info(arg.getClass().toString());23             if(PagingCommand.class.isAssignableFrom(arg.getClass()))24             {25                 logger.info("需要分页行为");26                 PagingContext.setPagingCommand((PagingCommand)arg);27             }28             else29             {30                 logger.info("不需要分页行为");31             }32         }33 34         return pjp.proceed();35     }36 }

 

代码很容易识别,判断参数是否是继承自PagingCommand,只要有1个继承自PagingCommand就会设置相应参数到PagingContext来标识需要分页处理,下面看看这个Context类:

1 public final class PagingContext { 2     private static ThreadLocal
pagingCommand=new ThreadLocal
(); 3 private static ThreadLocal
totalCount=new ThreadLocal
(); 4 private static ThreadLocal
totalPage=new ThreadLocal
(); 5 6 public static void setPagingCommand(PagingCommand cmd) 7 { 8 pagingCommand.set(cmd); 9 }10 11 public static PagingCommand getPagingCommand()12 {13 return pagingCommand.get();14 }15 16 public static boolean isPagingCommandEmpty()17 {18 if(pagingCommand.get()==null)19 return true;20 21 return false;22 }23 24 25 public static int getTotalCount() {26 return totalCount.get();27 }28 29 public static void setTotalCount(int count) {30 totalCount.set(count);31 }32 33 public static boolean isTotalCountEmpty()34 {35 if(totalCount.get()==null)36 return true;37 38 return false;39 }40 41 42 public static int getTotalPage() {43 return totalPage.get();44 }45 46 public static void setTotalPage(int pages) {47 totalPage.set(pages);48 }49 }

 

针对各个线程的ThreadLocal变量,但是目前只支持普通的httprequest线程才能正常工作,ThreadPool的有问题,等以后再解决。

下面是核心的mybatis分页插件了:

1 @Intercepts({@Signature(type=Executor.class,method="query",args={ MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class/*, CacheKey.class, BoundSql.class*/})}) 2 public class PagingInterceptor implements Interceptor { 3     private static final Logger logger = LoggerFactory.getLogger(PagingInterceptor.class); 4  5     @Override 6     public Object intercept(Invocation invocation) throws Throwable { 7  8  9         logger.info("intercept.............");10 11         //判断是否需要分页行为, from PagingContext中12         if(PagingContext.isPagingCommandEmpty())13             return invocation.proceed();14 15         MappedStatement mappedStatement=(MappedStatement)invocation.getArgs()[0];16         Object parameter = invocation.getArgs()[1];17         BoundSql boundSql = mappedStatement.getBoundSql(parameter);18         String originalSql = boundSql.getSql().trim();19 20         //生成count sql,然后执行21         int totalCount = getTotalCount(mappedStatement, boundSql, originalSql);22         //set totalCount value to context23         PagingContext.setTotalCount(totalCount);24 25         int totalPages=calculateTotalPagesCount(totalCount, PagingContext.getPagingCommand().getPageSize());26         PagingContext.setTotalPage(totalPages);27 28         //生成分页limit sql,然后执行29         MappedStatement newMs = wrapPagedMappedStatement(mappedStatement, boundSql, originalSql);30         invocation.getArgs()[0]= newMs;31 32         return invocation.proceed();33     }34 35     private int calculateTotalPagesCount(int totalCount, int pageSize) {36         int pageCount=totalCount/pageSize;37 38         if(pageCount==0)39             return 1;40 41         if(pageCount*pageSize<=totalCount)42             return pageCount;43 44         return pageCount+1;45     }46 47     private MappedStatement wrapPagedMappedStatement(MappedStatement mappedStatement, BoundSql boundSql, String originalSql) {48         PagingCommand page= PagingContext.getPagingCommand();49         int offset = (page.getPageIndex()) * page.getPageSize();50         StringBuffer sb = new StringBuffer();51         sb.append(originalSql).append(" limit ").append(offset).append(",").append(page.getPageSize());52         BoundSql newBoundSql = MyBatisUtils.copyFromBoundSql(mappedStatement, boundSql, sb.toString());53         return MyBatisUtils.copyFromMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));54     }55 56     private int getTotalCount(MappedStatement mappedStatement, BoundSql boundSql, String originalSql) throws SQLException {57         Object parameterObject = boundSql.getParameterObject();58         String countSql = getCountSql(originalSql);59         Connection connection=mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection()  ;60         PreparedStatement countStmt = connection.prepareStatement(countSql);61         BoundSql countBS = MyBatisUtils.copyFromBoundSql(mappedStatement, boundSql, countSql);62         DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBS);63         parameterHandler.setParameters(countStmt);64         ResultSet rs = countStmt.executeQuery();65         int totalCount=0;66         if (rs.next()) {67             totalCount = rs.getInt(1);68         }69         rs.close();70         countStmt.close();71         connection.close();72         return totalCount;73     }74 75     private String getCountSql(String sql) {76         return "SELECT COUNT(1) FROM (" + sql + ") Mybatis_Pager_TBL_ALIAS";77     }78 79     @Override80     public Object plugin(Object o) {81         return Plugin.wrap(o, this);82     }83 84     @Override85     public void setProperties(Properties properties) {86 87     }88 }

 

最后就一步了,就是写一个ResponseBodyAdvice来根据判断是否分页输出,来返回json:

1 @ControllerAdvice 2 public class GlobalMessageResponseBodyAdvice implements ResponseBodyAdvice { 3  4     @Override 5     public boolean supports(MethodParameter methodParameter, Class aClass) { 6         return true; 7     } 8  9     @Override10     public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {11         12             Object payload = o;13 14             //判断是否需要分页15             if (isNeedPagingResponse()) {16                 PagingResponse response = new PagingResponse();17 18                 response.setTotalCount(PagingContext.getTotalCount());19                 response.setTotalPage(PagingContext.getTotalPage());20                 response.setRecords((List) payload);21 22                 payload = response;23             }24 25             NormalMessage msg = new NormalMessage();26             msg.setSuccessful(true);27             msg.setMessage(null);28             msg.setBody(payload);29             return msg;30         31     }32 33     public boolean isNeedPagingResponse() {34         if(PagingContext.isPagingCommandEmpty())35             return false;36 37         return true;38     }39 }

 

完成。

转载地址:http://jhnsx.baihongyu.com/

你可能感兴趣的文章
android系列4.Intent学习
查看>>
linux 试题
查看>>
Java,mysql String与date类型转换
查看>>
springboot初学---定时任务
查看>>
理论整理(三)
查看>>
T+开发者社区
查看>>
得到某个字段为空的所有记录
查看>>
GDOI2013 哈希和
查看>>
Android实用代码七段(一)
查看>>
转载:Posix线程编程指南(2)
查看>>
asp.net 6.aspx页面
查看>>
用正则截取自定义的字数,多余的用省略号代替
查看>>
(六)6.7 Neurons Networks whitening
查看>>
python读书笔记-《简明python教程》上
查看>>
求排列的逆序数(分治)
查看>>
[LeetCode]题解(python):138-Copy List with Random Pointer
查看>>
【ssoj2935】询问
查看>>
C#动态编译
查看>>
LINUX系统下的shell命令---diff、cut、sort、uniq等
查看>>
一个小型的文本分类系统-python(文末附语料,停用词文本文档,工程全部代码)...
查看>>