mybatis-plus3.4.0版本之前
pom.xml文件
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<!--分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
3.4.0版本之后
pom.xml文件
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
我是用的是3.4.3.1版本的依赖,倘若你想使用其他版本的依赖,你也可以自行去Maven仓库查找。
仓库地址:https://mvnrepository.com/artifact/com.baomidou/mybatis-plus
3.4.0之前的配置文件
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus配置
* @author zyy
* @date 2021/1/18 11:51
*/
@Configuration
public class MyBatisConfig {
/**
* mybatis-plus分页插件<br>
* 文档:http://mp.baomidou.com<br>
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
// paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
3.4.0之后的配置文件
分页插件
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.classfee.chen.Mapper")
public class MybatisPlusConfig {
/**
*
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
我们要将mybatisPlusInterceptor 这个方法注册到Bean里面,不然我们的插件就会失效。
使用@MapperScan这个注解,让系统扫描我们的Mapper类。注意一定要指定正确的Mapper地址。
在类方法上上面使用注解:@Configuration表面这是一个config类。
到这里我们的分页插件也就导入完成了,然后我们就需要去应用了。
语法总结
方法名 | 说明 | 用法实例 | 等价SQL |
---|---|---|---|
allEq(Map<R, V> params) | 全部等于 | map.put(“id”,“3”);map.put(“user_name”,“IT可乐”);allEq(map) | user_name = “IT可乐” AND id = 3 |
eq(R column, Object val) | 等于 = | eq(“id”,“3”) | id = 3 |
ne(R column, Object val) | 不等于 <> | ne(“id”, “3”) | id <> 3 |
gt(R column, Object val) | 大于 > | gt(“user_age”,“18”)user_age > 18 | user_age > 18 |
ge(R column, Object val) | 大于等于 >= | ge(“user_age”,“18”) | user_age >= 18 |
lt(R column, Object val) | 小于 < | lt(“user_age”,“18”) | user_age < 18 |
le(R column, Object val) | 小于等于 <= | le(“user_age”,“18”) | user_age <= 18 |
between(R column, Object val1, Object val2) | BETWEEN 值1 AND 值2 | between(“user_age”,“18”,“25”) | user_age BETWEEN 18 AND 25 |
notBetween(R column, Object val1, Object val2) | NOT BETWEEN 值1 AND 值2 | notBetween(“user_age”,“18”,“25”) | user_age NOT BETWEEN 18 AND 25 |
like(R column, Object val) | LIKE ‘%值%’ | like(“user_name”,“可乐”) | like ‘%可乐%’ |
notLike(R column, Object val) | NOT LIKE ‘%值%’ | notLike(“user_name”,“可乐”) | not like ‘%可乐%’ |
likeLeft(R column, Object val) | LIKE ‘%值’ | likeLeft(“user_name”,“可乐”) | like ‘%可乐’ |
likeRight(R column, Object val) | LIKE ‘值%’ | likeRight(“user_name”,“可乐”) | like ‘可乐%’ |
isNull(R column) | 字段 IS NULL | isNull(“user_name”) | user_name IS NULL |
isNotNull(R column) | 字段 IS NOT NULL | isNotNull(“user_name”) | user_name IS NOT NULL |
in(R column, Collection<?> value) | 字段 IN (value.get(0), value.get(1), …) | in(“user_age”,{1,2,3}) | user_age IN (?,?,?) |
notIn(R column, Collection<?> value) | 字段 NOT IN (value.get(0), value.get(1), …) | notIn(“user_age”,{1,2,3}) | user_age NOT IN (?,?,?) |
inSql(R column, String inValue) | 字段 IN ( sql语句 ) | inSql(“id”,“select id from user”) | id IN (select id from user) |
notInSql(R column, String inValue) | 字段 NOT IN ( sql语句 ) | notInSql(“id”,“select id from user where id > 2”) | id NOT IN (select id from user where id > 2 |
groupBy(R… columns) | 分组:GROUP BY 字段, … | groupBy(“id”,“user_age”) | GROUP BY id,user_age |
orderByAsc(R… columns) | 排序【升序】:ORDER BY 字段, … ASC | orderByAsc(“id”,“user_age”) | ORDER BY id ASC,user_age ASC |
orderByDesc(R… columns) | 排序【降序】:ORDER BY 字段, … DESC | orderByDesc(“id”,“user_age”) | ORDER BY id DESC,user_age DESC |
orderBy(boolean condition, boolean isAsc, R… columns) | ORDER BY 字段, … | orderBy(true,true,“id”,“user_age”) | ORDER BY id ASC,user_age ASC |
having(String sqlHaving, Object… params) | HAVING ( sql语句 ) | having(“sum(user_age)>{0}”,“25”) | HAVING sum(user_age)>25 |
or() | 拼接 OR | eq(“id”,1).or().eq(“user_age”,25) | id = 1 OR user_age = 25 |
and(Consumerconsumer) | AND 嵌套 | and(i->i.eq(“id”,1).ne(“user_age”,18)) | id = 1 AND user_age <> 25 |
nested(Consumerconsumer) | 正常嵌套 不带 AND 或者 OR | nested(i->i.eq(“id”,1).ne(“user_age”,18)) | id = 1 AND user_age <> 25 |
apply(String applySql, Object… params) | 拼接 sql(不会有SQL注入风险) | apply(“user_age>{0}”,“25 or 1=1”) | user_age >‘25 or 1=1’ |
last(String lastSql) | 拼接到 sql 的最后,多次调用以最后一次为准(有sql注入的风险) | last(“limit 1”) | limit 1 |
exists(String existsSql) | 拼接 EXISTS ( sql语句 ) | exists(“select id from user where user_age = 1”) | EXISTS (select id from user where user_age = 1) |
通用文件
yml配置文件
# PageHelper 分页插件配置
pagehelper:
helper-dialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
page-size-zero: true
mybatis:
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true # 也可以在 yml 配置文件中设置属性
# 开启日志功能
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
type-aliases-package: com.lss.domain
helper-dialect:指定数据库,不指定的话会默认自动检测数据库类型
reasonable:是否启动分页合理化。如果启用,当 pagenum < 1 时,会自动查询第一页的数据,当 pagenum > pges 时,自动查询最后一页数据;不启用的,以上两种情况都会返回空数据,如果启用则 pageHelper可以自动拦截请求参数中的 pageNum,pageSize参数,否则需要使用 PageHelper.startPage(pageNum,pageSize) 方法调用。
support-methods-arguments:默认为 false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。
params:用于从对象中根据属性名取值,可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值,默认值为 pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
条件构造器
QueryWrapper查询
QueryWrapper<实体类> queryWrapper = new QueryWrapper<>();
List<Long> ids= new ArrayList<>();
queryWrapper.in("id",ids); // 可入参数组查询
queryWrapper.and(wrapper->wrapper.eq("数字库字段",入参数据)); //可进行嵌套子查询
时间段查询
// 查询开始时间
String startTime = "2022-04-19"; //yyyy-MM-dd HH:mm:ss
queryWrapper.apply("UNIX_TIMESTAMP(start_time)>=UNIX_TIMESTAMP('" + startTime + "')");
//queryWrapper.ge("start_time",startTime);
// 结束时间
String endTime = "2022-04-20"; //yyyy-MM-dd HH:mm:ss
queryWrapper.apply("UNIX_TIMESTAMP(end_time)<=UNIX_TIMESTAMP('" + endTime + "')");
//queryWrapper.le("end_time",endTime);
// 备注:存入数据库的时间格式,和查询时间格式必须统一
condition(动态组装查询条件)
查询名字中包含n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选的
/**
* 使用if语句动态判断
*/
@Test
public void test1() {
//定义查询条件,有可能为null(用户未输入)
String name = null;
Integer ageBegin = 10;
Integer ageEnd = 20;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if(StringUtils.isNotBlank(name)){
queryWrapper.like("name","n");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
使用带condition参数的重载方法构建查询条件,简化代码的编写
@Test
public void test1Condition() {
//定义查询条件,有可能为null(用户未输入)
String name = null;
Integer ageBegin = 10;
Integer ageEnd = 20;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.like(StringUtils.isNotBlank(name), "name", "n")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
LambdaQueryWrapper
@Test
public void test2() {
//定义查询条件,有可能为null(用户未输入)
String name = null;
Integer ageBegin = 10;
Integer ageEnd = 20;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper
//避免使用字符串表示字段,防止运行时错误
//User 是用户实体类
.like(StringUtils.isNotBlank(name), User::getName, "n")
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
第一种实现的方法
controller层
//分页
@PostMapping("page")
public R page(@RequestBody(required = false) UserVo userVo){
//当前页
//int page = query.getCurrent() == null ? 1 : query.getCurrent();
//每页的页数
//int pageSize = query.getSize() == null ? 10 : query.getSize();
//创建一个分页对象 一般post 都是使用一个类文件接受相对应的参数
Page<User> objectPage = new Page<>(userVo.getPage(),userVo.getSize());
//构造查询条件
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//条件查询
//名称
String name = userVo.getUserName();
//开始名称
String startTime = userVo.getStartTime();
//结束名称
String endTime = userVo.getEndTime();
//部门
String deptName =userVo.getDeptName();
//判断是否为空不为空则拼接
if (!StringUtils.isEmpty(deptName )){
//根据部门名查询部门名称
Dept one = deptService.findOneByName(deptName);
//实体类不为空则把部门id传进去为空传null
if (one!=null){
//前面数据库字段后面传递的参数
objectQueryWrapper.eq("dept_id", one.getId());
}else {
objectQueryWrapper.eq("dept_id", null);
}
}
if (!StringUtils.isEmpty(name)){
objectQueryWrapper.eq("real_name", name);
}
if(!StringUtils.isEmpty(startTime)){
objectQueryWrapper.apply("UNIX_TIMESTAMP(time)>=UNIX_TIMESTAMP('" + startTime + "')");
}
if(!StringUtils.isEmpty(endTime)){
objectQueryWrapper.apply("UNIX_TIMESTAMP(time)<=UNIX_TIMESTAMP('" + endTime + "')");
}
//调用方法 使用分页
Page<TGridPunchRecordEntity> page = UserService.page(objectPage, objectQueryWrapper);
page.getRecords().forEach(tGridPunchRecordEntity -> {
tGridPunchRecordEntity.setName(tsUserService.findById(tGridPunchRecordEntity.getUserId()).getRealName());
}
);
long total = objectPage.getTotal(); //总记录数
// List<TGridPunchRecordEntity> records = objectPage.getRecords(); //集合
List<TGridPunchRecordEntity> records = page.getRecords();
return R.okPage(records,total);
}
service需要继承IService<实体类>
public interface UserService extends IService<UserEntity> {
}
第二种实现的方法
/**
* 使用PageHelper进行分页操作 并对parms中的Key进行模糊查询
*/
@GetMapping("/findPage")
public R findPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize,
@RequestParam Map<String, Object> params) {
PageInfo pageInfo = deptService.pageHelper(pageNum,pageSize,params);
return R.ok(pageInfo);
}
/**
* 使用Mybatis-plus自带分页操作 并对parms中的Key进行模糊查询
*/
@GetMapping("/findPageByMybatisPlus")
public R findPageByMybatisPlus(@RequestParam Integer pageNum, @RequestParam Integer pageSize,
@RequestParam Map<String, Object> params) {
IPage page = deptService.pageByMybatisPlus(pageNum,pageSize,params);
return R.ok(page);
}
Service层
PageInfo pageHelper(Integer pageNum, Integer pageSize, Map<String, Object> params);
IPage pageByMybatisPlus(Integer pageNum, Integer pageSize, Map<String, Object> params);
serviceImpl
@Override
public PageInfo pageHelper(Integer pageNum, Integer pageSize, Map<String, Object> params) {
//首先创建一个queryWrapper
QueryWrapper<DeptEntity> queryWrapper = new QueryWrapper<>();
//开启分页功能
PageHelper.startPage(pageNum,pageSize);
// 如果有传来的params中有Key这个数据 我们让其对Name进行模糊查询
String key = (String) params.get("key");
if (StringUtils.hasText(key)){
queryWrapper.like("name",key);
}
//查所有数据返回
PageInfo pageInfo = new PageInfo(deptDao.selectList(queryWrapper));
return pageInfo;
}
@Override
public IPage pageByMybatisPlus(Integer pageNum, Integer pageSize, Map<String, Object> params) {
QueryWrapper<DeptEntity> queryWrapper = new QueryWrapper<>();
String key = (String) params.get("key");
//如果名字等于Key 或者 email like key再查询出结果
if (StringUtils.hasText(key)){
queryWrapper.eq("name",key).or().like("email",key);
}
IPage<DeptEntity> page = this.page(new Page<>(pageNum, pageSize), queryWrapper);
return page;
}
结果测试
http://localhost:8082/page/1?pageNum=1&pageSize=5&key=孙
第三种实现方法
Controller层
@GetMapping("query/{current}/{limit}")
public List<DeptEntity> query(@PathVariable Long current, @PathVariable Long limit,
DeptVo deptVo){
//current为当前页,limit为每页显示个数,voQuery为封装的查询条件
List<DeptEntity> pagequery = deptService.Pagequery(current,limit,deptVo);
return pagequery;
}
Service
List<DeptEntity> Pagequery(Long current, Long limit, DeptVo deptVo);
ServiceImpl
@Override
public List<DeptEntity> Pagequery(Long current, Long limit, DeptVo deptVo) {
//初始化page
Page<DeptEntity> page = new Page<>(current,limit);
//设置条件
QueryWrapper<DeptEntity> wrapper =new QueryWrapper<>();
//eq是等于,ge是大于等于,gt是大于,le是小于等于,lt是小于,like是模糊查询
if(!StringUtils.isEmpty(deptVo.getName())){
wrapper.like("name",deptVo.getName());
}
//执行查询
deptService.page(page,wrapper);
long total = page.getTotal();//总数
List<DeptEntity> rs = page.getRecords();//结果
return rs;
}
第四种方法
Controller层
@GetMapping("/list")
@ApiOperation(value = "按条件分页查询")
public R list(@ApiParam(value = "当前页码", required = true) Long current,
@ApiParam(value = "每页记录数", required = true) Long size,
@ApiParam("查询对象条件") XXXVO xxxVO) {
//分页参数
Page<KnowledgeData> page = new Page<>(current, size);
//调用service层查询
IPage<实体类> pageMode = knowledgeDataService.selectPage(page, xxxVO);
//分页查询 获取返回数据
List<实体类> dataList = pageMode.getRecords();
//获取查询数据总记录数
long total = pageMode.getTotal();
HashMap<Object, Object> map = new HashMap<>();
map.put("total", total);
map.put("data", dataList);
return R.data(map);
}
service层
public IPage<实体类> selectPage(Page<实体类> page, XXXVO xxxVO) {
QueryWrapper<实体类> queryWrapper = new QueryWrapper<>();
//中间判断 加查询条件
Page<实体类> selectPage = baseMapper.selectPage(page, queryWrapper);
return selectPage;
}
自定义的 mapper#method 使用分页
UserMapper.java中定义接口方法
/**
* 查询 : 根据年龄查询用户列表,分页显示
*
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位
* @param age 年龄
* @return 分页对象
*/
IPage<User> selectPageByPage(Page<?> page, Integer age);
自定义UserMapper.java.xml
<select id="selectPageByPage" resultType="com.lyh.mybatisplus.entity.User">
SELECT id,name FROM user WHERE age > #{age}
</select>
乐观锁
在实体类的字段上加上@Version注解
@Version
private Integer version;
说明
**例子:**假设两个同时执行数据更新操作,会造成只有一个执行成功,另一个会被覆盖。
说明:
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1
newVersion 会回写到 entity 中
仅支持 updateById(id) 与 update(entity, wrapper) 方法
在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
常用注解
@TableName
@TableName(value = "t_user") //对应数据库中表的名字
public class User {
@TableId
@TableId(value = "uid") //映射数据表中的列名
private String id;
@TableField(可配置自动填充时间)
@TableField(value = "name") //映射数据表中的列名
private String name;
//自动填充表数据:创建时间、更新时间
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
// 注意:当对象的id被明确赋值时,不会使用雪花算法
@TableId(type = IdType.ASSIGN_ID) //雪花算法策略生成数据id
private Long id;
@TableId(type = IdType.AUTO) //使用数据库的自增策略
private Long id;
自动填充时间的处理类MyMetaObjectHandler.java
实现元对象处理器接口 -> 创建handler包,创建MyMetaObjectHandler类
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
@TableLogic逻辑删除
物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
使用场景:可以进行数据恢复
数据库中创建逻辑删除状态列
实体类中添加逻辑删除属性
@TableLogic
@TableField(value = "is_deleted")
private Integer deleted;
mybatis-plus时间范围查询的3种方式
实体类字段设置
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("开始时间")
@TableField(exist = false)
private Date startTime;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("截止时间")
@TableField(exist = false)
private Date endTime;
或者
@ApiModelProperty("开始时间")
@TableField(exist = false)
private Date startTime;
@ApiModelProperty("截止时间")
@TableField(exist = false)
private Date endTime;
数据库的create_time字段类型为datetime
第一种:
使用框架提供的查询方法
QueryWrapper<Goods> queryWrapper = Condition.getQueryWrapper(goods)
.ge(Func.isNotEmpty(goods.getStartTime()),"create_time",goods.getStartTime())
.le(Func.isNotEmpty(goods.getEndTime()),"create_time",goods.getEndTime());
IPage<Goods> pages = goodsService.page(Condition.getPage(query), queryWrapper);
第二种:
适用于自定义的查询方法
<if test="order.startTime != null">
and o.create_time >= #{order.startTime}
</if>
<if test="order.endTime != null">
and o.create_time <= #{order.endTime}
</if>
第三种:
lambda方法构造查询方法
QueryWrapper<Goods> queryWrapper = new QueryWrapper<>();
LambdaQueryWrapper<Goods> lambdaQueryWrapper = queryWrapper.lambda().ge(Func.isNotEmpty(goods.getStartTime()),Goods::getCreateTime, goods.getStartTime());
ambdaQueryWrapper = lambdaQueryWrapper.le(Func.isNotEmpty(goods.getEndTime()),Goods::getCreateTime, goods.getEndTime());
IPage<Goods> pages = goodsService.page(Condition.getPage(query), lambdaQueryWrapper);
打印日志:
第一种
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志
或者:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl #关闭sql日志
第二种
logging:
level:
com.app.mapper: debug