Mybatis-Plus的使用


MyBatisPlus

什么是MybatisPlus

一个开源项目,非侵入式,帮助简化Mybatis的使用。

如何使用

0. 准备工作

建立数据库mybatis_plus

建表与导入数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DROP TABLE IF EXISTS user;

CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

1.新建项目,添加依赖

新建项目没啥说的,新建个SpringBoot的项目就行。

添加依赖,pom.xml文件中依赖如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>

<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>

<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>

其中需要安装Idea的Lombok的插件,我早就装好了。

2.application配置文件

这个配置文件里连接数据库的地方,有点需要注意的

1
2
3
4
5
6
7
8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

  1. driver-class-name处,mysql8以前,用com.mysql.jdbc.Driver,之后需要加上这个cj,变成com.mysql.cj.jdbc.Driver,不然会报警告。。。
  2. url处,在mysql8以后,需要加上那个时区:?serverTimezone=GMT%2B8

1) 简单·的实现查询

建立entity与mapper文件夹,同时建立User.java实体类与UserMapper.java类

User类中包括简单的信息,然后写上Lombok的@Data注解就有了getter和setter方法以及有参无参的构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
package com.songx64.mpdemo1010.entity;

import lombok.Data;

@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}

UserMapper则是接口,集成了BaseMapper,这个是mybatisplus里面的一个接口,实现了基本的增删改查之类的。继承时候需要一个模板T。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.songx64.mpdemo1010.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.songx64.mpdemo1010.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

//下面三个注解任一个,解决自动注入Mapper时候,飘红线
//@Component
//@Service
//@Repository
//@Mapper
public interface UserMapper extends BaseMapper<User> {
//SpringBoot启动的时候,接口要找的话,会找接口这个实现类的的对象
// 但是这里没有实现类,所以说要去启动类中加入@MapperScan注解
}

注释里写了部分东西了。

之后还需要改启动类里面的一点东西,就是加个@MapperScan注解,扫描某个路径下的Mapper。

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@MapperScan("com.songx64.mpdemo1010.mapper")
public class Mpdemo1010Application {

public static void main(String[] args) {
SpringApplication.run(Mpdemo1010Application.class, args);
}

}

当然这个@MapperScan注解也可以不加,而是在UserMapper接口中加上个@Mapper注解,也应该是一样的效果。

2)查询的单元测试

编写单元测试,进行简单的查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.songx64.mpdemo1010;

@SpringBootTest
public class Mpdemo1010ApplicationTests {

@Autowired
private UserMapper userMapper;

@Test
public void findAll() {
List<User> users = userMapper.selectList(null);
System.out.println(users);

}

}

就只是自动注入了一个UserMapper,然后直接调用里面的selectList。

这里有个小技巧,写userMapper.selectList(null).var然后回车,IDEA就会自动生成一个变量: List users = userMapper.selectList(null)也就是这一句。

3)简单的插入

再在单元测试里加个添加的方法:

1
2
3
4
5
6
7
8
9
10
11
@Test
public void insert() {
User user = new User();
user.setName("Lucy");
user.setAge(18);
user.setEmail("PleaseLucy@gmail.com");

int insert = userMapper.insert(user);
System.out.println("insert:" + insert);

}

运行结果如下:

image-20200814222227381

这个就是输出的日志,可以看出插入成功了。

这里要提的就是这个Id主键,没有给他手动设置,他还是插入了一个值:“1294276830076878850”。这个是MybatisPlus自动生成的。

生成策略下面讲一下。

4) 生成策略

可能面试会问到相关的的东西。

参考网址:https://www.cnblogs.com/haoxinyue/p/5208136.html

生成策略有以下几种

1.自增主键

Auto Increment

优点:

1)简单,代码方便,性能可以接受。

2)数字ID天然排序,对分页或者需要排序的结果很有帮助。

缺点:

分表存储,每次都需要得到上一张表的末尾ID不方便。

image-20200831201543956

2.UUID

image-20200831201601046

优点:唯一。

缺点:排序不方便。

3. Redis生成

image-20200831201810060

4. MybatisPlus自带算法,Twitter的Snowflake算法

结果是一个long型的ID。

5) Id操作

在实体类User.java中,加上一个注解:

