Mybatis Generator 之分页查询

通过之前的基础篇注释篇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 功能,也可以采取上诉思路:

  1. 在 Example 中增加 offset 和 count 两个字段
  2. 在 Example clear() 方法中清除 offset 和 count 两个字段
  3. 在 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;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值