SpringBoot学习笔记八:分页插件、通用Mapper及MBG的集成

插件简介

PageHelper 与通用 Mapper 作者为同一人 abel533 ,使用这两个插件可以极大地简化 MyBatis 的开发,如果你想深入了解Mybatis以及插件开发可以购买作者的书籍。

MyBatis从入门到精通

分页插件 PageHelper

GitHub地址:https://github.com/pagehelper/Mybatis-PageHelper

如果你也在用 MyBatis,建议尝试该分页插件,这一定是最方便使用的分页插件

在没有分页插件之前,写一个分页需要两条 SQL 语句,一条查询一条统计,然后计算封装 PageBean,这样的代码冗余而又枯燥,更重要的一点是数据库迁移,众所周知不同的数据库分页写法是不同的,而Mybatis 不同于 Hibernate 的是它只提供动态 SQL 和结果集映射。值得庆幸的是,它虽然没有为分页提供良好的解决方案,但却提供了 Interceptor 以供开发者自己扩展,这也是这款分页插件的由来….

通用Mapper

GitHub地址:https://github.com/abel533/Mapper/
文档地址:https://github.com/abel533/Mapper/wiki
gitee地址:https://gitee.com/free/Mapper

通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。

极其方便的使用MyBatis单表的增删改查。

支持单表操作,不支持通用的多表联合查询。

添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MyBatis与Spring Boot整合starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

<!-- MyBatis 通用Mapper -->
<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>2.0.3</version>
</dependency>

<!-- MyBatis 分页插件pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.5</version>
</dependency>

<!-- mysql 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- devtools -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

相关配置

spring:
  application:
    name: spring-boot-mbg
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
    username: root
    password: mysql123
    driver-class-name: com.mysql.jdbc.Driver