1
2
@TableId(type = IdType.ID_WORKER)
private Long id;

其中 ‘@TableId(type = IdType.XXX)’ 是固定的。

去看IdType的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 数据库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);

其中NONE是不设定,需要人手动输入的。

6) 自动填充

  1. 在enity实体类User.java中的字段上加入注解@TableField(fill = FieldFill.XXX)

    1
    2
    3
    4
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
  2. 实现接口MetaObjectHandler, 重写里面的方法中insertFill和updateFill . 根据名字来设定值.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package com.songx64.mpdemo1010.handler;

    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;

    import java.util.Date;

    /**
    * @author Admin
    */
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
    this.setFieldValByName("createTime", new Date(), metaObject);
    this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
    this.setFieldValByName("updateTime", new Date(), metaObject);
    }
    }

7)乐观锁

什么是乐观锁

乐观锁是数据库中的概念,为了解决某些问题。主要解决:丢失更新问题。

问题

数据库中,如果不考虑事务隔离性,会产生什么问题?

  • 读问题:
    • 脏读
    • 不可重复读
    • 幻读
  • 写问题
    • 丢失更新

丢失更新问题举例

image-20200901111219126

解决方案

  • 悲观锁
  • 乐观锁

悲观锁是只能有一个人进行操作。

乐观锁是通过版本号来进行控制,可能有多个人操作但有可能操作失败。

乐观锁举例

通过版本号,version字段来进行控制

image-20200901111433177

mp代码实现乐观锁

  1. 给数据库加个version字段

  2. 给实体类加个version属性,并带上@Version注解

  3. 写个配置类,加入@Configuration注解。之后在类中加入乐观锁插件(复制代码)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Configuration
    public class MpConfig {
    /**
    * 乐观锁插件
    */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
    }
    }
  4. 为了效果明显,先给数据库中的version一个默认的值,这里依旧通过上述的mp的自动填充fill方式实现。

    1. 加入@TableField(fill = FieldFill.INSERT)注解

    2. 去meta元数据的那里加入插入默认值

      1
      this.setFieldValByName("version", 1, metaObject);
  5. 测试

测试的时候必须“先查再改”,如果直接手动设置值的话version是不会改变的。

首先通过insertTest插入一个新的记录,可以看到其version为1.

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 测试修改
*/
@Test
public void testOptimisticLocker() {
//需要先查再改
User user = userMapper.selectById(1300975732545150977L);
user.setAge(22);

int row = userMapper.updateById(user);
System.out.println("row:" + row);
}

经过上述测试,发现修改年龄之后,记录的version变为2 。说明乐观锁生效。

8) 查询Select

1. 通过id来查询

1
User user = userMapper.selectById(1300975732545150977L);

2.通过多个id批量查询

1
2
3
4
5
6
7
8
9
10
11
/**
* 多个id批量查询
*/
@Test
public void testSelectBatchs() {
//传入一个集合
// 这里直接通过Arrays.asList()来直接构建一个集合
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));

System.out.println(users);
}

3.通过Map查询

1
2
3
4
5
6
7
8
9
10
11
/**
* 测试Map查询
*/
@Test
public void testSelectMap() {
HashMap<String,Object> map = new HashMap<>();
map.put("age",18);
List<User> users = userMapper.selectByMap(map);

users.forEach(System.out::println);
}

构建map的语句:HashMap<String,Object> map = new HashMap<>();

关于最后一句输出语句:

System.out::print :是方法引用

方法引用是当你想把一个方法当作一个“函数指针”传给别的方法用时有用的。

例如说,我有个ArrayList想把里面每个元素都打印出来,每个元素一行。
那么Java 8之前会这样写:

1
2
3
for (ElementType e : list) {
System.out.println(e);
}

从Java 8开始,使用ArrayList的新API加上lambda表达式,我们可以这样写:

1
list.forEach(e -> System.out.println(e));

而这里的lambda表达式的内容其实只不过就是把参数传给了println()方法,而没有做任何别的事情,所以可以进一步简写为:

1
list.forEach(System.out::println);

仅此而已。

