讓MyBatis Generator產生的程式碼支援分頁

  • 2020 年 2 月 14 日
  • 筆記

本文提供一種方法,讓MyBatis Generator產生的程式碼支援分頁, 適用於MySQL。

01

分析

如果要獲取分頁資訊,使用MySQL語句,我們需要怎麼做呢?

select * from t_user limit 0 , 2

在MySQL系統中,如果要完成一個分頁,我們需要指定limit的值,也就是需要指定兩個數,第一個指定從什麼地方開始(示例中為0);另一個指定需要獲取多少條數據(示例中為2)。

  • 問題轉化

如果要使得產生的自動產生的程式碼具備分頁功能的話,那麼,Mapper對應的XML中select語句需要多增加兩個屬性值,比如:

  • limitStart (指定從什麼位置開始查找)
  • limitSize (指定找到多少條數據)

上述已經提到需要兩個值limitStartlimitSize,那麼,我們需要添加在哪裡才能有效果呢

  • 何處添加

以t_news表為例,創建表的SQL語句如下:

CREATE TABLE `t_news` (    `news_id` int(11) NOT NULL AUTO_INCREMENT,    `title` varchar(150) NOT NULL,    `content` text NOT NULL,    `brief_intro` varchar(255) DEFAULT NULL,    `pic_url` varchar(255) DEFAULT NULL,    `news_from` varchar(100) DEFAULT NULL,    `news_author` varchar(50) DEFAULT NULL,    `news_url` varchar(255) DEFAULT NULL,    `keywords` varchar(150) DEFAULT NULL,    `meta_desc` varchar(150) DEFAULT NULL,    `create_time` datetime DEFAULT NULL,    PRIMARY KEY (`news_id`)  ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

那麼,我們可以看到NewsMapper.java中查找列表數據都是通過Example來完成的。

    List<News> selectByExampleWithBLOBs(NewsExample example);        List<News> selectByExample(NewsExample example);

其中,selectByExampleWithBLOBs方法只有當數據表中的某一列需要存儲較大內容的時候,才會產生。來看一下判斷是否為BLOB列的源程式碼吧。摘自IntrospectedColumn類。

    public boolean isBLOBColumn() {          String typeName = getJdbcTypeName();            return "BINARY".equals(typeName) || "BLOB".equals(typeName) //$NON-NLS-1$ //$NON-NLS-2$                  || "CLOB".equals(typeName) || "LONGVARBINARY".equals(typeName) //$NON-NLS-1$ //$NON-NLS-2$                  || "LONGVARCHAR".equals(typeName) || "VARBINARY".equals(typeName); //$NON-NLS-1$ //$NON-NLS-2$      }

注意: 使用selectByExample方法是不會返回BLOB類型的欄位,如t_news新聞表中的content內容欄位。如果想返回content的值,那麼,需要使用selectByExampleWithBLOBs方法。

大家可以來看看如下內容感受一下。selectByExampleWithBLOBs中包含Blob_Column_List,而selectByExample沒有。

<select id="selectByExampleWithBLOBs" parameterType="my.mybatis.generator.auto.entity.NewsExample" resultMap="ResultMapWithBLOBs">      <!--        WARNING - @mbggenerated        This element is automatically generated by MyBatis Generator, do not modify.        This element was generated on Wed Nov 09 19:01:57 CST 2016.      -->      select      <if test="distinct">        distinct      </if>      <include refid="Base_Column_List" />      ,      <include refid="Blob_Column_List" />      from m_news      <if test="_parameter != null">        <include refid="Example_Where_Clause" />      </if>      <if test="orderByClause != null">        order by ${orderByClause}      </if>    </select>    <select id="selectByExample" parameterType="my.mybatis.generator.auto.entity.NewsExample" resultMap="BaseResultMap">      <!--        WARNING - @mbggenerated        This element is automatically generated by MyBatis Generator, do not modify.        This element was generated on Wed Nov 09 19:01:57 CST 2016.      -->      select      <if test="distinct">        distinct      </if>      <include refid="Base_Column_List" />      from m_news      <if test="_parameter != null">        <include refid="Example_Where_Clause" />      </if>      <if test="orderByClause != null">        order by ${orderByClause}      </if>    </select>

從上述簡單分析可以看出,limitStartlimitSize添加的地方有兩個:

  • 實體類對應的Example中需要添加。
  • XML文件中,selectByExampleselectByExampleWithBLOBs配置需要添加limitStartlimitSize屬性。

    <if test="limitStart != null and limitSize&gt;=0">        limit #{limitStart} , #{limitSize}      </if>

有了上述的分析之後,我們需要寫什麼就很清楚了 。

02

編碼

  • Example類具有分頁屬性

Example中包含兩個欄位limitStartlimitSize,並具有GetterSetter方法,如:

public class NewsExample {        protected Integer limitStart;        protected Integer limitSize;        public void setLimitStart(Integer limitStart) {          this.limitStart = limitStart;      }        public Integer getLimitStart() {          return limitStart;      }        public void setLimitSize(Integer limitSize) {          this.limitSize = limitSize;      }        public Integer getLimitSize() {          return limitSize;      }        //省略其它  }

增加一個私有方法addLimit用於在Example中創建欄位並生成Getter和Setter方法:

private void addLimit(TopLevelClass topLevelClass,        IntrospectedTable introspectedTable, String name) {        CommentGenerator commentGenerator = context.getCommentGenerator();        /**       * 創建成員變數       * 如protected Integer limitStart;       */      Field field = new Field();      field.setVisibility(JavaVisibility.PROTECTED);      field.setType(PrimitiveTypeWrapper.getIntegerInstance());      field.setName(name);      commentGenerator.addFieldComment(field, introspectedTable);      topLevelClass.addField(field);      /**       * 首字母大寫       */      char c = name.charAt(0);      String camel = Character.toUpperCase(c) + name.substring(1);        /**       * 添加Setter方法       */      Method method = new Method();      method.setVisibility(JavaVisibility.PUBLIC);      method.setName("set" + camel);      method.addParameter(new Parameter(PrimitiveTypeWrapper          .getIntegerInstance(), name));        StringBuilder sb = new StringBuilder();      sb.append("this.");      sb.append(name);      sb.append(" = ");      sb.append(name);      sb.append(";");      /**       * 如 this.limitStart = limitStart;       */      method.addBodyLine(sb.toString());        commentGenerator.addGeneralMethodComment(method, introspectedTable);      topLevelClass.addMethod(method);        /**       * 添加Getter Method 直接調用AbstractJavaGenerator的getGetter方法       */      Method getterMethod = AbstractJavaGenerator.getGetter(field);      commentGenerator.addGeneralMethodComment(getterMethod,          introspectedTable);      topLevelClass.addMethod(getterMethod);    }

其實,產生上述的程式碼並不難,因為MyBatis Generator本身就是在為生成的實體類添加變數和Getter Setter方法。

如:

AbstractJavaGenerator抽象類本身就有產生Getter方法的函數,直接調用即可。

public abstract class AbstractJavaGenerator extends AbstractGenerator {      public abstract List<CompilationUnit> getCompilationUnits();        public static Method getGetter(Field field) {          Method method = new Method();          method.setName(getGetterMethodName(field.getName(), field                  .getType()));          method.setReturnType(field.getType());          method.setVisibility(JavaVisibility.PUBLIC);          StringBuilder sb = new StringBuilder();          sb.append("return "); //$NON-NLS-1$          sb.append(field.getName());          sb.append(';');          method.addBodyLine(sb.toString());          return method;      }  }

另外, Setter方法的實現,可以參考AbstractJavaGenerator抽象類的getJavaBeansSetter方法,如:

  public Method getJavaBeansSetter(IntrospectedColumn introspectedColumn) {          FullyQualifiedJavaType fqjt = introspectedColumn                  .getFullyQualifiedJavaType();          String property = introspectedColumn.getJavaProperty();            Method method = new Method();          method.setVisibility(JavaVisibility.PUBLIC);          method.setName(getSetterMethodName(property));          method.addParameter(new Parameter(fqjt, property));          context.getCommentGenerator().addSetterComment(method,                  introspectedTable, introspectedColumn);            StringBuilder sb = new StringBuilder();          if (isTrimStringsEnabled() && introspectedColumn.isStringColumn()) {              sb.append("this."); //$NON-NLS-1$              sb.append(property);              sb.append(" = "); //$NON-NLS-1$              sb.append(property);              sb.append(" == null ? null : "); //$NON-NLS-1$              sb.append(property);              sb.append(".trim();"); //$NON-NLS-1$              method.addBodyLine(sb.toString());          } else {              sb.append("this."); //$NON-NLS-1$              sb.append(property);              sb.append(" = "); //$NON-NLS-1$              sb.append(property);              sb.append(';');              method.addBodyLine(sb.toString());          }            return method;      }

然後,重寫modelExampleClassGenerated產生的方法,如:

  @Override    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass,        IntrospectedTable introspectedTable) {      addLimit(topLevelClass, introspectedTable, "limitStart");      addLimit(topLevelClass, introspectedTable, "limitSize");      return super.modelExampleClassGenerated(topLevelClass,          introspectedTable);    }

這樣,Example改變就完成了。

  • XML文件支援分頁

接下來,我們需要對產生的XML的selectByExampleselectByExampleWithBLOBs方法添加limitStartlimitSize屬性。

為selectByExample添加limitStart和limitSize

  /**     * 為selectByExample添加limitStart和limitSize     */    @Override    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(        XmlElement element, IntrospectedTable introspectedTable) {      XmlElement isNotNullElement = new XmlElement("if");      isNotNullElement.addAttribute(new Attribute("test",          "limitStart != null and limitSize >= 0"));      isNotNullElement.addElement(new TextElement(          "limit #{limitStart} , #{limitSize}"));      element.addElement(isNotNullElement);      return super.sqlMapSelectByExampleWithoutBLOBsElementGenerated(element,          introspectedTable);    }

為selectByExampleWithBLOBs添加limitStart和limitSize

  /**     * 為selectByExampleWithBLOBs添加limitStart和limitSize     */    @Override    public boolean sqlMapSelectByExampleWithBLOBsElementGenerated(        XmlElement element, IntrospectedTable introspectedTable) {      XmlElement isNotNullElement = new XmlElement("if");      isNotNullElement.addAttribute(new Attribute("test",          "limitStart != null and limitSize >= 0"));      isNotNullElement.addElement(new TextElement(          "limit #{limitStart} , #{limitSize}"));      element.addElement(isNotNullElement);      return super.sqlMapSelectByExampleWithBLOBsElementGenerated(element,          introspectedTable);    }

MysqlPaginationPlugin類完整程式碼

package my.mabatis.example.plugin;    import java.util.List;    import org.mybatis.generator.api.CommentGenerator;  import org.mybatis.generator.api.IntrospectedTable;  import org.mybatis.generator.api.PluginAdapter;  import org.mybatis.generator.api.dom.java.Field;  import org.mybatis.generator.api.dom.java.JavaVisibility;  import org.mybatis.generator.api.dom.java.Method;  import org.mybatis.generator.api.dom.java.Parameter;  import org.mybatis.generator.api.dom.java.PrimitiveTypeWrapper;  import org.mybatis.generator.api.dom.java.TopLevelClass;  import org.mybatis.generator.api.dom.xml.Attribute;  import org.mybatis.generator.api.dom.xml.TextElement;  import org.mybatis.generator.api.dom.xml.XmlElement;  import org.mybatis.generator.codegen.AbstractJavaGenerator;    /**   * MyBatis MySQL自動生成帶分頁插件   *   * @author wangmengjun   *   */  public class MysqlPaginationPlugin extends PluginAdapter {      @Override    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass,        IntrospectedTable introspectedTable) {      addLimit(topLevelClass, introspectedTable, "limitStart");      addLimit(topLevelClass, introspectedTable, "limitSize");      return super.modelExampleClassGenerated(topLevelClass,          introspectedTable);    }      /**     * 為selectByExample添加limitStart和limitSize     */    @Override    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(        XmlElement element, IntrospectedTable introspectedTable) {      XmlElement isNotNullElement = new XmlElement("if");      isNotNullElement.addAttribute(new Attribute("test",          "limitStart != null and limitSize >= 0"));      isNotNullElement.addElement(new TextElement(          "limit #{limitStart} , #{limitSize}"));      element.addElement(isNotNullElement);      return super.sqlMapSelectByExampleWithoutBLOBsElementGenerated(element,          introspectedTable);    }      /**     * 為selectByExampleWithBLOBs添加limitStart和limitSize     */    @Override    public boolean sqlMapSelectByExampleWithBLOBsElementGenerated(        XmlElement element, IntrospectedTable introspectedTable) {      XmlElement isNotNullElement = new XmlElement("if");      isNotNullElement.addAttribute(new Attribute("test",          "limitStart != null and limitSize >= 0"));      isNotNullElement.addElement(new TextElement(          "limit #{limitStart} , #{limitSize}"));      element.addElement(isNotNullElement);      return super.sqlMapSelectByExampleWithBLOBsElementGenerated(element,          introspectedTable);    }      private void addLimit(TopLevelClass topLevelClass,        IntrospectedTable introspectedTable, String name) {        CommentGenerator commentGenerator = context.getCommentGenerator();        /**       * 創建類成員變數 如protected Integer limitStart;       */      Field field = new Field();      field.setVisibility(JavaVisibility.PROTECTED);      field.setType(PrimitiveTypeWrapper.getIntegerInstance());      field.setName(name);      commentGenerator.addFieldComment(field, introspectedTable);      topLevelClass.addField(field);      /**       * 首字母大寫       */      char c = name.charAt(0);      String camel = Character.toUpperCase(c) + name.substring(1);        /**       * 添加Setter方法       */      Method method = new Method();      method.setVisibility(JavaVisibility.PUBLIC);      method.setName("set" + camel);      method.addParameter(new Parameter(PrimitiveTypeWrapper          .getIntegerInstance(), name));        StringBuilder sb = new StringBuilder();      sb.append("this.");      sb.append(name);      sb.append(" = ");      sb.append(name);      sb.append(";");      /**       * 如 this.limitStart = limitStart;       */      method.addBodyLine(sb.toString());        commentGenerator.addGeneralMethodComment(method, introspectedTable);      topLevelClass.addMethod(method);        /**       * 添加Getter Method 直接調用AbstractJavaGenerator的getGetter方法       */      Method getterMethod = AbstractJavaGenerator.getGetter(field);      commentGenerator.addGeneralMethodComment(getterMethod,          introspectedTable);      topLevelClass.addMethod(getterMethod);    }      public boolean validate(List<String> warnings) {      return true;    }    }

修改自動產生程式碼配置文件generatorConfig.xml中的plugin。

    <!-- 配置內置的或者自定義的Plugin -->      <plugin type="my.mabatis.example.plugin.MysqlPaginationPlugin" />

自動產生程式碼,我們可以看到NewsExample.java以及NewsMapper.xml都具有limitStart和limitSize, 可以支援分頁。部分相關程式碼如下:

package my.mybatis.generator.auto.entity;    import java.util.ArrayList;  import java.util.Date;  import java.util.List;    public class NewsExample {        /**       * This field was generated by MyBatis Generator.       * This field corresponds to the database table m_news       *       * @mbggenerated Wed Nov 09 21:39:59 CST 2016       */      protected Integer limitStart;        /**       * This field was generated by MyBatis Generator.       * This field corresponds to the database table m_news       *       * @mbggenerated Wed Nov 09 21:39:59 CST 2016       */      protected Integer limitSize;          /**       * This method was generated by MyBatis Generator.       * This method corresponds to the database table m_news       *       * @mbggenerated Wed Nov 09 21:39:59 CST 2016       */      public void setLimitStart(Integer limitStart) {          this.limitStart = limitStart;      }        /**       * This method was generated by MyBatis Generator.       * This method corresponds to the database table m_news       *       * @mbggenerated Wed Nov 09 21:39:59 CST 2016       */      public Integer getLimitStart() {          return limitStart;      }        /**       * This method was generated by MyBatis Generator.       * This method corresponds to the database table m_news       *       * @mbggenerated Wed Nov 09 21:39:59 CST 2016       */      public void setLimitSize(Integer limitSize) {          this.limitSize = limitSize;      }        /**       * This method was generated by MyBatis Generator.       * This method corresponds to the database table m_news       *       * @mbggenerated Wed Nov 09 21:39:59 CST 2016       */      public Integer getLimitSize() {          return limitSize;      }      //省略其它  }
 <select id="selectByExampleWithBLOBs" parameterType="my.mybatis.generator.auto.entity.NewsExample" resultMap="ResultMapWithBLOBs">      <!--        WARNING - @mbggenerated        This element is automatically generated by MyBatis Generator, do not modify.        This element was generated on Wed Nov 09 21:39:59 CST 2016.      -->      select      <if test="distinct">        distinct      </if>      <include refid="Base_Column_List" />      ,      <include refid="Blob_Column_List" />      from m_news      <if test="_parameter != null">        <include refid="Example_Where_Clause" />      </if>      <if test="orderByClause != null">        order by ${orderByClause}      </if>      <if test="limitStart != null and limitSize &gt;= 0">        limit #{limitStart} , #{limitSize}      </if>    </select>    <select id="selectByExample" parameterType="my.mybatis.generator.auto.entity.NewsExample" resultMap="BaseResultMap">      <!--        WARNING - @mbggenerated        This element is automatically generated by MyBatis Generator, do not modify.        This element was generated on Wed Nov 09 21:39:59 CST 2016.      -->      select      <if test="distinct">        distinct      </if>      <include refid="Base_Column_List" />      from m_news      <if test="_parameter != null">        <include refid="Example_Where_Clause" />      </if>      <if test="orderByClause != null">        order by ${orderByClause}      </if>      <if test="limitStart != null and limitSize &gt;= 0">        limit #{limitStart} , #{limitSize}      </if>

至此,大功告成。

03

測試

創建一個用於獲取分頁列表的方法。

package my.mabatis.example.service;    import java.util.List;    import my.mabatis.example.util.MyBatisUtil;  import my.mybatis.generator.auto.dao.UserMapper;  import my.mybatis.generator.auto.entity.User;  import my.mybatis.generator.auto.entity.UserExample;  import my.mybatis.generator.auto.entity.UserExample.Criteria;    import org.apache.ibatis.session.SqlSession;    /**   *   * @author wangmengjun   *   */  public class UserService {      /**     * 查找分頁列表     */    public List<User> selectNewsByPage(int pageNo, int pageSize) {        SqlSession sqlSession = MyBatisUtil.getSqlSessionFactory()          .openSession();      try {        UserMapper userDao = sqlSession.getMapper(UserMapper.class);        /**         * 使用Example來操作         */        UserExample example = new UserExample();        /**         * 設置limitStart和limitSize         */        example.setLimitStart((pageNo - 1) * pageSize);        example.setLimitSize(pageSize);        return userDao.selectByExample(example);      } finally {        sqlSession.close();      }    }    }

寫一個測試類,獲取第一頁數據,一頁5條

package my.mabatis.example.runner;    import java.util.List;    import my.mabatis.example.service.UserService;  import my.mybatis.generator.auto.entity.User;    public class Test {      public static void main(String[] args) {      UserService userService = new UserService();      /**       * 獲取第一頁的數據, 一頁5條數據       */      List<User> users = userService.selectNewsByPage(1, 5);      System.out.println(users.size());    }  }

測試數據一共有三條,所以返回結果是正確的。

log4j:WARN No appenders could be found for logger (org.apache.ibatis.logging.LogFactory).  log4j:WARN Please initialize the log4j system properly.  log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

Note: 本篇文章的編寫與之前的幾篇文章有部分聯繫,如果有類內容不知道,請參考之前的兩篇博文; <<使用MyBatis Generator自動生成程式碼>> <<讓MyBatis Generator產生的Mapper更簡潔>>

也可以直接問我即可。