成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從 PageHelper 學(xué)到的不侵入 Signature 的 AOP

trigkit4 / 1648人閱讀

摘要:從學(xué)到的不侵入的前言最近搭新項目框架,之前的攔截器都是自己寫的,一般是有個類型做判斷是否增加分頁。發(fā)現(xiàn)開源項目不需要侵入方法的就可以做分頁,特此來源碼分析一下。

從 PageHelper 學(xué)到的不侵入 Signature 的 AOP 前言

最近搭新項目框架,之前 Mybatis 的攔截器都是自己寫的,一般是有個 Page 類型做判斷是否增加分頁 sql。但是這樣同樣的業(yè)務(wù)開放給頁面和 api 可能要寫兩個,一種帶分頁類型 Page 一種不帶分頁。
發(fā)現(xiàn)開源項目 PageHelper 不需要侵入方法的 Signature 就可以做分頁,特此來源碼分析一下。

P.S: 看后面的源碼分析最好能懂 mybatis 得攔截器分頁插件原理

PageHelper 的簡答使用
    public PageInfo listRpmDetailByCondition(String filename, Date startTime, Date endTime,
                                                    Integer categoryId, Integer ostypeId, Integer statusId,
                                                    Integer pageNo, Integer pageSize) {
        PageHelper.startPage(pageNo, pageSize);
        List result = rpmDetailMapper.listRpmDetailByCondition(filename, startTime, endTime, categoryId,
                ostypeId, statusId);
        return new PageInfo(result);
    }
PageHelper.startPage 解析
    public static  Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page page = new Page(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        Page oldPage = getLocalPage();
        if (oldPage != null && oldPage.isOrderByOnly()) {
            page.setOrderBy(oldPage.getOrderBy());
        }

        setLocalPage(page);
        return page;
    }
    protected static final ThreadLocal LOCAL_PAGE = new ThreadLocal();
    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

可以看出在真正使用分頁前,生成了一個 page 對象,然后放入了 ThreadLocal 中,這個思想很巧妙,利用每個請求 Web 服務(wù),每個請求由一個線程處理的原理。利用線程來決定是否進行分頁。

是否用 ThreadLocal 來判斷是否分頁的猜想?
        //調(diào)用方法判斷是否需要進行分頁,如果不需要,直接返回結(jié)果
            if (!dialect.skip(ms, parameter, rowBounds)) {
                //判斷是否需要進行 count 查詢
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    //查詢總數(shù)
                    Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    //處理查詢總數(shù),返回 true 時繼續(xù)分頁查詢,false 時直接返回
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        //當查詢總數(shù)為 0 時,直接返回空的結(jié)果
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }
                }
                resultList = ExecutorUtil.pageQuery(dialect, executor,
                        ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                //rowBounds用參數(shù)值,不使用分頁插件處理時,仍然支持默認的內(nèi)存分頁
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
 @Override
    private PageParams pageParams;
    public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
        if (ms.getId().endsWith(MSUtils.COUNT)) {
            throw new RuntimeException("在系統(tǒng)中發(fā)現(xiàn)了多個分頁插件,請檢查系統(tǒng)配置!");
        }
        Page page = pageParams.getPage(parameterObject, rowBounds);
        if (page == null) {
            return true;
        } else {
            //設(shè)置默認的 count 列
            if (StringUtil.isEmpty(page.getCountColumn())) {
                page.setCountColumn(pageParams.getCountColumn());
            }
            autoDialect.initDelegateDialect(ms);
            return false;
        }
    }
    /**
     * 獲取分頁參數(shù)
     *
     * @param parameterObject
     * @param rowBounds
     * @return
     */
    public Page getPage(Object parameterObject, RowBounds rowBounds) {
        Page page = PageHelper.getLocalPage();
        if (page == null) {
            if (rowBounds != RowBounds.DEFAULT) {
                if (offsetAsPageNum) {
                    page = new Page(rowBounds.getOffset(), rowBounds.getLimit(), rowBoundsWithCount);
                } else {
                    page = new Page(new int[]{rowBounds.getOffset(), rowBounds.getLimit()}, rowBoundsWithCount);
                    //offsetAsPageNum=false的時候,由于PageNum問題,不能使用reasonable,這里會強制為false
                    page.setReasonable(false);
                }
                if(rowBounds instanceof PageRowBounds){
                    PageRowBounds pageRowBounds = (PageRowBounds)rowBounds;
                    page.setCount(pageRowBounds.getCount() == null || pageRowBounds.getCount());
                }
            } else if(parameterObject instanceof IPage || supportMethodsArguments){
                try {
                    page = PageObjectUtil.getPageFromObject(parameterObject, false);
                } catch (Exception e) {
                    return null;
                }
            }
            if(page == null){
                return null;
            }
            PageHelper.setLocalPage(page);
        }
        //分頁合理化
        if (page.getReasonable() == null) {
            page.setReasonable(reasonable);
        }
        //當設(shè)置為true的時候,如果pagesize設(shè)置為0(或RowBounds的limit=0),就不執(zhí)行分頁,返回全部結(jié)果
        if (page.getPageSizeZero() == null) {
            page.setPageSizeZero(pageSizeZero);
        }
        return page;
    }
    /**
     * 獲取 Page 參數(shù)
     *
     * @return
     */
    public static  Page getLocalPage() {
        return LOCAL_PAGE.get();
    }

