SpringBoot学习笔记八:分页插件、通用Mapper及MB
插件简介
PageHelper
与通用 Mapper
作者为同一人 abel533 ,使用这两个插件可以极大地简化 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
包下的 ArgumentsMapTest
和 ArgumentsObjTest
。
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 类加载器
,因此只要保证 通用Mapper
的 jar
包使用 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.Mapper
及tk.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映射文件啦。
为示例这里创建一张用户表
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);
}
}
测试结果