mybatis:
  config-location: classpath:mybatis/mybatis-config.xml       # mybatis配置文件位置
  mapper-locations: classpath:mybatis/mappers/*.xml           # mapper映射文件位置
  type-aliases-package: com.example.springbootmybatis.entity  # 别名包

mapper:
  mappers:
    - com.example.springbootmbg.common.MyBaseMapper
  identity: MYSQL # 取回主键的方式 MYSQL: SELECT LAST_INSERT_ID()
  order: AFTER    # 还可设置为 mapper.before = false
  not-empty: true # insertSelective 和 updateByPrimaryKeySelective 中,是否判断字符串类型 !=''
  style: camelhumpAndLowercase # 实体和表转换时的默认规则:驼峰转下划线小写形式
  enableMethodAnnotation: true # 是否支持(getter 和 setter)在方法上使用注解,默认false
  enumAsSimpleType: true # 枚举类型当成基本类型对待

pagehelper:
  helper-dialect: mysql
  reasonable: false
  support-methods-arguments: true
  params: count=countSql

# mybatis sql日志
logging:
  level:
    com:
      example:
        springbootmbg:
          mapper: debug

属性解释

通用Mapper

通用Mapper 属性配置上面的注释已经很清楚了,这里不再做过多解释,唯一一点是 mappers 配置的是自定义的 MyBaseMapper 接口,具体下面介绍。
更多 通用Mapper 属性配置可参考官方文档 3.配置介绍部分

PageHelper

helperDialect:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置 helperDialect 属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:
oracle , mysql , mariadb , sqlite , hsqldb , postgresql , db2 , sqlserver , informix , h2 , sqlserver2012 , derby
特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012,否则会使用 SqlServer2005 的方式进行分页。

reasonable:分页合理化参数,默认值为 false ,直接根据参数进行查询 。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。

supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值 false ,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTestArgumentsObjTest

params:为了支持 startPage(Object params) 方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum , pageSize , count , pageSizeZero , reasonable ,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero

更多 PageHelper 属性配置可参考官方使用方法部分

MyBatis sql日志

对于 MyBatis sql 日志可以像上面那样在 application.yml 文件中配置,但在项目中往往使用 logback 日志框架,因此可以在 logback-spring.xml 配置文件中添加如下 logger

<logger name="com.example.springbootmbg.mapper" level="DEBUG" additivity="false"></logger>

MyBatis配置文件 mybatis-config.xml

src/main/resource/mybatis/mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--<typeAliases>
        <package name="com.example.springbootmbg.entity"/>
    </typeAliases>-->
</configuration>

Spring DevTools 配置

在使用 DevTools 时,通用Mapper 经常会出现 class x.x.A cannot be cast to x.x.A。

同一个类如果使用了不同的类加载器,就会产生这样的错误,所以解决方案就是让 通用Mapper 和实体类使用相同的类加载器即可。

DevTools 默认会对 IDE 中引入的所有项目使用 restart 类加载器,对于引入的 jar 包使用 base 类加载器,因此只要保证 通用Mapperjar 包使用 restart 类加载器 即可。

src/main/resources 中创建 META-INF 目录,在此目录下添加 spring-devtools.properties 配置,内容如下:

restart.include.mapper=/mapper-[\\w-\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar

使用这个配置后,就会使用 restart 类加载器 加载 include 进去的 jar 包。

以上 Spring DevTools 配置 内容引自官方Spring 集成示例——集成Spring Boot部分

通用Mapper MBG代码生成

Maven集成MBG插件

pom.xml

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.6</version>
    <configuration>
        <configurationFile>
            ${basedir}/src/main/resources/generator/generatorConfig.xml
        </configurationFile>
        <overwrite>true</overwrite>
        <verbose>true</verbose>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>4.0.0</version>
        </dependency>
    </dependencies>
</plugin>

MBG配置文件

详细配置参数可参考http://git.oschina.net/free/Mybatis_Utils/blob/master/MybatisGeneator/MybatisGeneator.md官方文档 4.1专用代码生成器部分在此不作过多解释

src/main/resources/generator/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>

    <properties resource="application.yml"/>

    <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">

        <property name="autoDelimitKeywords" value="true"/>
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>

        <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
            <property name="mappers" value="com.example.springbootmbg.common.MyBaseMapper"/>
        </plugin>

        <commentGenerator>
            <!-- suppressAllComments:阻止生成注释,默认为false -->
            <property name="suppressAllComments" value="false"/>
            <!-- suppressDate:阻止生成的注释包含时间戳,默认为false -->
            <property name="suppressDate" value="true"/>
        </commentGenerator>

        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/test_db"
                        userId="root"
                        password="mysql123">
        </jdbcConnection>

        <!-- 对应生成的pojo所在包 -->
        <javaModelGenerator targetPackage="com.example.springbootmbg.entity" targetProject="src/main/java"/>

        <!-- 对应生成的mapper所在目录 -->
        <sqlMapGenerator targetPackage="mybatis/mappers" targetProject="src/main/resources"/>

        <!-- 对应生成的mapper接口所在包 -->
        <javaClientGenerator targetPackage="com.example.springbootmbg.mapper" targetProject="src/main/java"
                             type="XMLMAPPER"/>

        <table tableName="tb_user" domainObjectName="User">
            <!--mysql 配置-->
            <!--<generatedKey column="id" sqlStatement="Mysql" />-->
            <!--oracle 配置-->
            <!--<generatedKey column="id" sqlStatement="select SEQ_{1}.nextval from dual" identity="false" type="pre"/>-->
        </table>

    </context>
</generatorConfiguration>

<table> 标签需要根据数据库表自行增添

com.example.springbootmbg.common.MyBaseMapper

package com.example.springbootmbg.common;

import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;

public interface MyBaseMapper<T> extends Mapper<T>, MySqlMapper<T> {
    //TODO
    //FIXME 特别注意,该接口不能被扫描到,否则会出错
}

很简单的一个接口,继承了 通用Mapper 提供的 tk.mybatis.mapper.common.Mappertk.mybatis.mapper.common.MySqlMapper 接口,MBG 生成的 Mapper 接口都会继承这个接口,从而间接继承前面提到的两个接口,从而获得 通用Mapper 的魔力(其实就是获得了一系列 通用Mapper 提供的单表增、删、改、查方法而已),具体方法可以查看那两个接口。

扫描Mapper接口所在的包

需要在启动类上添加 @MapperScan 注解,但请注意这个注解不是 MyBatis 提供的 org.mybatis.spring.annotation.MapperScan 而是 通用Mapper 提供的 tk.mybatis.spring.annotation.MapperScan

package com.example.springbootmbg;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan(basePackages = {"com.example.springbootmbg.mapper"})
public class SpringBootMbgApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootMbgApplication.class, args);
    }

}

注意:这里有个坑就是这个自定义 MyBaseMapper 接口不能被扫描到,否则会报错,因此上面将该接口放在了别的包下。

不嫌麻烦的话可以去 MBG 生成的每个 Mapper 接口上添加 @Mapper 注解

使用MBG生成实体类、Mapper接口及xml映射文件

在项目 Maven 插件中找到 mybatis-generator 插件,点击 mybatis-generator:generate 一下就可以了,如果数据库连接信息配置正确的话,不出意外就会顺利在配置的目录下生成对应的实体类、Mapper接口及xml映射文件啦。

MBG使用

为示例这里创建一张用户表

CREATE TABLE `tb_user` (
  `id` varchar(64) NOT NULL COMMENT '用户id',
  `username` varchar(64) NOT NULL COMMENT '用户名',
  `password` varchar(64) NOT NULL COMMENT '用户密码',
  `face_icon` varchar(255) DEFAULT NULL COMMENT '用户头像',
  `nickname` varchar(64) NOT NULL COMMENT '用户昵称',
  `fans_counts` int(11) DEFAULT '0' COMMENT '用户粉丝数',
  `follow_counts` int(11) DEFAULT '0' COMMENT '关注其他用户数',
  `receive_like_counts` int(11) DEFAULT '0' COMMENT '收到的喜欢数目',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';

生成的代码大致像下面这样

com.example.springbootmbg.entity.user

package com.example.springbootmbg.entity;

import java.util.Date;
import javax.persistence.*;

@Table(name = "tb_user")
public class User {
    /**
     * 用户id
     */
    @Id
    private String id;

    /**
     * 用户名
     */
    private String username;

    /**
     * 用户密码
     */
    private String password;

    /**
     * 用户头像
     */
    @Column(name = "face_icon")
    private String faceIcon;

    /**
     * 用户昵称
     */
    private String nickname;

    /**
     * 用户粉丝数
     */
    @Column(name = "fans_counts")
    private Integer fansCounts;

    /**
     * 关注其他用户数
     */
    @Column(name = "follow_counts")
    private Integer followCounts;

    /**
     * 收到的喜欢数目
     */
    @Column(name = "receive_like_counts")
    private Integer receiveLikeCounts;

    /**
     * 创建时间
     */
    @Column(name = "create_time")
    private Date createTime;

    /**
     * 更新时间
     */
    @Column(name = "update_time")
    private Date updateTime;

    // setters and getters
}