果然如此,至此真相已經(jīng)揭開。

怎么保證之后的 sql 不使用分頁呢?

如: 先查出最近一個月注冊的 10 個用戶,再根據(jù)這些用戶查出他們所有的訂單。第一次查詢需要分頁,第二次并不需要

來看看作者怎么實現(xiàn)的?

    try{
        # 分頁攔截邏輯
    } finally {
         dialect.afterAll();
    }

    @Override
  public void afterAll() {
      //這個方法即使不分頁也會被執(zhí)行,所以要判斷 null
      AbstractHelperDialect delegate = autoDialect.getDelegate();
      if (delegate != null) {
          delegate.afterAll();
          autoDialect.clearDelegate();
      }
      clearPage();
  }
   /**
   * 移除本地變量
   */
  public static void clearPage() {
      LOCAL_PAGE.remove();
  }
總結(jié)邏輯

將分頁對象放入 ThreadLocal

根據(jù) ThreadLocal 判斷是否進行分頁

無論分頁與不分頁都需要清除 ThreadLocal

備注
            
                com.github.pagehelper
                pagehelper
                5.1.8
            

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74179.html

相關(guān)文章

  • 造個輪子,我學(xué)到了什么

    摘要:閱讀原文造個輪子我學(xué)到了什么聽說的最多的是不是不要重復(fù)的造輪子不要被這句話蒙騙了,這句話應(yīng)該還沒說完整,在什么情況下不要造輪子實際項目中由于工期和質(zhì)量原因,肯定不希望你造輪子,你造輪子花費時間且質(zhì)量不如現(xiàn)有的輪子。 閱讀原文:造個輪子,我學(xué)到了什么 聽說的最多的是不是不要重復(fù)的造輪子?不要被這句話蒙騙了,這句話應(yīng)該還沒說完整,在什么情況下不要造輪子?實際項目中由于工期和質(zhì)量原因,肯定不...

    Acceml 評論0 收藏0
  • 手把手教你如何優(yōu)雅使用Aop記錄帶參數(shù)復(fù)雜Web接口日志

    摘要:幾乎每一個接口被調(diào)用后,都要記錄一條跟這個參數(shù)掛鉤的特定的日志到數(shù)據(jù)庫。我最終采用了的方式,采取攔截的請求的方式,來記錄日志。所有打上了這個注解的方法,將會記錄日志。那么如何從眾多可能的參數(shù)中,為當前的日志指定對應(yīng)的參數(shù)呢。 前言 不久前,因為需求的原因,需要實現(xiàn)一個操作日志。幾乎每一個接口被調(diào)用后,都要記錄一條跟這個參數(shù)掛鉤的特定的日志到數(shù)據(jù)庫。舉個例子,就比如禁言操作,日志中需要記...

    Loong_T 評論0 收藏0
  • 前端裝飾器,AOP使用

    摘要:中的裝飾在前端編程中,我們可以采用裝飾器,來實現(xiàn)編程。裝飾器使用我們先建立一個簡單的類,這個類的作用,就是在執(zhí)行的時候,打印出。至此,一個簡單的裝飾器范例已經(jīng)完成。 什么是裝飾器? 了解AOP 在學(xué)習(xí)js中的裝飾器之間,我們需要了解AOP(面向切面編程)編程思想。 AOP是一種可以通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)在不修改源代碼的情況下給程序動態(tài)統(tǒng)一添加功能的一種技術(shù)。AOP實際是Go...

    lewinlee 評論0 收藏0
  • 基于SpringBoot后臺管理系統(tǒng)(啟動類解析,開源世界真好)(一)

    摘要:目前只是一個后臺模塊,希望自己技能增強到一定時,可以把的融合進來。目錄第一站,分析了啟動類??匆姏],這個也是配置類,它聲明了視圖解析器地域解析器以及靜態(tài)資源的位置,想起來沒,就是前置,后置。程序啟動類我們點擊源碼看看。 Guns基于SpringBoot,致力于做更簡潔的后臺管理系統(tǒng),完美整合springmvc + shiro + 分頁插件PageHelper + 通用Mapper + ...

    SwordFly 評論0 收藏0
  • Java 代理模式與 AOP

    摘要:本文首發(fā)于作者最近在學(xué),研究了下和代理模式,寫點心得和大家分享下。所以下面來重點分析下代理模式。這里代理模式分為靜態(tài)代理和動態(tài)代理兩種,我們分別來看下。代理模式,代理,意味著有一方代替另一方完成一件事。 本文首發(fā)于 https://jaychen.cc作者 jaychen 最近在學(xué) Spring,研究了下 AOP 和代理模式,寫點心得和大家分享下。 AOP 先說下AOP,AOP 全稱 ...

    jk_v1 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<