打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
MyBatisPlus学习整理
本文是通过慕课网相关课程学习MyBatisPlus整理的笔记。
MyBatisPlus入门 : - ) 老师讲的挺好的,还不会MyBatisPlus的小伙伴门可以听一下。
MyBatisPlus官网
MyBatisPlus源码地址
MyBatisPlus架构图(盗用官网的,侵,删。)
mybatis-plus.png
SpringBoot第一个简单应用
数据库建表
#创建用户表CREATE TABLE user ( id BIGINT(20) PRIMARY KEY NOT NULL COMMENT '主键', name VARCHAR(30) DEFAULT NULL COMMENT '姓名', age INT(11) DEFAULT NULL COMMENT '年龄', email VARCHAR(50) DEFAULT NULL COMMENT '邮箱', manager_id BIGINT(20) DEFAULT NULL COMMENT '直属上级id', create_time DATETIME DEFAULT NULL COMMENT '创建时间', CONSTRAINT manager_fk FOREIGN KEY (manager_id) REFERENCES user (id)) ENGINE=INNODB CHARSET=UTF8;#初始化数据:INSERT INTO user (id, name, age, email, manager_id , create_time)VALUES (1087982257332887553, '大boss', 40, 'boss@baomidou.com', NULL , '2019-01-11 14:20:20'), (1088248166370832385, '王天风', 25, 'wtf@baomidou.com', 1087982257332887553 , '2019-02-05 11:12:22'), (1088250446457389058, '李艺伟', 28, 'lyw@baomidou.com', 1088248166370832385 , '2019-02-14 08:31:16'), (1094590409767661570, '张雨琪', 31, 'zjq@baomidou.com', 1088248166370832385 , '2019-01-14 09:15:15'), (1094592041087729666, '刘红雨', 32, 'lhm@baomidou.com', 1088248166370832385 , '2019-01-14 09:48:16');依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.2</version> </dependency>springboot配置文件
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=truelogging: level: root: warn org.ywb.demo.dao: trace pattern: console: '%p%m%n'
创建相关包,如图:
image.png
在pojo包中新建和数据库user表映射的类
@Datapublic class User { private Long id; private String name; private Integer age; private String email; private String managerId; private LocalDateTime createTime;}在dao包中创建mapper接口,并集成mybatisPlus的BaseMapper
public interface UserMapper extends BaseMapper<User> {}在springboot启动类添加@MapperScan扫描dao层接口
@MapperScan("org.ywb.demo.dao")@SpringBootApplicationpublic class MybatisPlusDemoApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusDemoApplication.class, args); }}
8.编写测试类
@RunWith(SpringRunner.class)@SpringBootTestpublic class MybatisPlusDemoApplicationTests { @Resource private UserMapper userMapper; @Test public void select(){ List<User> users = userMapper.selectList(null); users.forEach(System.out::println); }}
运行结果:
image.png
常用注解
MyBatisPlus提供了一些注解供我们在实体类和表信息出现不对应的时候使用。通过使用注解完成逻辑上匹配。
注解名称说明
@TableName实体类的类名和数据库表名不一致
@TableId实体类的主键名称和表中主键名称不一致
@TableField实体类中的成员名称和表中字段名称不一致
@Data@TableName("t_user")public class User { @TableId("user_id") private Long id; @TableField("real_name") private String name; private Integer age; private String email; private Long managerId; private LocalDateTime createTime;}排除实体类中非表字段
使用transient关键字修饰非表字段,但是被transient修饰后,无法进行序列化。
使用static关键字,因为我们使用的是lombok框架生成的get/set方法,所以对于静态变量,我们需要手动生成get/set方法。
使用@TableField(exist = false)注解
CURD
BaseMapper中封装了很多关于增删该查的方法,后期自动生成,我们直接调用接口中的相关方法即可完成相应的操作。
BaseMapper部分代码
public interface BaseMapper<T> extends Mapper<T> { int insert(T entity); int deleteById(Serializable id); int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); int updateById(@Param(Constants.ENTITY) T entity);...}
插入一条记录测试:
@Test public void insert(){ User user = new User(); user.setAge(31); user.setManagerId(1088250446457389058L); user.setCreateTime(LocalDateTime.now()); int insert = userMapper.insert(user); System.out.println("影像记录数:"+insert); }
image.png
条件构造器查询
除了BaseMapper中提供简单的增删改查方法之外,还提供了很多关于区间查询,多表连接查询,分组等等查询功能,实现的类图如下所示:
image.png
通过观察类图可知,我们需要这些功能时,只需要创建QueryWrapper对象即可。模糊查询
/** * 查询名字中包含'雨'并且年龄小于40 * where name like '%雨%' and age < 40 */ @Test public void selectByWrapper(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("name","雨").lt("age",40); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
嵌套查询
/** * 创建日期为2019年2月14日并且直属上级姓名为王姓 * date_format(create_time,'%Y-%m-%d') and manager_id in (select id from user where name like '王%') */ @Test public void selectByWrapper2(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.apply("date_format(create_time,'%Y-%m-%d')={0}","2019-02-14") .inSql("manager_id","select id from user where name like '王%'"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
注意
上面的日期查询使用的是占位符的形式进行查询,目的就是为了防止SQL注入的风险。
apply方法的源码 /** * 拼接 sql * <p>!! 会有 sql 注入风险 !!</p> * <p>例1: apply("id = 1")</p> * <p>例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")</p> * <p>例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())</p> * * @param condition 执行条件 * @return children */ Children apply(boolean condition, String applySql, Object... value);
SQL 注入的例子:
queryWrapper.apply("date_format(create_time,'%Y-%m-%d')=2019-02-14 or true=true") .inSql("manager_id","select id from user where name like '王%'");
sql注入例子
and & or
/** * 名字为王姓,(年龄小于40或者邮箱不为空) */ @Test public void selectByWrapper3(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.likeRight("name","王").and(wq-> wq.lt("age",40).or().isNotNull("email")); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }between & and
/** * 名字为王姓,(年龄小于40,并且年龄大于20,并且邮箱不为空) */ @Test public void selectWrapper4(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.likeRight("name", "王").and(wq -> wq.between("age", 20, 40).and(wqq -> wqq.isNotNull("email"))); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
nested
/** * (年龄小于40或者邮箱不为空)并且名字为王姓 * (age<40 or email is not null)and name like '王%' */ @Test public void selectWrapper5(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email")).likeRight("name","王"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
in
/** * 年龄为30,31,35,34的员工 */ @Test public void selectWrapper6(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.in("age", Arrays.asList(30,31,34,35)); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
last 有SQL注入的风险!!!
/** * 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用) * <p>例: last("limit 1")</p> * <p>注意只能调用一次,多次调用以最后一次为准</p> * * @param condition 执行条件 * @param lastSql sql语句 * @return children */ Children last(boolean condition, String lastSql); /** * 只返回满足条件的一条语句即可 * limit 1 */ @Test public void selectWrapper7(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.in("age", Arrays.asList(30,31,34,35)).last("limit 1"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
查询指定部分列
/** * 查找为王姓的员工的姓名和年龄 */ @Test public void selectWrapper8(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.select("name","age").likeRight("name","王"); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
使用过滤器查询指定列
/** * 查询所有员工信息除了创建时间和员工ID列 */ @Test public void selectWrapper9(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.select(User.class,info->!info.getColumn().equals("create_time") &&!info.getColumn().equals("manager_id")); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
condition 的作用
在我们调用的查询语句中,通过查看源码(这里以apply方法为例)可以看出,每个查询方法的第一个参数都是boolean类型的参数,重载方法中默认给我们传入的都是true。
default Children apply(String applySql, Object... value) { return apply(true, applySql, value); } Children apply(boolean condition, String applySql, Object... value);
这个condition的作用是为true时,执行其中的SQL条件,为false时,忽略设置的SQL条件。
实体作为条件构造方法的参数
在web开发中,controller层常常会传递给我们一个用户的对象,比如通过用户姓名和用户年龄查询用户列表。
我们可以将传递过来的对象直接以构造参数的形式传递给QueryWrapper,MyBatisPlus会自动根据实体对象中的属性自动构建相应查询的SQL语句。
@Test public void selectWrapper10(){ User user = new User(); user.setName("刘红雨"); user.setAge(32); QueryWrapper<User> queryWrapper = new QueryWrapper<>(user); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
如果想通过对象中某些属性进行模糊查询,我们可以在跟数据库表对应的实体类中相应的属性标注注解即可。
比如我们想通过姓名进行模糊查询用户列表。
@TableField(condition = SqlCondition.LIKE) private String name; @Test public void selectWrapper10(){ User user = new User(); user.setName("红"); user.setAge(32); QueryWrapper<User> queryWrapper = new QueryWrapper<>(user); List<User> userList = userMapper.selectList(queryWrapper); userList.forEach(System.out::println); }
image.png
Lambda条件构造器
MybatisPlus提供了4种方式创建lambda条件构造器,前三种分别是这样的
LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda(); LambdaQueryWrapper<User> lambdaQueryWrapper1 = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> lambdaQueryWrapper2 = Wrappers.lambdaQuery();查询名字中包含‘雨’并且年龄小于40的员工信息
@Test public void lambdaSelect(){ LambdaQueryWrapper<User> lambdaQueryWrapper = Wrappers.lambdaQuery(); lambdaQueryWrapper.like(User::getName,"雨").lt(User::getAge,40); List<User> userList = userMapper.selectList(lambdaQueryWrapper); userList.forEach(System.out::println); }
image.png
QueryWrapper类已经提供了很强大的功能,而lambda条件构造器做的和QueryWrapper的事也是相同的为什么要冗余的存在lambda条件构造器呢?
QueryWrapper是通过自己写表中相应的属性进行构造where条件的,容易发生拼写错误,在编译时不会报错,只有运行时才会报错,而lambda条件构造器是通过调用实体类中的方法,如果方法名称写错,直接进行报错,所以lambda的纠错功能比QueryWrapper要提前很多。
举个例子:
查找姓名中包含“雨”字的员工信息。
使用QueryWrapper
queryWrapper.like("name","雨");
使用lambda
lambdaQueryWrapper.like(User::getName,"雨");
如果在拼写name的时候不小心,写成了naem,程序并不会报错,但是如果把方法名写成了getNaem程序立即报错。
第四种lambda构造器
细心的人都会发现无论是之前的lambda构造器还是queryWrapper,每次编写完条件构造语句后都要将对象传递给mapper 的selectList方法,比较麻烦,MyBatisPlus提供了第四种函数式编程方式,不用每次都传。
查询名字中包含“雨”字的,并且年龄大于20的员工信息
@Test public void lambdaSelect(){ List<User> userList = new LambdaQueryChainWrapper<>(userMapper).like(User::getName, "雨").ge(User::getAge, 20).list(); userList.forEach(System.out::println); }
image.png
自定义SQL
在resources资源文件夹下新建mapper文件夹,并将mapper文件夹的路径配置到配置文件中
image.png
mybatis-plus: mapper-locations: mapper/*.xml在mapper 文件夹中新建UserMapper.xml。
像mybatis那样在UseMapper接口中写接口,在UserMapper接口中写SQL即可。
UserMapper
public interface UserMapper extends BaseMapper<User> { /** * 查询所有用户信息 * @return list */ List<User> selectAll();}
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="org.ywb.demo.dao.UserMapper"> <select id="selectAll" resultType="org.ywb.demo.pojo.User"> select * from user </select></mapper>分页查询
MyBatis分页提供的是逻辑分页,每次将所有数据查询出来,存储到内存中,然后根据页容量,逐页返回。如果表很大,无疑是一种灾难!
MyBatisPlus物理分页插件
新建config类,在config类中创建PaginationInterceptor对象
@Configurationpublic class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); }}测试:查询年龄大于20 的用户信息,并以每页容量为两条分页的形式返回。
@Test public void selectPage(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.ge("age",20); //设置当前页和页容量 Page<User> page = new Page<>(1, 2); IPage<User> userIPage = userMapper.selectPage(page, queryWrapper); System.out.println("总页数:"+userIPage.getPages()); System.out.println("总记录数:"+userIPage.getTotal()); userIPage.getRecords().forEach(System.out::println); }
image.png
测试:不查询总记录数,分页查询
IPage类的构造参数提供了参数的重载,第三个参数为false时,不会查询总记录数。
public Page(long current, long size, boolean isSearchCount) { this(current, size, 0, isSearchCount);}~~·## 更新1. 通过userMapper提供的方法更新用户信息~~~java @Test public void updateTest1(){ User user = new User(); user.setId(1088250446457389058L); user.setEmail("update@email"); int rows = userMapper.updateById(user); System.out.println(rows); }
image.png
使用UpdateWrapper更新数据(相当于使用联合主键)
@Test public void updateTest2(){ UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("name","李艺伟").eq("age",26); User user = new User(); user.setEmail("update2@email"); int rows = userMapper.update(user, updateWrapper); System.out.println(rows); }
image.png
当我们更新少量用户信息的时候,可以不用创建对象,直接通过调用set方法更新属性即可。
@Test public void updateTest3(){ UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("name","李艺伟").eq("age",26).set("email","update3@email.com"); userMapper.update(null,updateWrapper); }
image.png
使用lambda更新数据
@Test public void updateByLambda(){ LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate(); lambdaUpdateWrapper.eq(User::getName,"李艺伟").eq(User::getAge,26).set(User::getAge,27); userMapper.update(null,lambdaUpdateWrapper); }
image.png
删除
删除方式和update极其类似。
AR模式(Active Record)
直接通过实体类完成对数据的增删改查。
实体类继承Model类
@Data@EqualsAndHashCode(callSuper = false)public class User extends Model<User> { private Long id; @TableField(condition = SqlCondition.LIKE) private String name; private Integer age; private String email; private Long managerId; private LocalDateTime createTime;}
Model类中封装了很多增删改查方法,不用使用UserMapper即可完成对数据的增删改查。
查询所有用户信息
@Test public void test(){ User user = new User(); user.selectAll().forEach(System.out::println); }
image.png
主键策略
MyBatisPlus的主键策略封装在IdType枚举类中。
@Getterpublic enum IdType { /** * 数据库ID自增 */ AUTO(0), /** * 该类型为未设置主键类型(将跟随全局) */ NONE(1), /** * 用户输入ID * <p>该类型可以通过自己注册自动填充插件进行填充</p> */ INPUT(2), /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */ /** * 全局唯一ID (idWorker) */ ID_WORKER(3), /** * 全局唯一ID (UUID) */ UUID(4), /** * 字符串全局唯一ID (idWorker 的字符串表示) */ ID_WORKER_STR(5); private final int key; IdType(int key) { this.key = key; }}
在实体类中对应数据库中的主键id属性上标注注解TableId(type='xxx')即可完成主键配置。
@TableId(type = IdType.AUTO) private Long id;
这种配置方式的主键策略只能在该表中生效,但是其他表还需要进行配置,为了避免冗余,麻烦,MybatisPlus提供了全局配置,在配置文件中配置主键策略即可实现。
mybatis-plus: mapper-locations: mapper/*.xml global-config: db-config: id-type: auto
如果全局策略和局部策略全都设置,局部策略优先。
基本配置
MyBatisPlus官方文档
mybatis-plus: mapper-locations: mapper/*.xml global-config: db-config: # 主键策略 id-type: auto # 表名前缀 table-prefix: t # 表名是否使用下划线间隔,默认:是 table-underline: true # 添加mybatis配置文件路径 config-location: mybatis-config.xml # 配置实体类包地址 type-aliases-package: org.ywb.demo.pojo # 驼峰转下划线 configuration: map-underscore-to-camel-case: true附录
源码地址:https://github.com/xiao-ren-wu/notebook/tree/master/mybatis-plus-demo
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Mybatis-Plus3.0入门手册
MyBatis-Plus配置日志与CRUD
熟练掌握 mybatis-plus,一篇就够!
MyBatisPlus 入门教程,这篇很赞
5.mybatisPlus自定义SQL_mybatisplus 自定义sql
Mybatis缓存介绍
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服