通用Mapper 会用到 Jpa 的几个简单注解,这里不做解释

com.example.springbootmbg.mapper.UserMapper

package com.example.springbootmbg.mapper;

import com.example.springbootmbg.common.MyBaseMapper;
import com.example.springbootmbg.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserMapper extends MyBaseMapper<User> {
}

生成的代码不带 @Repository 注解

src/main/resource/mybatis/mappers/UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.springbootmbg.mapper.UserMapper">
  <resultMap id="BaseResultMap" type="com.example.springbootmbg.entity.User">
    <!--
      WARNING - @mbg.generated
    -->
    <id column="id" jdbcType="VARCHAR" property="id" />
    <result column="username" jdbcType="VARCHAR" property="username" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="face_icon" jdbcType="VARCHAR" property="faceIcon" />
    <result column="nickname" jdbcType="VARCHAR" property="nickname" />
    <result column="fans_counts" jdbcType="INTEGER" property="fansCounts" />
    <result column="follow_counts" jdbcType="INTEGER" property="followCounts" />
    <result column="receive_like_counts" jdbcType="INTEGER" property="receiveLikeCounts" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
    <result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
  </resultMap>
</mapper>

测试

com.example.springbootmbg.mapper.UserMapperTest

package com.example.springbootmbg.mapper;

import com.example.springbootmbg.entity.User;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Date;
import java.util.List;
import java.util.UUID;

@SpringBootTest
@RunWith(SpringRunner.class)
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    private static final Logger logger = LoggerFactory.getLogger(UserMapperTest.class);

    @Test
    public void testInsert() {
        User user = new User();
        user.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        user.setUsername("aaa");
        user.setPassword("123456");
        user.setNickname("aaa");
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        userMapper.insertSelective(user);

        User user2 = new User();
        user2.setId(UUID.randomUUID().toString().replaceAll("-", ""));
        user2.setUsername("bbb");
        user2.setPassword("123456");
        user2.setNickname("bbb");
        user2.setCreateTime(new Date());
        user2.setUpdateTime(new Date());
        userMapper.insertSelective(user2);
    }

    @Test
    public void testPage() {
        PageHelper.startPage(1, 10);
        List<User> userList = userMapper.selectAll();
        PageInfo<User> pageInfo = new PageInfo<>(userList);
        logger.info("pageInfo: {}", pageInfo);
    }

}
测试结果
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容