通过之前的基础篇、注释篇和 Lombok 篇,生成的实体类和 Mapper 基本上可以满足大部分常见的业务,但这其中并不包括分页查询。
不能分页的查询是没有灵魂的,小编能力有限,只实现了 mysql 的 limit 功能。
1 实现思路
通过观察生成的 UserExample.java、UserSqlProvider.java 和 UserMapper.java 三个类的源码,学到了 Order 功能的实现方式,所以实现 limit 也准备效仿这种形式。
UserExample.java(部分代码)
package org.example.user.domain;
import java.util.ArrayList;
import java.util.List;
public class UserExample {
protected String orderByClause;
protected List<Criteria> oredCriteria;
public UserExample() {
oredCriteria = new ArrayList<>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
}
UserSqlProvider.java(部分代码)
package org.example.user.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.jdbc.SQL;
import org.example.user.domain.User;
import org.example.user.domain.UserExample.Criteria;
import org.example.user.domain.UserExample.Criterion;
import org.example.user.domain.UserExample;
public class UserSqlProvider {
public String selectByExample(UserExample example) {
SQL sql = new SQL();
if (example != null && example.isDistinct()) {
sql.SELECT_DISTINCT("id");
} else {
sql.SELECT("id");
}
sql.SELECT("name");
sql.SELECT("password");
sql.SELECT("create_time");
sql.SELECT("update_time");
sql.FROM("user");
applyWhere(sql, example, false);
if (example != null && example.getOrderByClause() != null) {
sql.ORDER_BY(example.getOrderByClause());
}
return sql.toString();
}
}
UserMapper.java(部分代码)
package org.example.user.dao;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.DeleteProvider;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectKey;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.type.JdbcType;
import org.example.user.domain.User;
import org.example.user.domain.UserExample;
public interface UserMapper {
@SelectProvider(type=UserSqlProvider.class, method="selectByExample")
@Results({
@Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
@Result(column="password", property="password", jdbcType=JdbcType.VARCHAR),
@Result(column="create_time", property="createTime", jdbcType=JdbcType.BIGINT),
@Result(column="update_time", property="updateTime", jdbcType=JdbcType.BIGINT)
})
List<User> selectByExample(UserExample example);
}
通过以上代码可以分析得到:在调用 UserMapper.selectByExample() 方法时,Mybatis 框架会去调用 UserSqlProvider.selectByExample()。该方法在返回生成的 SQL 表达式之前会判断参数传入的 Example 对象中的 orderByClause 字段是否为空,如果不为空,将就其追加到 SQL 表达式后面。
并且可以通过调用 UserExample.clear() 方法来清除 orderByClause 字段。
所以,要实现 limit 功能,也可以采取上诉思路:
- 在 Example 中增加 offset 和 count 两个字段
- 在 Example clear() 方法中清除 offset 和 count 两个字段
- 在 SqlProvider.selectByExample() 判断 offset 和 count 两个字段不同时为 null,追加 limit 内容。
2 实现步骤
2.1 创建插件
LimitPlugin.java
package com.zwj.mybatis.plugin;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import java.util.List;
/**
* 在 Example 中生成 limit 相应的字段
*/
public class LimitPlugin extends PluginAdapter {
@Override
public boolean validate(List<String> warnings) {
return true;
}
/**
* 在 Example 类中增加 offset、count 字段及其 getter/setter,同时在 clear 方法中添加清除 offset、count 的值
*/
@Override
public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper();
FullyQualifiedJavaType booleanPrimitive = FullyQualifiedJavaType.getBooleanPrimitiveInstance();
// 添加 Integer offset 字段
Field offset = new Field("offset", integerWrapper);
offset.setVisibility(JavaVisibility.PROTECTED);
topLevelClass.addField(offset);
// 添加 Integer count 字段
Field count = new Field("count", integerWrapper);
count.setVisibility(JavaVisibility.PROTECTED);
topLevelClass.addField(count);
// 添加 void setLimit(Integer offset, Integer count) 方法
Method setLimit = new Method("setLimit");
setLimit.setVisibility(JavaVisibility.PUBLIC);
setLimit.addParameter(new Parameter(integerWrapper, "offset"));
setLimit.addParameter(new Parameter(integerWrapper, "count"));
setLimit.addBodyLine("this.offset = offset;");
setLimit.addBodyLine("this.count = count;");
topLevelClass.addMethod(setLimit);
// 添加 boolean getLimit() 方法,返回值为 offset、count 是否同时不为 null
Method getLimit = new Method("getLimit");
getLimit.setVisibility(JavaVisibility.PUBLIC);
getLimit.setReturnType(booleanPrimitive);
getLimit.addBodyLine("return (this.offset != null && this.count != null);");
topLevelClass.addMethod(getLimit);
// 添加 Integer getOffset() 方法,返回值为 offset 的值
Method getOffset = new Method("getOffset");
getOffset.setVisibility(JavaVisibility.PUBLIC);
getOffset.setReturnType(integerWrapper);
getOffset.addBodyLine("return offset;");
topLevelClass.addMethod(getOffset);
// 添加 Integer getCount() 方法,返回值为 count 的值
Method getCount = new Method("getCount");
getCount.setVisibility(JavaVisibility.PUBLIC);
getCount.setReturnType(integerWrapper);
getCount.addBodyLine("return count;");
topLevelClass.addMethod(getCount);
List<Method> methods = topLevelClass.getMethods();
// 在 clear() 方法中添加清除 offset、count 的值
for (Method method : methods) {
if ("clear".equals(method.getName())) {
method.addBodyLine("this.offset = null;");
method.addBodyLine("this.count = null;");
break;
}
}
return true;
}
private void providerLimit(List<String> bodyLines) {
bodyLines.remove(bodyLines.size() - 1);
bodyLines.add("String limitExpress = \"\";");
bodyLines.add("");
bodyLines.add("if (example != null && example.getLimit()) {");
bodyLines.add("limitExpress = \" LIMIT \" + example.getOffset() + \", \" + example.getCount();");
bodyLines.add("}");
bodyLines.add("");
bodyLines.add("return sql + limitExpress;");
}
/**
* 在 SqlProvider SelectByExample 方法中增加 limit 判断
*/
@Override
public boolean providerSelectByExampleWithoutBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
providerLimit(method.getBodyLines());
return true;
}
@Override
public boolean providerSelectByExampleWithBLOBsMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
providerLimit(method.getBodyLines());
return true;
}
}
2.2 配置插件
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="Mysql" targetRuntime="MyBatis3" defaultModelType="flat">
<!-- 表名前后加上''的分隔符 -->
<property name="beginningDelimiter" value="`" />
<property name="endingDelimiter" value="`" />
<!-- 整合分页插件 -->
<plugin type="com.zwj.mybatis.plugin.LimitPlugin" />
<!-- 整合 lombok 插件 -->
<plugin type="com.zwj.mybatis.plugin.LombokPlugin" />
<!-- 根据表生成注释 -->
<commentGenerator type="com.zwj.mybatis.generator.CustomCommentGenerator">
<!-- 定义生成实体类注解所需参数 -->
<property name="author" value="青城山上学编程" />
<property name="dateFormat" value="yyyy/MM/dd HH:mm" />
</commentGenerator>
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://192.168.1.188:3307/user-center"
userId="root" password="123456" />
<!-- 对于生成的pojo所在包 -->
<javaModelGenerator targetPackage="org.example.user.domain" targetProject="src\main\java" />
<!-- 对于生成的mapper所在目录 -->
<sqlMapGenerator targetPackage="org.example.user.dao" targetProject="src\main\resources" />
<!-- 配置dao对应的映射 -->
<javaClientGenerator targetPackage="org.example.user.dao" targetProject="src\main\java"
type="ANNOTATEDMAPPER" />
<table tableName="%">
<!--insert 方法返回主键-->
<generatedKey column="id" sqlStatement="MySql" identity="true" />
</table>
</context>
</generatorConfiguration>
3 生成效果及使用
UserExample.java(部分代码)
package org.example.user.domain;
import java.util.ArrayList;
import java.util.List;
public class UserExample {
protected String orderByClause;
protected List<Criteria> oredCriteria;
protected Integer offset;
protected Integer count;
public UserExample() {
oredCriteria = new ArrayList<>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
this.offset = null;
this.count = null;
}
public void setLimit(Integer offset, Integer count) {
this.offset = offset;
this.count = count;
}
public boolean getLimit() {
return (this.offset != null && this.count != null);
}
public Integer getOffset() {
return offset;
}
public Integer getCount() {
return count;
}
}
UserSqlProvider.java(部分代码)
package org.example.user.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.jdbc.SQL;
import org.example.user.domain.User;
import org.example.user.domain.UserExample.Criteria;
import org.example.user.domain.UserExample.Criterion;
import org.example.user.domain.UserExample;
public class UserSqlProvider {
public String selectByExample(UserExample example) {
SQL sql = new SQL();
if (example != null && example.isDistinct()) {
sql.SELECT_DISTINCT("id");
} else {
sql.SELECT("id");
}
sql.SELECT("name");
sql.SELECT("password");
sql.SELECT("create_time");
sql.SELECT("update_time");
sql.FROM("user");
applyWhere(sql, example, false);
if (example != null && example.getOrderByClause() != null) {
sql.ORDER_BY(example.getOrderByClause());
}
String limitExpress = "";
if (example != null && example.getLimit()) {
limitExpress = " LIMIT " + example.getOffset() + ", " + example.getCount();
}
return sql + limitExpress;
}
}
使用分页查询,直接调用 UserExample.setLimit() 方法即可。
UserService.java
package org.example.user.service;
import org.example.user.DTO.UserInfoDTO;
import org.example.user.DTO.UserRegisterDTO;
import org.example.user.dao.UserMapper;
import org.example.user.domain.User;
import org.example.user.domain.UserExample;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public List<UserInfoDTO> getAllUser(int pageNum, int pageSize) {
UserExample userExample = new UserExample();
userExample.setOrderByClause("id DESC");
// 设置分页查询
userExample.setLimit((pageNum - 1) * pageSize, pageSize);
userExample.createCriteria().andIdGreaterThan(0L);
// 条件查询记录
List<User> userList = userMapper.selectByExample(userExample);
List<UserInfoDTO> retVal = new ArrayList<>(userList.size());
for (User user : userList) {
UserInfoDTO userInfo = new UserInfoDTO();
BeanUtils.copyProperties(user, userInfo);
retVal.add(userInfo);
}
return retVal;
}
}