本文是经过慕课网相关课程学习MyBatisPlus整理的笔记。
MyBatisPlus入门 : - ) 老师讲的挺好的,还不会MyBatisPlus的小伙伴门能够听一下。
MyBatisPlus官网
MyBatisPlus源码地址
#建立用户表 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>
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=true logging: level: root: warn org.ywb.demo.dao: trace pattern: console: '%p%m%n'
@Data public class User { private Long id; private String name; private Integer age; private String email; private String managerId; private LocalDateTime createTime; }
public interface UserMapper extends BaseMapper<User> { }
@MapperScan
扫描dao层接口@MapperScan("org.ywb.demo.dao") @SpringBootApplication public class MybatisPlusDemoApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusDemoApplication.class, args); } }
8.编写测试类java
@RunWith(SpringRunner.class) @SpringBootTest public class MybatisPlusDemoApplicationTests { @Resource private UserMapper userMapper; @Test public void select(){ List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } }
运行结果:mysql
MyBatisPlus提供了一些注解供咱们在实体类和表信息出现不对应的时候使用。经过使用注解完成逻辑上匹配。git
注解名称 | 说明 |
---|---|
@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)
注解BaseMapper中封装了不少关于增删该查的方法,后期自动生成,咱们直接调用接口中的相关方法便可完成相应的操做。BaseMapper
部分代码github
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); ... }
插入一条记录测试:web
@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); }
除了BaseMapper
中提供简单的增删改查方法以外,还提供了不少关于区间查询,多表链接查询,分组等等查询功能,实现的类图以下所示:
经过观察类图可知,咱们须要这些功能时,只须要建立QueryWrapper
对象便可。spring
/** * 查询名字中包含'雨'而且年龄小于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); }
/** * 建立日期为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); }
注意
上面的日期查询使用的是占位符的形式进行查询,目的就是为了防止SQL注入的风险。
apply方法的源码sql
/** * 拼接 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 '王%'");
/** * 名字为王姓,(年龄小于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); }
/** * 名字为王姓,(年龄小于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); }
/** * (年龄小于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); }
/** * 年龄为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); }
/** * 无视优化规则直接拼接到 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); }
/** * 查找为王姓的员工的姓名和年龄 */ @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); }
/** * 查询全部员工信息除了建立时间和员工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); }
在咱们调用的查询语句中,经过查看源码(这里以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条件。segmentfault
在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); }
若是想经过对象中某些属性进行模糊查询,咱们能够在跟数据库表对应的实体类中相应的属性标注注解便可。
好比咱们想经过姓名进行模糊查询用户列表。
@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); }
MybatisPlus提供了4种方式建立lambda条件构造器,前三种分别是这样的
LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda(); LambdaQueryWrapper<User> lambdaQueryWrapper1 = new LambdaQueryWrapper<>(); LambdaQueryWrapper<User> lambdaQueryWrapper2 = Wrappers.lambdaQuery();
@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); }
QueryWrapper类已经提供了很强大的功能,而lambda条件构造器作的和QueryWrapper的事也是相同的为何要冗余的存在lambda条件构造器呢?
QueryWrapper是经过本身写表中相应的属性进行构造where条件的,容易发生拼写错误,在编译时不会报错,只有运行时才会报错,而lambda条件构造器是经过调用实体类中的方法,若是方法名称写错,直接进行报错,因此lambda的纠错功能比QueryWrapper要提早不少。
举个例子:
查找姓名中包含“雨”字的员工信息。
使用QueryWrapperqueryWrapper.like("name","雨");使用lambda
lambdaQueryWrapper.like(User::getName,"雨");若是在拼写name的时候不当心,写成了naem,程序并不会报错,可是若是把方法名写成了getNaem程序当即报错。
第四种lambda构造器
细心的人都会发现不管是以前的lambda构造器仍是queryWrapper,每次编写完条件构造语句后都要将对象传递给mapper 的selectList方法,比较麻烦,MyBatisPlus提供了第四种函数式编程方式,不用每次都传。
@Test public void lambdaSelect(){ List<User> userList = new LambdaQueryChainWrapper<>(userMapper).like(User::getName, "雨").ge(User::getAge, 20).list(); userList.forEach(System.out::println); }
mybatis-plus: mapper-locations: mapper/*.xml
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物理分页插件
PaginationInterceptor
对象@Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); } }
@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); }
IPage类的构造参数提供了参数的重载,第三个参数为false时,不会查询总记录数。
public Page(long current, long size, boolean isSearchCount) { this(current, size, 0, isSearchCount); } ~~· ## 更新 1. 经过userMapper提供的方法更新用户信息
@Test public void updateTest1(){ User user = new User(); user.setId(1088250446457389058L); user.setEmail("update@email"); int rows = userMapper.updateById(user); System.out.println(rows); }
 2. 使用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); }
 3. 当咱们更新少许用户信息的时候,能够不用建立对象,直接经过调用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); }
 4. 使用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); }
 ## 删除 删除方式和update极其相似。 ## AR模式(Active Record) 直接经过实体类完成对数据的增删改查。 1. 实体类继承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便可完成对数据的增删改查。 1. 查询全部用户信息
@Test public void test(){ User user = new User(); user.selectAll().forEach(System.out::println); }
 ## 主键策略 MyBatisPlus的主键策略封装在`IdType`枚举类中。
@Getter
public 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官方文档](https://baomidou.gitee.io/mybatis-plus-doc/#/api?id=globalconfiguration)
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
- 附录 1. mybatisPlus进阶功能请戳[ MyBatisPlus学习整理(二)](https://www.jianshu.com/p/5c9e6acf9a70) 2. 源码地址:[https://github.com/xiao-ren-wu/notebook/tree/master/mybatis-plus-demo](https://github.com/xiao-ren-wu/notebook/tree/master/mybatis-plus-demo)