• Mybatis 使用Spring boot AOP +自定义注解+PageHelper实现分页


    最近项目又用到了Mybaits。在Mybatis中分页是个比较头疼的事,因为需要我们每次都写重复的sql。好在我们有PageHelper这样的分页工具,它可以拦截你的sql,从而进行分页操作。

    一、使用PageHelper分页和遇到的问题

    首先我们引入maven依赖。

            <pagehelper-version>1.2.5</pagehelper-version>
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>${pagehelper-version}</version>
            </dependency>
    

    然后假定你有个需要分页的查询方法selectList(),已经定义到mapper中了。这个方法是不分页的,因此你不必写任何分页的语句。

    service或者persistance层,你需要定义一个分页查询的方法selectPage()

      public PageBean<ReportTemplate> selectPage(PageBean<User> page) {
            //PageHelper设置当前页和页大小
            PageHelper.startPage(page.getPageNo(), page.getPageSize());
            PageHelper.orderBy(page.getSortedField());
            List<User> users= userMapper.selectPage(page.getKeyWords());
            PageInfo<User> pageInfo = new PageInfo<>(users);
            page.setCount(pageInfo.getTotal());
            page.setList(pageInfo.getList());
            return page;
        }
    

    这样我们就完成了PageHelper的分页。我们总结下使用PageHelper分页查询的步骤。

    1. 编写一个查询sql
    2. 编写一个分页查询方法,设置PageHelper的当前页和页大小
    3. 执行查询语句
    4. 查询完成后把PageInfo的数据填充到自定义的PageBean中

    以上四个步骤我们可以看出,除了第三步是真正需要我们手动写sql的,其他的步骤都是重复的过程。一个合格的程序员一定不要写重复的代码,那么我们有没有什么办法能去掉重复的代码呢?

    首先你可能想到把PageHelper的设置和自定义PageBean的数据填充分别封装成一个函数,然后每次调用两个方法就行了。虽然这样确实能减少一定的代码,但是仅仅是减少了部分代码,还是没达到我想的效果。我们想的是最后能像lombok那样简单易用,只需要一个注解就能省掉许多代码。那么我们应该如果做到呢?

    二、使用Spring boot AOP和自定义注解

    AOP的概念相信阅读本文的人应该都有所了解,但是真正用起来的不算多。我这里不想谈AOP的概念,因为你可以很容易地从百度/谷歌上找到相关的文章。我会在文章的最后放上我查阅过的文章链接,以供你参考。

    AOP的作用就是无侵入地实现部分功能,例如日志记录,操作记录等。
    首先引入Spring boot AOP依赖

            <!-- 引入Spring boot AOP依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    

    然后我们先不管AOP的事,我们先定义一个我们需要的注解。一般来说只使用AOP就可以无侵入地实现分页,但是麻烦的一点在于AOP中切点匹配规则的扩展性不够强。尤其是我们开发过程中添加新功能往往会新增一个package,这就可能导致AOP的失效。因此我们在此处使用自定义注解,这样对于任意我们想分页的方法,只需要在上面加一个注解就能实现分页,这样用起来更灵活一些。

    自定义注解很简单,你无须写任何逻辑代码。我们可以如下定义一个注解。

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EnablePaging {
    }
    

    定义好注解之后仅仅意味着你能在方法上使用此注解了。当然,你可以通过类的操作获取到这个注解。但是这还无法实现分页操作。我们需要写AOP的相关代码来实现分页操作了。

    package org.flow.approval.annotation;
    
    import com.github.pagehelper.PageHelper;
    import com.google.common.base.Strings;
    import com.sino.common.util.StringUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.flow.bean.PageBean;
    import org.springframework.stereotype.Component;
    
    import java.util.Objects;
    
    /**
     * @Description: Mybatis排序的切入点实现方法
     * @Author: gaoyakang
     * @Version: 1.0
     * @Create Date Time: 2020-05-07 17:52
     * @Update Date Time:
     * @see EnablePaging
     */
    
    @Aspect
    @Slf4j
    @Component
    public class MybatisPageEvent {
        /**
         * 启用分页实现类
         *
         * @param point 切入点
         * @return 切入点的执行结果
         * @throws Throwable 可能出现的异常
         */
        @Around("@annotation(EnablePaging)")
        public Object doPaging(ProceedingJoinPoint point) throws Throwable {
            Object[] args = point.getArgs();
            //获取第一个参数
            Object obj = args[0];
            //仅仅当第一个参数为PageBean的时候才执行分页
            if (obj instanceof PageBean) {
                PageBean pageBean = (PageBean) obj;
                Integer pageNum = pageBean.getPageNo();
                Integer pageSize = pageBean.getPageSize();
                if (Objects.isNull(pageSize)) {
                    pageSize = 10;
                }
                if (Objects.isNull(pageNum)) {
                    pageNum = 1;
                }
                //使用PageHelper进行分页
                PageHelper.startPage(pageNum, pageSize);
                log.info("启动分页方法,当前分页页号为:{},页大小:{}", pageNum, pageSize);
            }
            return point.proceed(args);
        }
    }
    
    

    这里的代码也很简单。但是你需要按照你的需要来写,我认为照搬代码毕竟不可取。因为在我的项目中,我定义了一个PageBean,用来保存分页的一些信息,如当前页、页大小、分页后的数据、总数等信息。因此在我的项目中所有分页的方法,第一个参数必须是PageBean。在你的项目中,可能你是直接传rawType参数到方法中的,这里需要你按照自己的需要进行处理。

    因为这里是环绕通知,所以我们也可以对方法调用之后的结果进行处理。

            //分页逻辑省略......
            Object result=point.proceed(args);
            if(resultinstanceof List) {
                List objList = (List) result;
                PageInfo pageInfo = new PageInfo<>(objList);
                return pageInfo;
            }
    

    实现上述操作之后的分页方法就变得简单多了

        @Override
        @EnablePaging
        public PageBean<User> selectPage(PageBean<User> page) {
            List<User> list = userMapper.selectPageData(page.getKeyWords());
            PageInfo<User> pageInfo = new PageInfo<>(list);
            //我的项目中因为需要特殊的操作,因此没有使用后置通知处理
            page.setCount((int) pageInfo.getTotal());
            page.setList(pageInfo.getList());
            return page;
        }
    

    三、需要注意的是......

    这里我遇到的问题是,在一些情况下AOP会失效。例如我现在有ControllerA,ServiceB,ServiceB中的方法fun1和fun2。这个三个层次,A调用了fun1(),fun1()调用fun2()。这种情况下AOP失效了。但是直接调用fun2()可以完美分页。暂时我还没发现为何会出现这种问题,如果你有任何见解或者看法,可以在评论区留言。

    参考文章

    博客园:Spring AOP + PageHelper分页
    博客园:Spring Boot:实现MyBatis分页
    Java获取类方法上的注解
    比较 Spring AOP 与 AspectJ
    柠檬五个半:Spring AOP SpringBoot集成

  • 相关阅读:
    PAT查找题---1032 挖掘机技术哪家强 (20分)
    PAT查找题---1028 人口普查 (20分)
    PAT查找题---1004 成绩排名 (20分)
    01_1JAVA简介
    01考试简介
    shell时间变量拼接问题
    如何将oracle查询的结果传输给变量
    生产环境邮件问题总结
    mutt+msmtp做linux邮件客户端
    linux配置邮件客户端
  • 原文地址:https://www.cnblogs.com/rever/p/12853796.html
Copyright © 2020-2023  润新知