1.介绍你对MybatisPlus的理解?
mybatisPuls是对Mybatis的一个增强,在Mybatis的基础上进行增强,内部提供好了对于单表的简单的crud操作,可以提高效率,简称:MP
MP的特性:
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
需要引入的依赖为:
普通Maven项目下的依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.2</version>
</dependency>
SpringBoot环境下的MP启动器(导入这个启动器后不需要再导入Mybatis的启动器)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
配置文件配置数据库连接时可以加上sql打印:
#mybatis-plus配置控制台打印完整带参数SQL语句
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
MybatisPlus和Mybatis是什么关系?
MybatisPlus是增强版的Mybatis,对Mybatis只做增强,不影响其原有的功能
MybatisPlus可以解决那些问题?
当需要对单表进行简单的crud时,需要写sql什么的,太过于麻烦,这时候可以用mybatisPlus进行操作,只需要调用api传入相关的参数即可。
MybatisPlus使用步骤?
1:导入依赖 2:配置链接数据库 3:就可以使用了 4:想要使用分页查询需要使用分页内部拦截器 5:可以使用逆向工程生成service impl mapper pojo层
第一步的依赖上边已经写过了
第四步的配置拦截器代码:
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/**
* 想要使用分页,那么就要配置分页拦截器
*
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//第一步想创建一个mybatisPlus拦截器对象
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//创建一个分页内部拦截器,类型是musql
PaginationInnerInterceptor interceptor = new PaginationInnerInterceptor(DbType.MYSQL);
//设置页面最大效果,默认是false, true:如果请求的页数是10,但是一共就9页,那么就返回第一页也就是首页的值 false:如果查询的10一共9页,那么就继续查询第十页,明显是空
interceptor.setOverflow(true);
//设置最大单页限制数量,-1不受限制
interceptor.setMaxLimit(-1L);
mybatisPlusInterceptor.addInnerInterceptor(interceptor);
return mybatisPlusInterceptor;
}
MybatisPlus封装Service层的步骤
第一步,service实现Iservice("数据库对应的实体类") 第二步:因为service层实现了Iservice,所以service的实现层也就是impl里面,也要实现Iservice,所以impl的实现类也要继承ServiceImpl这个类,因为ServiceImpl实现了IService然后再去实现service层
MybatisPlus-API方法介绍
具体实现和细节看代码:
测试类都是api:
package com.itheima;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.*;
@SpringBootTest
public class TestMybatiesRuMen {
@Autowired
private UserMapper userMapper;
@Autowired
private UserService userService;
@Test
public void testFindById() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("user_name", "赵一伤");
List<User> users = userMapper.selectList(userQueryWrapper);
System.out.println(users);
// User user = userMapper.selectById(1);
// System.out.println("user = " + user);
}
@Test
public void insert01() {
/**
* 没有指定添加的id,mybatiesplus底层自动生成一个雪花算法来填充主键
* 如果传入到insert中的实体类写入了主键的值,那么就讲填入的主键的值插入进去
*/
User build = User.builder()
.userName("hmhinsert01")
.name("hmhname")
.age(18)
.email("1374729938@qq.com1")
.password("1234567").build();
int insert = userMapper.insert(build);
if (insert > 0) {
System.out.println("添加成功");
}
}
/**
* 根据id批量删除
*/
@Test
public void testDelete() {
//创建集合将需要删除的id保存进去然后之间将这个集合给mybatisplus的方法就可完成删除这个集合中的所有的id
ArrayList<Long> longs = new ArrayList<>();
longs.add(1537667301311516673L);
longs.add(1537667301311516674L);
int i = userMapper.deleteBatchIds(longs);
if (i > 1) System.out.println("删除成功");
}
@Test
public void testDelete02() {
int delete = userMapper.deleteById(1537667301311516675L);
if (delete > 1) System.out.println("删除成功");
}
/**
* 根据map删除
* key在使用特殊符号的时候,尽量小心点,一下面为例子,-- 就会出现sql错误因为最后弄出来以后是这样的一条sql
* DELETE FROM tb_user WHERE -- user_name = ? AND id = ? AND email = ?
* 会发现where后面的被-- 注释了
*/
@Test
public void deleteByMap() {
Map<String, Object> map = new HashMap<>();
map.put("-- user_name", "hmhinsert01");
map.put("id", "1537664068602064898");
map.put("email", "赵七伤");
int i = userMapper.deleteByMap(map);
if (i > 0) {
System.out.println("map删除成功");
}
}
/**
* 修改 修改的时候必须填写被修改的值的id
*/
@Test
public void testUpdate() {
// 构建修改后的数据信息
User user = User.builder()
.id(2L)
.userName("liuyan")
.password("6669")
.name("柳岩")
.age(18)
.email("liuyan@qq.com")
.build();
int i = userMapper.updateById(user);
System.out.println(i > 0 ? "修改成功" : "修改失败");
}
/**
* 分页查询
* 在mybatisPlus中分页查询需要配置分页拦截器
*/
@Test
public void testPage01() {
Page<User> page = new Page<>(1, 2);
page.setMaxLimit(-1L);
//下边是设置排序,可以插入集合,具体用法如下
List<OrderItem> orders = new ArrayList<>();
orders.add(OrderItem.asc("age"));
orders.add(OrderItem.asc("id"));
page.setOrders(orders);
userMapper.selectPage(page, null);
//获取分页后当前页的数据
System.out.println(page.getRecords());
//获取分页后当前页的数量
System.out.println(page.getSize());
//获取数据库总数量
System.out.println(page.getTotal());
//获取一共多少页
System.out.println(page.getPages());
//当前是第多少页
System.out.println(page.getCurrent());
//MappedStatement的id
System.out.println(page.getCountId());
//最大每页分页限制,优先级高于分页插件内的maxlimit
System.out.println(page.getMaxLimit());
//排序信息
System.out.println(page.getOrders());
}
/**
* queryWrapper
* 需求:查询用户中姓名包含"伤",密码为"123456",且年龄为19或者25或者29,查询结果按照年龄降序排序;
*/
@Test
public void queryWrapper() {
Page<User> page1 = new Page<>(1, 2);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("user_name", "伤")
.eq("password", "123456")
.in("age", 19, 25, 29).orderByDesc("age");
Page<User> page = userMapper.selectPage(page1, wrapper);
System.out.println(page);
System.out.println(page.getRecords());
//获取分页后当前页的数量
System.out.println(page.getSize());
//获取数据库总数量
System.out.println(page.getTotal());
//获取一共多少页
System.out.println(page.getPages());
//当前是第多少页
System.out.println(page.getCurrent());
//MappedStatement的id
System.out.println(page.getCountId());
//最大每页分页限制,优先级高于分页插件内的maxlimit
System.out.println(page.getMaxLimit());
//排序信息
System.out.println(page.getOrders());
}
/**
* 1.通过QueryWrapper多条件查询时,默认使用and关键字拼接SQL;
* 2.通过QueryWrapper调用or()方法时,底层会使用or关键字拼接方法左右的查询条件
* 业务要求:查询用户名为"liuyan"或者年龄大于23的用户信息;
*/
@Test
public void queryWrapperOr() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("user_name", "liuyan").or().gt("age", 23);
List<User> users = userMapper.selectList(userQueryWrapper);
System.out.println("users = " + users);
}
/**
* QueryWrapper模糊查询like
* 模糊查询有三个分别为左边添加% 右边添加% 两边都不添加% 分别对应likeLeft likeRight like
* like("表列名","条件值"); 作用:查询包含关键字的信息,底层会自动添加匹配关键字,比如:%条件值%
* likeLeft("表列名","条件值"); 作用:左侧模糊搜索,也就是查询以指定条件值结尾的数据,比如:%条件值
* likeRight("表列名","条件值");作用:右侧模糊搜索,也就是查询以指定条件值开头的数据,比如:条件值%
*/
@Test
public void queryWrapperLike() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.like("user_name", "伤").ne("user_name", "赵一伤");
List<User> users = userMapper.selectList(userQueryWrapper);
System.out.println("users = " + users);
}
/**
* 04-5-QueryWrapper排序查询
* orderByAsc 升序排序
* orderByDesc 降序排序
*/
@Test
public void queryWrapperOrder() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("age").orderByAsc("id");
List<User> users = userMapper.selectList(wrapper);
System.out.println("users = " + users);
}
/**
* 04-6-QueryWrapper限定字段查询
* MP查询时,默认将表中所有字段数据映射查询,但是有时我们仅仅需要查询部分字段信息,这是可以使用select()方法限定返回的字段信息,避免I/O资源的浪费;
* wrapper.select("字段1","字段2",......)
*/
@Test
public void queryWrapperSelect() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "user_name");
List<User> users = userMapper.selectList(wrapper);
System.out.println("users = " + users);
}
/**
* LambdaQueryWrapper查询
* 1.使用QueryWrapper查询数据时需要手写对应表的列名信息,及其容易写错,开发体验不好;
* 2.使用QueryWrapper查询数据时,表的列名硬编码书写,后期一旦表结构更改,则会带来很大的修改工作量,维护性较差;
* 个人理解就是因为上边是将名字写死了,比如user_name对应数据库就是user_name,但是如果可以对应实体类那么就可以使用@TableFiled这个注解更改名字
* 这样如果数据库字段名有更改,可以去改注解的名称
*/
@Test
public void lambdaQueryWrapper() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUserName, "liuyan");
List<User> users = userMapper.selectList(wrapper);
System.out.println("users = " + users);
}
/**
* 如果不写条件,那么就是更改的整个数据库的内容都会改变,注意写条件
* 使用wrapper其实就是将写入的内容当作条件并不能充当改变的数据
*/
@Test
public void update() {
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
//userUpdateWrapper.eq("id","2");
User user = new User(null, "2", "3", "4", 5, "6");
int update = userMapper.update(user, userUpdateWrapper);
System.out.println("update = " + update);
}
@Test
public void lambdaUpdateWrapper() {
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId, "2");
User user = new User(null, "2", "3", "4", 5, "6");
int update = userMapper.update(user, wrapper);
System.out.println("update = " + update);
}
@Test
public void zuoye() {
Page<User> page = new Page<>(1, 3);
//查询全部
List<User> list = userService.list();
System.out.println("list = " + list);
//分页查询全部
Page<User> userPage = userService.page(page);
System.out.println("userPage = " + userPage);
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(User::getId, 1, 2, 3, 3);
//按照条件分页查询
Page<User> page1 = userService.page(page, wrapper);
System.out.println("page1.total = " + page1.getTotal());
LambdaQueryWrapper<User> wrapperOne = new LambdaQueryWrapper<>();
wrapperOne.eq(User::getId, 1);
//getOne查询一条根据条件,如果数据库中符合条件的有多条那么就报错,必须符合条件的只有一条
User one = userService.getOne(wrapperOne);
System.out.println("one = " + one);
//根据id查询
User byId = userService.getById(1);
System.out.println("byId = " + byId);
//添加,如果id为nill那么就自动递增,默认是雪花算法
boolean save = userService.save(new User(null, "2,", "3", "4", 5, "6"));
System.out.println("save = " + save);
//根据传入的实体类的id进行修改
boolean b = userService.updateById(new User(17L, "2,", "3", "4", 5, "6"));
System.out.println("b = " + b);
LambdaUpdateWrapper<User> wrapperUpdate = new LambdaUpdateWrapper<>();
wrapperUpdate.eq(User::getId, 1537832400332623874L);
//根据条件修改
boolean update = userService.update(new User(17L, "2,", "3", "4", 5, "90"), wrapperUpdate);
System.out.println("update = " + update);
//保存或者修改,如果有这个id那么就更新,如果没有那么就更改
boolean saveOrUpdate = userService.saveOrUpdate(new User(18L, "2,", "3", "4", 5, "8"));
System.out.println("saveOrUpdate = " + saveOrUpdate);
//根据id删除
boolean remove01 = userService.removeById(18);
System.out.println("remove01 = " + remove01);
//批量删除根据id
ArrayList<Integer> relist1 = new ArrayList<>();
Collections.addAll(relist1, 17, 16);
boolean remove = userService.removeByIds(relist1);
System.out.println("remove = " + remove);
//根据条件删除
LambdaUpdateWrapper<User> wrapper1 = new LambdaUpdateWrapper<>();
wrapper1.eq(User::getId, 14);
boolean remove1 = userService.remove(wrapper1);
System.out.println("remove1 = " + remove1);
}
}
实体类:这里有一个@TableName @@TableFiled 集体看代码
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@TableName("tb_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User implements Serializable {
/**
* IdType.AUTO在数据库中的主键必须是自动递增而且还得是int数据类型
* IdType.ASSIGN_ID 是雪花算法也是数据库默认的
* IdType.ASSIGN_UUID 生成的随机数有字母和数字,所以主键在数据库中是String类型
* mybatisplus默认的数据类型是雪花算法
*/
//@TableId(type = IdType.AUTO)
//@TableId(type = IdType.ASSIGN_UUID)
private Long id;
//@TableField("tname")
private String userName;
private String password;
private String name;
private Integer age;
/**
* 这个注解可以是指定名称,比如user这个实体类中email对应的字段在数据库中是email_1,
* 那么可以使用@TableField("email_1")来指定
* 如果email有值但是插入的时候又不想让这个email插入到数据库中,可以使用 @
* TableField(exist = false)
* 来讲email这个字段忽略插入这样不管email有没有值都会跳过
*
*UPDATE tb_user SET user_name=?, password=?, name=?, age=? WHERE id=?
* 会发现如果使用了下边的注解 直接跳过 sql中根本不会出现email这个字段
* 修改为例子
* 并且这个注解只能当传入的是这个类的对象为参数的时候才可以
*/
//@TableField("email_1")
//@TableField(exist = false)
private String email;
}
service实现层:因为ServiceImpl已经注入了Mapper,所以用到Mapper的时候不用注入了直接可以用底层的一个basemapper就相当于是Mapper层的
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import com.itheima.service.UserService;
import org.springframework.stereotype.Service;
/**
* 因为Uservice接口继承了Iservice接口,所以实现类在实现UserService的时候,也要实现Iservice这个接口,又因为ServiceImpl正好实现了这个Iservice这个接口所以就需要这样写
* ServiceImpl的泛型第一个是UserMapper,因为这个泛型底层规定了<M extends BaseMapper<T>,正好UserMapper就是继承了BaseMapper
* ServiceImpl把所有的都重写完了,因为里面有一个protected M baseMapper; 也就是传入的UserMapper那么M也就是UserMapper,这时候底层注入,那么也就是注入UserMapper,又因为UserMapper继承了
* BaseMapper那么就调用BaseMapper的方法,这样ServiceImpl就完成了实现,然后这个实现类也可以baseMapper进行操作就不用注入UserMapper了因为UserMapper和baseMapper是等价的
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
Service层:
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.pojo.User;
public interface UserService extends IService<User> {
}