本文是通过慕课网相关课程学习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');

依赖

org.springframework.boot

spring-boot-starter-jdbc

org.springframework.boot

spring-boot-starter-web

mysql

mysql-connector-java

runtime

org.springframework.boot

spring-boot-configuration-processor

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

com.baomidou

mybatis-plus-boot-starter

3.1.2

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=true

logging:

level:

root: warn

org.ywb.demo.dao: trace

pattern:

console: '%p%m%n'

创建相关包,如图:

image.png

在pojo包中新建和数据库user表映射的类

@Data

public 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 {

}

在springboot启动类添加@MapperScan扫描dao层接口

@MapperScan("org.ywb.demo.dao")

@SpringBootApplication

public class MybatisPlusDemoApplication {

public static void main(String[] args) {

SpringApplication.run(MybatisPlusDemoApplication.class, args);

}

}

8.编写测试类

@RunWith(SpringRunner.class)

@SpringBootTest

public class MybatisPlusDemoApplicationTests {

@Resource

private UserMapper userMapper;

@Test

public void select(){

List 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 extends Mapper {

int insert(T entity);

int deleteById(Serializable id);

int deleteByMap(@Param(Constants.COLUMN_MAP) Map columnMap);

int delete(@Param(Constants.WRAPPER) Wrapper 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 queryWrapper = new QueryWrapper<>();

queryWrapper.like("name","雨").lt("age",40);

List 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 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 userList = userMapper.selectList(queryWrapper);

userList.forEach(System.out::println);

}

image.png

注意

上面的日期查询使用的是占位符的形式进行查询,目的就是为了防止SQL注入的风险。

apply方法的源码

/**

* 拼接 sql

*

!! 会有 sql 注入风险 !!

*

例1: apply("id = 1")

*

例2: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

*

例3: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", LocalDate.now())

*

* @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 queryWrapper = new QueryWrapper<>();

queryWrapper.likeRight("name","王").and(wq-> wq.lt("age",40).or().isNotNull("email"));

List userList = userMapper.selectList(queryWrapper);

userList.forEach(System.out::println);

}

between & and

/**

* 名字为王姓,(年龄小于40,并且年龄大于20,并且邮箱不为空)

*/

@Test

public void selectWrapper4(){

QueryWrapper queryWrapper = new QueryWrapper<>();

queryWrapper.likeRight("name", "王").and(wq -> wq.between("age", 20, 40).and(wqq -> wqq.isNotNull("email")));

List 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 queryWrapper = new QueryWrapper<>();

queryWrapper.nested(wq->wq.lt("age",40).or().isNotNull("email")).likeRight("name","王");

List userList = userMapper.selectList(queryWrapper);

userList.forEach(System.out::println);

}

image.png

in

/**

* 年龄为30,31,35,34的员工

*/

@Test

public void selectWrapper6(){

QueryWrapper queryWrapper = new QueryWrapper<>();

queryWrapper.in("age", Arrays.asList(30,31,34,35));

List userList = userMapper.selectList(queryWrapper);

userList.forEach(System.out::println);

}

image.png

last 有SQL注入的风险!!!

/**

* 无视优化规则直接拼接到 sql 的最后(有sql注入的风险,请谨慎使用)

*

例: last("limit 1")

*

注意只能调用一次,多次调用以最后一次为准

*

* @param condition 执行条件

* @param lastSql sql语句

* @return children

*/

Children last(boolean condition, String lastSql);

/**

* 只返回满足条件的一条语句即可

* limit 1

*/

@Test

public void selectWrapper7(){

QueryWrapper queryWrapper = new QueryWrapper<>();

queryWrapper.in("age", Arrays.asList(30,31,34,35)).last("limit 1");

List userList = userMapper.selectList(queryWrapper);

userList.forEach(System.out::println);

}

image.png

查询指定部分列

/**

* 查找为王姓的员工的姓名和年龄

*/

@Test

public void selectWrapper8(){

QueryWrapper queryWrapper = new QueryWrapper<>();

queryWrapper.select("name","age").likeRight("name","王");

List userList = userMapper.selectList(queryWrapper);

userList.forEach(System.out::println);

}

image.png

使用过滤器查询指定列

/**

* 查询所有员工信息除了创建时间和员工ID列

*/

@Test

public void selectWrapper9(){

QueryWrapper queryWrapper = new QueryWrapper<>();

queryWrapper.select(User.class,info->!info.getColumn().equals("create_time")

&&!info.getColumn().equals("manager_id"));

List 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 queryWrapper = new QueryWrapper<>(user);

List 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 queryWrapper = new QueryWrapper<>(user);

List userList = userMapper.selectList(queryWrapper);

userList.forEach(System.out::println);

}

image.png

Lambda条件构造器

MybatisPlus提供了4种方式创建lambda条件构造器,前三种分别是这样的

LambdaQueryWrapper lambdaQueryWrapper = new QueryWrapper().lambda();

LambdaQueryWrapper lambdaQueryWrapper1 = new LambdaQueryWrapper<>();

LambdaQueryWrapper lambdaQueryWrapper2 = Wrappers.lambdaQuery();

查询名字中包含‘雨’并且年龄小于40的员工信息

@Test

public void lambdaSelect(){

LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery();

lambdaQueryWrapper.like(User::getName,"雨").lt(User::getAge,40);

List 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 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 {

/**

* 查询所有用户信息

* @return list

*/

List selectAll();

}

UserMapper.xml

select * from user

分页查询

MyBatis分页提供的是逻辑分页,每次将所有数据查询出来,存储到内存中,然后根据页容量,逐页返回。如果表很大,无疑是一种灾难!

MyBatisPlus物理分页插件

新建config类,在config类中创建PaginationInterceptor对象

@Configuration

public class MybatisPlusConfig {

@Bean

public PaginationInterceptor paginationInterceptor(){

return new PaginationInterceptor();

}

}

测试:查询年龄大于20 的用户信息,并以每页容量为两条分页的形式返回。

@Test

public void selectPage(){

QueryWrapper queryWrapper = new QueryWrapper<>();

queryWrapper.ge("age",20);

//设置当前页和页容量

Page page = new Page<>(1, 2);

IPage 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 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 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 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 {

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枚举类中。

@Getter

public enum IdType {

/**

* 数据库ID自增

*/

AUTO(0),

/**

* 该类型为未设置主键类型(将跟随全局)

*/

NONE(1),

/**

* 用户输入ID

*

该类型可以通过自己注册自动填充插件进行填充

*/

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

如果全局策略和局部策略全都设置,局部策略优先。

基本配置

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

附录

mybitsplus的idworker应java什么类型_MyBatisPlus学习整理(一)相关推荐

  1. java日期类型_Java 学习笔记 (三) Java 日期类型

    以下内容摘自:  https://www.cnblogs.com/crazylqy/p/4172324.html import java.sql.Timestamp; import java.text ...

  2. 《深入理解java内存模型》学习整理1

    为什么80%的码农都做不了架构师?>>>    1:在java中,所有实例域.静态域和数组元素存储在堆内存中,堆内存在线程之间共享. 2:局部变量.方法定义参数和异常处理器参数不会在 ...

  3. mybatis plus 格式话_MyBatisPlus学习整理(二)

    不多bibi,直接建表 DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` bigint(20) NULL DEFAULT NULL COM ...

  4. java注解类型_Java注解类型

    本篇文章帮大家学习java注解类型,包含了Java注解类型使用方法.操作技巧.实例演示和注意事项,有一定的学习价值,大家可以用来参考. 标记注解类型 标记注解类型是没有元素的注解类型,甚至没有默认值. ...

  5. java注释类型_Java 8类型注释

    java注释类型 Lambda表达式是迄今为止Java 8讨论最多和最受促进的功能.虽然我同意Lambda是一个很大的改进,但我认为其他一些Java 8功能由于Lambda的炒作而有所欠缺. 在这篇文 ...

  6. Java 8类型注释

    Lambda表达式是迄今为止Java 8讨论最多和最受促进的功能.虽然我同意Lambda是一个很大的改进,但我认为其他一些Java 8功能由于Lambda的炒作而有所欠缺. 在这篇文章中,我想展示另一 ...

  7. 深入理解Java注解类型(@Annotation)

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/71860633 出自[zejian ...

  8. C#到Java byte类型冲突的解决

    最近要改写一个核心加密认证类,从C#改写成Java. 发现在调试时,加密的数据无论如何也对不上. 经过跟踪,发现问题出在C#和Java byte类型的区别上:在C#里 byte类型是无符号的,而Jav ...

  9. Android JNI 第三篇 Java参数类型与本地参数类型对照

    转载请标明出处: http://blog.csdn.net/michael1112/article/details/56665383 江东橘子的博客 这一篇从基础上了解一些Java参数类型与本地参数类 ...

最新文章

  1. Xml解析之PULL解析 例1
  2. java jmf 视屏监控的核心代码_Java中利用JMF编写摄像头拍照程序_java
  3. SAP成都研究院的小伙伴们庆祝公司再次获得2019年最佳雇主的场景
  4. redisTemplate进行自增操作例子
  5. java工具类应该抛异常吗,java学习阶段一 工具类(异常)
  6. vue回调函数this指向问题
  7. iPhone程序中如何生成随机数
  8. 190415每日一句
  9. iptables 性能 测试
  10. 信息论Matlab仿真——信源熵
  11. Linux下mysql数据恢复
  12. 影视剪辑,PR剪辑软件两个转场教程
  13. 用 Webgoat 撬动地球,看安全测试的引路石!
  14. 勒索病毒数据库恢复 勒索病毒解密恢复 中勒索病毒解密恢复数据
  15. C语言中将数字形式的字符串转换为数字的方法
  16. 控制面板打开后立即自动关闭
  17. 对于CRM之于现代化企业的影响以及作用的分析
  18. 【随记】Dialog dismiss无法正常关闭问题
  19. 引流又增效,跨境电商必备的宝藏工具
  20. vue3源码系列之计算属性computed原理剖析

热门文章

  1. shell变量$$,$!,$?,$*,$0,$1,$#,$@的含义解释
  2. 【Day01】你有封装过 axios 吗?主要是封装哪些方面?如何中断 axios 请求?
  3. C#经典算法实践,回顾往生,更是致敬《算法导论》
  4. mysql基本功能+show+innodb+索引+慢sql+explain
  5. 微信web-view 开发_在不到7个月的时间里我如何成为一名Web开发人员-以及如何
  6. 使用React,TypeScript和Socket.io构建聊天应用
  7. oc开源应用_失眠:一款开源应用,可根据睡眠周期告诉您何时入睡
  8. 124_Power PivotPower BI DAX优化计算最大连续次数
  9. docker中使用postgresql
  10. Matlab常见问题处理——错误使用 xlswrite (line 219),调用错误,调度异常: 参数错误。