重点:

  • System.out是一个PrintStream实例的引用;System.out::println 是对一个实例方法的引用
    • 该引用同时指定了对实例(System.out)的引用以及对方法(PrintStream::println)的引用
  • System.out::println 不是 System.out.println 的等价物;前者是一个方法引用表达式,而后者不能单独作为一个表达式,而必须在后面跟上由圆括号包围的参数列表来构成方法调用表达式。
  • System.out::println 可以看作 lambda表达式 e -> System.out.println(e) 的缩写形式。

所以说以后想要遍历list集合的时候,就直接写 ’list.forEach(System.out::println())’ 就行了

4.分页

与PageHelper写法相似。。。

  1. 配置分页插件,还是去Config配置类里面加上。(复制代码)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Configuration
    public class MpConfig {
    /**
    * 乐观锁插件
    */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
    }

    /**
    * 分页插件
    */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
    }
    }
  2. 写测试方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /**
    * 分页查询方法测试
    */
    @Test
    public void testPage() {
    //1.创建page对象
    // 参数:current当前页数,size每页多少条记录
    Page<User> page = new Page<>(1, 5);

    //2.进行查询,查询到的数据会全部封装在传过来的page对象里面
    //参数:page对象,wrapper查询条件(暂时写null)
    userMapper.selectPage(page, null);

    //3.输出测试
    System.out.println("当前页:" + page.getCurrent());
    System.out.println("Records每页数据list集合:");
    page.getRecords().forEach(System.out::println);
    System.out.println("Size每页记录数:" + page.getSize());
    System.out.println("Total记录总条数:" + page.getTotal());
    System.out.println("Pages总页数:" + page.getPages());
    System.out.println("是否有下一页? -:" + page.hasNext());
    System.out.println("是否有上一页? -:" + page.hasPrevious());
    }

    其输出结果如下:

    当前页:1
    Records每页数据list集合:
    User(id=1, name=Jone, age=22, email=test1@baomidou.com, createTime=null, updateTime=Wed Sep 02 09:56:47 CST 2020, version=null)
    User(id=2, name=Jack, age=20, email=test2@baomidou.com, createTime=null, updateTime=null, version=null)
    User(id=3, name=Tom, age=28, email=test3@baomidou.com, createTime=null, updateTime=null, version=null)
    User(id=4, name=Sandy, age=21, email=test4@baomidou.com, createTime=null, updateTime=null, version=null)
    User(id=5, name=Billie, age=24, email=test5@baomidou.com, createTime=null, updateTime=null, version=null)
    Size每页记录数:5
    Total记录总条数:10
    Pages总页数:2
    是否有下一页? -:true
    是否有上一页? -:false

控制台sql语句打印:SELECT id,name,age,email,create_time,update_time FROM user LIMIT 0,5 这里的这个Limit

这个是测试的SelectMapsPage,上面的那个是SelectPage,两个方法不一样。
image-20200902111447261

9)删除

0.删除类型

  • 物理删除

    物理删除,删除表中的实际数据。删了数据库里就没了。

  • 逻辑删除

    逻辑删除,软删除,只是查询不到。数据库中记录仍然存在。通过一个标志位字段deleted来实现。

首先在表中加个boolean字段:deleted

1
ALTER TABLE `user` ADD COLUMN `deleted` boolean

1.根据Id删除

deleteById,物理删除

1
2
3
4
5
6
/* 删除操作,物理删除*/
@Test
public void testDeleteByID(){
int result = userMapper.deleteById(1L);
System.out.println("影响的行数:"+result);
}

2.批量ID删除

deleteBatchIds

1
2
3
4
5
6
/* 批量删除操作,物理删除*/
@Test
public void testDeleteBatchs(){
int result = userMapper.deleteBatchIds(Arrays.asList(2L,3L,4L));
System.out.println("影响的行数:"+result);
}

3.Map条件删除

1
2
3
4
5
6
7
8
9
/* Map条件删除操作,物理删除*/
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Billie");

int result = userMapper.deleteByMap(map);
System.out.println("影响的行数:" + result);
}

4.逻辑删除

