本文提供一种方法,让MyBatis Generator产生的代码支持分页, 适用于MySQL。
分析
如果要获取分页信息,使用MySQL语句,我们需要怎么做呢?
select * from t_user limit 0 , 2
在MySQL系统中,如果要完成一个分页,我们需要指定limit的值,也就是需要指定两个数,第一个指定从什么地方开始(示例中为0);另一个指定需要获取多少条数据(示例中为2)。
问题转化
如果要使得产生的自动产生的代码具备分页功能的话,那么,Mapper对应的XML中,select语句需要多增加两个属性值,比如:
- limitStart (指定从什么位置开始查找)
- limitSize (指定找到多少条数据)
何处添加
上述已经提到需要两个值limitStart和limitSize,那么,我们需要添加在哪里才能有效果呢?
以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来完成的。
ListselectByExampleWithBLOBs(NewsExample example); List 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没有。
从上述简单分析可以看出,limitStart和limitSize添加的地方有两个:
- 实体类对应的Example中需要添加。
- XML文件中,selectByExample和selectByExampleWithBLOBs配置需要添加limitStart和limitSize属性。
如
limit #{limitStart} , #{limitSize}
有了上述的分析之后,我们需要写什么就很清楚了 。
编码
Example类具备分页属性
Example中包含两个字段limitStart和limitSize,并具有Getter和Setter方法,如:
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 ListgetCompilationUnits(); 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的selectByExample和selectByExampleWithBLOBs方法添加limitStart和limitSize属性。
为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(Listwarnings) { return true; }}
修改自动产生代码配置文件generatorConfig.xml中的plugin。
自动产生代码,我们可以看到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; } //省略其它}
至此,大功告成。
测试验证
创建一个用于获取分页列表的方法。
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 ListselectNewsByPage(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条数据 */ Listusers = 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.3
如有疑问
Note:
本篇文章的编写与之前的几篇文章有部分联系,如果有类内容不知道,请参考之前的两篇博文;
也可以直接问我即可。