本文提供一种方法,让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来完成的。
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>
从上述简单分析可以看出,limitStart和limitSize添加的地方有两个:
- 实体类对应的Example中需要添加。
- XML文件中,selectByExample和selectByExampleWithBLOBs配置需要添加limitStart和limitSize属性。
如
<if test="limitStart != null and limitSize>=0">
limit #{limitStart} , #{limitSize}
</if>
有了上述的分析之后,我们需要写什么就很清楚了 。
编码
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 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的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(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 >= 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 >= 0">
limit #{limitStart} , #{limitSize}
</if>
</select>
至此,大功告成。
测试验证
创建一个用于获取分页列表的方法。
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.
3
如有疑问
Note:
本篇文章的编写与之前的几篇文章有部分联系,如果有类内容不知道,请参考之前的两篇博文;
也可以直接问我即可。
来源:oschina
链接:https://my.oschina.net/u/2911530/blog/785306