Mp自带了逻辑删除的功能。

  1. 在数据库表中增加一个deleted字段。这里通过数据库表,设置其默认值为0.

    1
    alter table user add `deleted` boolean default 0 null;
  2. 在实体类User中添加属性,并添加@TableLogic注解

    1
    2
    3
    4
    5
    /**
    * 逻辑删除字段
    */
    @TableLogic
    private Integer deleted;
  3. 去配置类中,增加逻辑删除插件

    1
    2
    3
    4
    5
    6
    7
    /**
    * 逻辑删除插件
    */
    @Bean
    public ISqlInjector sqlInjector() {
    return new LogicSqlInjector();
    }
  4. (可选)可以去配置文件application.properties中修改mp的配置:

    1
    2
    3
    4
    # mp逻辑删除字段
    # 被删除的是1,不被删除的是0。这是mp默认的,所以下面这两句不写也可以。
    mybatis-plus.global-config.db-config.logic-delete-value=1
    mybatis-plus.global-config.db-config.logic-not-delete-value=0

    如注释所说。这里可以随意修改这两个值。

  5. 然后直接去测试就可以了。用的还是原来的方法,不过从物理删除变成了逻辑删除。

    依然执行deleteById方法:

    1
    2
    3
    4
    5
    6
    /* 删除操作,配置了之后变为删除*/
    @Test
    public void testDeleteByID() {
    int result = userMapper.deleteById(1301357687774146562L);
    System.out.println("影响的行数:" + result);
    }

    然后查看控制台输出的语句:

    image-20200903112040757

    可以看到,这里执行的是Update语句而不是Delete语句。修改了deleted,也就是含有@TableLogic注解的字段。

    而且可以看到,数据库表中的数据没有被删掉而只是修改了标志位:

    image-20200903112245335

    同时,查询的时候也是一样,直接执行selectAll方法:
    image-20200903152310626

10) 性能分析

性能分析拦截器,用于输出每条 SQL 语句及其执行时间

SQL 性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题

1.配置插件

(1)参数说明

参数:maxTime: SQL 执行最大时长,超过自动停止运行,有助于发现问题。

参数:format: SQL是否格式化,默认false。

(2)在 MybatisPlusConfig 中配置

1
2
3
4
5
6
7
8
9
10
11
12
/**
* SQL 执行性能分析插件
* 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
*/
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//ms,超过此处设置的ms则sql不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}

(3)Spring Boot 中设置dev环境

1
2
#环境设置:dev、test、prod
spring.profiles.active=dev

可以针对各环境新建不同的配置文件application-dev.propertiesapplication-test.propertiesapplication-prod.properties

也可以自定义环境名称:如test1、test2

  • dev:开发环境
  • test:测试环境
  • prod:生产环境

2.测试运行效果

运行成功情况:其中显示运行时间49ms

image-20200903154718335

改小执行时间,执行失败的情况:这时候虽然会报错,但是仍然会向数据库中插入数据。

image-20200903155055509

Wrapper

这里是用Wrapper的子类,较常用的QueryWrapper,进行了几个方法的演示。

  1. 新建QueryWrapper对象
  2. 使用wrapper.xxx()方法,指定查询条件
  3. 传入wrapper对象,执行查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 测试Wrapper
*/
@Test
public void testWrapper() {

QueryWrapper<User> wrapper = new QueryWrapper<>();

//大于,大于等于,小于,小于等于
//gt,ge,lt,le
//wrapper.gt("age", 22);

//之间,between。包括两头,是等于。
//wrapper.between("age", 18, 23);

//等于,不等于
//eq,ne
//wrapper.eq("email","LLL@gmail.com");

//模糊查询
//like
wrapper.like("name", "不");

//拼接sql
//last
//wrapper.last("limit 1");

//正序排序,逆序排序
//orderByAsc,orderByDesc
//wrapper.orderByDesc("age");

//查询指定列,而不是select *
//select
wrapper.select("name", "age");

List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}

课堂笔记,全

这个是全的笔记,上面的只是列举了几个常用的、可能在项目中用到的方法。

一、wapper介绍

img

Wrapper : 条件构造抽象类,最顶端父类

AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件

​ QueryWrapper : Entity 对象封装操作类,不是用lambda语法

​ UpdateWrapper : Update 条件封装,用于Entity对象更新操作

AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。

​ LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper

​ LambdaUpdateWrapper : Lambda 更新封装Wrapper

1
2
3
4
5
6
7
@RunWith(SpringRunner.class)
@SpringBootTest
public class QueryWrapperTests {

@Autowired
private UserMapper userMapper;
}

二、AbstractWrapper

**
**注意:以下条件构造器的方法入参中的 column 均表示数据库字段

1、ge、gt、le、lt、isNull、isNotNull

1
2
3
4
5
6
7
8
9
10
@Test
public void testDelete() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}

SQL:UPDATE user SET deleted=1 WHERE deleted=0 AND name IS NULL AND age >= ? AND email IS NOT NULL

2、eq、ne

注意:seletOne返回的是一条实体记录,当出现多条时会报错

1
2
3
4
5
6
7
@Test
public void testSelectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}

SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ?

3、between、notBetween

包含大小边界

1
2
3
4
5
6
7
@Test
public void testSelectCount() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("age", 20, 30);
Integer count = userMapper.selectCount(queryWrapper);
System.out.println(count);
}

SELECT COUNT(1) FROM user WHERE deleted=0 AND age BETWEEN ? AND ?

4、allEq

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testSelectList() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("name", "Jack");
map.put("age", 20);
queryWrapper.allEq(map);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

SELECT id,name,age,email,create_time,update_time,deleted,version

FROM user WHERE deleted=0 AND name = ? AND id = ? AND age = ?

5、like、notLike、likeLeft、likeRight

selectMaps返回Map集合列表

1
2
3
4
5
6
7
8
9
@Test
public void testSelectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.notLike("name", "e")
.likeRight("email", "t");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
maps.forEach(System.out::println);
}

SELECT id,name,age,email,create_time,update_time,deleted,version

FROM user WHERE deleted=0 AND name NOT LIKE ? AND email LIKE ?

6、in、notIn、inSql、notinSql、exists、notExists

in、notIn:

1
notIn("age",{1,2,3})--->age not in (1,2,3)notIn("age", 1, 2, 3)--->age not in (1,2,3)

inSql、notinSql:可以实现子查询

  • 例: inSql("age", "1,2,3,4,5,6")—>age in (1,2,3,4,5,6)
  • 例: inSql("id", "select id from table where id < 3")—>id in (select id from table where id < 3)
1
2
3
4
5
6
7
8
@Test
public void testSelectObjs() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.in("id", 1, 2, 3);
queryWrapper.inSql("id", "select id from user where id < 3");
List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
objects.forEach(System.out::println);
}

SELECT id,name,age,email,create_time,update_time,deleted,version

FROM user WHERE deleted=0 AND id IN (select id from user where id < 3)

7、or、and

注意:这里使用的是 UpdateWrapper

不调用or则默认为使用 and

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testUpdate1() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or()
.between("age", 20, 30);
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}

UPDATE user SET name=?, age=?, update_time=? WHERE deleted=0 AND name LIKE ? OR age BETWEEN ? AND ?

8、嵌套or、嵌套and

这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testUpdate2() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or(i -> i.eq("name", "李白").ne("age", 20));
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}

UPDATE user SET name=?, age=?, update_time=?

WHERE deleted=0 AND name LIKE ?

OR ( name = ? AND age <> ? )

9、orderBy、orderByDesc、orderByAsc

1
2
3
4
5
6
7
@Test
public void testSelectListOrderBy() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

SELECT id,name,age,email,create_time,update_time,deleted,version

FROM user WHERE deleted=0 ORDER BY id DESC

10、last

直接拼接到 sql 的最后

注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

1
2
3
4
5
6
7
@Test
public void testSelectListLast() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.last("limit 1");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

SELECT id,name,age,email,create_time,update_time,deleted,version

FROM user WHERE deleted=0 limit 1

11、指定要查询的列

1
2
3
4
5
6
7
@Test
public void testSelectListColumn() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "name", "age");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

SELECT id,name,age FROM user WHERE deleted=0

12、set、setSql

最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testUpdateSet() {
//修改值
User user = new User();
user.setAge(99);
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.set("name", "老李头")//除了可以查询还可以使用set设置修改的字段
.setSql(" email = '123@qq.com'");//可以有子查询
int result = userMapper.update(user, userUpdateWrapper);
}

UPDATE user SET age=?, update_time=?, name=?, email = ‘123@qq.com’ WHERE deleted=0 AND name LIKE ?


文章作者: SongX64
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SongX64 !
  目录