草庐IT

详解Mybatis-Plus中分页插件PaginationInterceptor, MybatisPlusInterceptor在SpringBoot中的使用

super先生 2023-04-10 原文

文章目录

1. 描述

1.1 MybatisPlusInterceptor

我们在开发的过程中,经常会遇到分页操作,其分为逻辑分页和物理分页,具体可参考我的博文:逻辑分页和物理分页

如果你用的是Mybatis-Plus框架,可用MybatisPlusInterceptor按如下配置分页代码:

/**
 * @author 念兮为美
 * @datetime 2022/11/28 14:10
 * @desc mybatis plus 配置类
 */
@Configuration
public class MybatisPlusConfig {

  @Bean
  public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 设置为使用 MYSQL 方言
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
  }
}

如果你用的是低版本spring boot,可用PaginationInterceptor按如下配置:

/**
 * @author 念兮为美
 * @datetime 2022/11/27 15:22
 * @desc mybatis plus 配置类
 */
@Configuration
public class MybatisPlusConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
}

MybatisPlusInterceptor插件是核心插件,目前代理了Executor#query、Executor#update、StatementHandler#prepare方法,其属性:

private List<InnerInterceptor> interceptors = new ArrayList<>();

1.2 InnerInterceptor

注意List<InnerInterceptor>泛型中的InnerInterceptormybatis-plus提供的插件都将基于此接口来实现功能,目前已有的功能如下:

  1. 自动分页: PaginationInnerInterceptor

  2. 多租户:TenantLineInnerInterceptor

  3. 动态表名:DynamicTableNameInnerInterceptor

  4. 乐观锁:OptimisticLockerInnerInterceptor

  5. sql性能规范: IllegalSQLInnerInterceptor

  6. 防止全表更新与删除: BlockAttackInnerInterceptor

【注意】使用多个功能需要注意顺序关系,建议使用如下顺序:

  1. 多租户

  2. 动态表名

  3. 分页,乐观锁

  4. sql性能规范,防止全表更新与删除

总结:对sql进行单次改造的优先放入,不对sql进行改造的最后放入。

2. 实现

2.1 不带条件的分页查询

  1. 编写查询展示类
/**
 * @author 念兮为美
 * @datetime 2023/2/2 09:58
 * @desc 用户查询结果
 */
@NoArgsConstructor
@Data
@ApiModel(description = "用户返回结果")
public class UserPageVo {

  @ApiModelProperty(name = "username", value = "用户名")
  private String username;

  @ApiModelProperty(name = "nickname", value = "昵称")
  private String nickname;

  @ApiModelProperty(name = "userType", value = "用户类型")
  private String userType;
}
  1. 编写mapper
@Mapper
public interface UserMapper extends BaseMapper<User> {

   @Select("select " +
            "username,nickname,user_type as userType " +
          "from " +
            "user " +
          "order by " +
            "create_time desc"
  )
  List<UserPageVo> findPageUsers(Page<UserPageVo> page);
}
  1. 编写service
@Service
public class UserService extends ServiceImpl<UserMapper, User> {

  @Resource private UserMapper userMapper;
  
  public Page<UserPageVo> findPageUsers() {
    Long currentPage = 4L;
    Long pageSize = 3L;
    Page<UserPageVo> page = new Page<>(currentPage, pageSize);
    List<UserPageVo> pageUsers = userMapper.findPageUsers(page);
    page.setRecords(pageUsers);
    return page;
  }
}
  1. 编写controller
@Api(tags = "用户模块")
@RestController
@RequestMapping("/user")
@Slf4j
@Validated
public class UserController {

  @Autowired private UserService userService;

  @ApiOperationSupport(author = "念兮为美")
  @ApiOperation(value = "用户查询接口")
  @GetMapping("/findPageUsers")
  public Page<UserPageVo> findPageUsers() {
    return userService.findPageUsers();
  }
}
  1. 测试运行结果

JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@2ce7d43] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM user
==> Parameters: 
<==    Columns: total
<==        Row: 24
<==      Total: 1
==>  Preparing: select username,nickname,user_type as userType from user order by create_time desc LIMIT ?,?
==> Parameters: 9(Long), 3(Long)
<==    Columns: username, nickname, userType
<==        Row: cs, null, admin
<==        Row: lin, null, admin
<==        Row: test3, null, TEST4
<==      Total: 3

2.2 带条件的分页查询

public class UserService extends ServiceImpl<UserMapper, User> {

  @Resource private UserMapper userMapper;

  public Page<User> findPageUsers() {
    Long currentPage = 1L;
    Long pageSize = 3L;
    Page<User> page = new Page<>(currentPage, pageSize);
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("username", "test5");
    Page<User> userPage = userMapper.selectPage(page, queryWrapper);
    return userPage;
  }

运行结果:

JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@475454ae] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) AS total FROM user WHERE deleted = false AND (username = ?)
==> Parameters: test2(String)
<==    Columns: total
<==        Row: 1
<==      Total: 1
==>  Preparing: SELECT username,nickname,user_type FROM user WHERE deleted=false AND (username = ?) LIMIT ?
==> Parameters: test2(String), 3(Long)
<==    Columns: username, nickname, user_type
<==        Row:  test2, null,TEST6
<==      Total: 1

2.3 简述Page类

简单分页模型, 有如下几个主要属性

/**
 * 查询数据列表
 */
protected List<T> records = Collections.emptyList();

/**
 * 总数
 */
protected long total = 0;

/**
 * 每页显示条数,默认 10
 */
protected long size = 10;

/**
 * 当前页
 */
protected long current = 1;

3. 注意事项

3.1 UncategorizedSQLException异常

在编写mapper.xml中的SQL语句时,或者使用@select注解编写SQL语句时,语句末尾不能使用 ; 结尾,原因是在做分页的时候会在编写的SQL语句后面拼接上limit语句, 导致出现SQL语法错误(UncategorizedSQLException)。

可以参考我的这篇博文:全网最详细的org.springframework.jdbc.UncategorizedSQLException的多种解决方法

3.2 不做记录总数的统计

new Page(queryParam.getCurrent(), queryParam.getSize())的两个参数表示:当前页和每页数量

如果业务需求只需要查询分页做上下页切换而不需要记录总数,可以设置第三个参数false就可以不查询count(),以提高性能,即new Page(queryParam.getCurrent(), queryParam.getSize(), false) ,如下代码所示:

/**
 * @author 念兮为美
 * @datetime 2022/11/28 14:20
 * @desc 用户服务实现类
 */
@Service
public class UserService extends ServiceImpl<UserMapper, User> {

  @Resource private UserMapper userMapper;

  public Page<UserPageVo> findPageUsers() {
    Long currentPage = 4L;
    Long pageSize = 3L;
    Page<UserPageVo> page = new Page<>(currentPage, pageSize, false);
    List<UserPageVo> pageUsers = userMapper.findPageUsers(page);
    page.setRecords(pageUsers);
    return page;
  }

从输出结果看,既没有count操作,如下所示:

JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@466c39de] will not be managed by Spring
==>  Preparing: select username,nickname,user_type as userType from user order by create_time desc LIMIT ?,?
==> Parameters: 9(Long), 3(Long)
<==    Columns: username, nickname, userType
<==        Row: cs, null, admin
<==        Row: lin, null, admin
<==        Row: test3, null, TEST4
<==      Total: 3

有关详解Mybatis-Plus中分页插件PaginationInterceptor, MybatisPlusInterceptor在SpringBoot中的使用的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  7. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  8. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  9. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  10. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

随机推荐