淺析PageHelper踩坑:不安全分頁導致的問題


  這個問題的起因是后端日志經常有一個報錯:Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: syntax error at or near "LIMIT"。

  但是奇怪的是那個查詢方法根本就沒有 limit,其次不清楚原因。后來才清楚為什么,因為我們使用了 分頁插件 PageHelper,在使用 PageHelper 的時候如果不注意,就會導致不安全的分頁問題。

一、結論先行

  主要原因:PageHelper 使用了靜態的 ThreadLocal 參數,讓線程綁定了分頁參數, 這個參數如果沒被使用就會一直留在那兒,當這個線程再次被使用時,就可能導致不該分頁的方法去消費這個分頁參數,這就產生了莫名其妙的分頁。

二、造成原因:

  下面這些內容都可以在官網描述里找到:https://pagehelper.github.io/docs/howtouse/

  什么時候會導致不安全的分頁? PageHelper 方法使用了靜態的 ThreadLocal 參數,分頁參數和線程是綁定的。

  只要你可以保證在 PageHelper 方法調用后緊跟 MyBatis 查詢方法,這就是安全的。因為 PageHelper 在 finally 代碼段中自動清除了 ThreadLocal 存儲的對象。

  如果代碼在進入 Executor 前發生異常,就會導致線程不可用,這屬於人為的 Bug(例如接口方法和 XML 中的不匹配,導致找不到 MappedStatement 時), 這種情況由於線程不可用,也不會導致 ThreadLocal 參數被錯誤的使用。

  但是如果你寫出下面這樣的代碼,就是不安全的用法:

PageHelper.setPage(1,10); if(param!=null){ list=userMapper.selectIf(param) }eles{ list=new ArrayList<User>(); }

  這樣子如果 param 沒有消費到,那么接下來如果進去了其他方法使用了 select 方法就會將這個page參數帶進去,被消費;

  如何改進呢?

if(param!=null){ PageHelper.setPage(1,10); list=userMapper.selectIf(param) }eles{ list=new ArrayList<User>(); }

  避坑:讓這個分頁參數緊跟查詢,可以查就建立;也就是保證被消費;或者在 finally 調用 PageHelper.clearPage(); 清除。

  這種寫法就能保證安全。如果你對此不放心,你可以手動清理 ThreadLocal 存儲的分頁參數,可以像下面這樣使用:

List<Country> list; if(param1 != null){ PageHelper.startPage(1, 10); try{ list = countryMapper.selectAll(); } finally { PageHelper.clearPage(); } } else { list = new ArrayList<Country>(); } // 這么寫很不好看,而且沒有必要

三、其他問題介紹

  最近在項目中頻繁出現 sqlException,報錯有兩種情況,一種是拼接了order by ‘column’,一種是拼接了limit ‘num’;奇怪的是我的sql語句中並沒有這些參數。

1、error1:在這里出現了未識別的字段

2、error2:sql語句中已經寫了limit,pagehelper又拼接了一次,出現 'limit 1 limit 10’的情況;

  通過百度,了解到PageHelper使用了靜態的ThreadLocal參數,分頁參數和線程是綁定的;當分頁參數沒有被消費時,會一直存在threadlocal中,在下一次執行的sql中會拼接這些參數。

  那么怎么避免這種情況:分頁參數緊跟 list 查詢。如果先寫分頁,又寫了別的判斷邏輯,沒有執行 list 查詢時,那么分頁參數就會在threadlocal中,下次執行sql會消費這些參數,就會導致“不安全分頁”。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM