专栏首页码农UP2U看完即可上手 MyBatis-Plus

看完即可上手 MyBatis-Plus

MyBatis 是当前 Java 项目中使用非常常用的持久层框架,而 MyBatis-Plus 是 MyBatis 非常好的伴侣。MyBatis-Plus 的官网是 https://mp.baomidou.com/ 。其官网对于 MyBatis-Plus 的简介如下:

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

在 MP 的官网给出了非常有意思的愿景,如下:

愿景我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。

官网还有一个非常有趣的配图,配图如下:

MP 的特性如下思维导图:(该思维导图通过 MP 的官网进行整理)

本文整理了一些 MP 中经常会用到的用法。

一、引入 MyBatis-Plus 的依赖

首先创建一个 SpringBoot 的项目,然后引入其依赖,依赖如下:

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

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

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

这里,引入了 MyBatis-Plus、MySQL 和 Lombok 几个依赖。MyBatis-Plus 是 MP 的依赖,mysql 是对 MySQL 数据库支持的依赖, Lombok 是用来简化开发的相关依赖。

前面提到 MP 是 MyBatis 的增强插件,我们通过 jar 包的依赖可以看出,具体如下:

二、快速开始

在 MP 的文档当中的 “快速开始” 下提供了测试用的 表结构 和 数据,代码如下:

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');

在 MP 的官网中,上面的表是建立在 H2 这个内存库中的,而我们这里使用的是 MySQL 数据库。

我们创建一个 User 表对应的实体类 User.java,代码如下:

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

接着,创建一个 User 实体类的对应的 Mapper,代码如下:

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

我本人喜欢使用 @Mapper 注解。

三、配置数据库连接

在 application.yml 中配置数据库的连接,配置如下:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf8
    username: root
    password:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

四、MP 的常用用法

上面就完成了准备的工作,接下来在测试类中,来介绍 MP 的常用用法

1. Select 的用法

在 MP 的官网中给出了关于 select 查询的相关方法列表,列表如下:

// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意:只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

这里先来介绍相对最简单的用法,selectList 方法的用法,代码如下:

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

通过上面的代码,就查询出了 user 表中的全部记录,查看 console 中的输出日志,代码如下:

==>  Preparing: SELECT id,name,age,email FROM user 
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==        Row: 1429818474686480386, ??, 20, test@qq.com
<==        Row: 1429819050690265090, 李斯, 20, test@qq.com
<==        Row: 1429824642158870530, 张三, 20, test@qq.com
<==        Row: 1429824869062291457, 周星星, 20, test@qq.com
<==        Row: 1430549049609277442, 周星星, 20, test@qq.com
<==        Row: 1431465861985271810, 王五, 20, test@qq.com
<==      Total: 10

在 select 方法中有一个 Wrapper 的条件构造器,它提供了各种复杂的查询方法,比如常用的有 eq、in、like、between 等。这里给出几个例子,首先是关于 eq 的使用方法,代码如下:

@Test
void testSelectEq()
{
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();

    userQueryWrapper.eq("name", "tom");

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

上面的代码是查询 name 字段为 tom 的记录。在来查看关于 like 的用法,代码如下:

@Test
void testSelectLike()
{
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.like("name", "t");
    List<User> users = userMapper.selectList(userQueryWrapper);
    users.forEach(System.out::println);
}

上面的代码是查询 name 字段中包含 t 字符的记录,其 SQL 语句如下:

SELECT id,name,age,email FROM user WHERE name LIKE '%t%'

MyBatis-Plus 为我们提供了 likeLeft 和 likeRight 的方法,表示其中的 % 在字符串的左侧或右侧。接着再来演示关于 between 的用法,代码如下:

@Test
void testSelectBetween()
{
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.between("age", 21, 24);
    List<User> users = userMapper.selectList(userQueryWrapper);
    users.forEach(System.out::println);
}

上面的代码是查询 age 字段在 21 到 24 范围之内的。接着再介绍关于 order 的用法,代码如下:

@Test
void testSelectOrder()
{
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.orderByDesc("id");
    List<User> users = userMapper.selectList(userQueryWrapper);
    users.forEach(System.out::println);
}

上面的代码是以 id 的倒序来进行查询显示,代码类似于 order by id desc 这样的形式。接着介绍一下如何使用 limit 来进行限制返回记录的条数,代码如下:

@Test
void testSelectLimit()
{
    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
    userQueryWrapper.last("limit 2");
    List<User> users = userMapper.selectList(userQueryWrapper);
    users.forEach(System.out::println);
}

在 MP 的 QueryWrapper 中并没有提供 limit 方法,而是使用 last 方法。

上面整理的是关于 selectList 和 QueryWrapper 构造器的内容,对于查询而言,这两种方法用的较多。在 select 中除了可以传入 QueryWrapper 外,还可以传入 Map,只是方法不再使用 selectList,而是改用 selectByMap。这里就不再进行演示。

在项目中,除了会用到条件查询外,更多的是使用 ID 进行查询,这里介绍两个关于查询 ID 的方法,分别是 selectById 和 selectBatchIds 两个方法。演示代码分别如下:

/**
 * 1个ID的批量查询
 */
@Test
void testSelectId()
{
    User user = userMapper.selectById(1L);

    System.out.println(user);
}

/**
 * 多个ID的批量查询
 */
@Test
void testSelectBatchIds()
{
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));

    System.out.println(users);

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

上面的代码使用 selectById 查询了 ID 为 1 的记录,使用 selectBatchIds 查询了 ID 为 1、2、3 的记录。

2. 分页查询

除了上面的查询外,通常我们在查询时会使用分页,分页功能是 MP 的一个插件功能,首先要配置插件,配置代码如下:

@Configuration
public class MpConfig {
    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

进行配置,就可以进行分页查询,代码如下:

/**
 * 分页查询
 */
@Test
void testSelectPage()
{
    Page<User> page = new Page<>(1, 3);
    Page<User> userPage = userMapper.selectPage(page, null);
    // 返回对象得到分页所有数据
    // 总页数
    long pages = userPage.getPages();
    // 当前页
    long current = userPage.getCurrent();
    // 查询数据集合
    List<User> records = userPage.getRecords();
    // 表中的总记录数
    long total = userPage.getTotal();
    // 是否有下一页
    boolean hasNext = userPage.hasNext();
    // 是否有上一页
    boolean hasPrevious = userPage.hasPrevious();

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

    System.out.println("总页数" + pages);
    System.out.println("当前页" + current);
    System.out.println("表中的总记录数" + total);
    System.out.println("是否有下一页" + hasNext);
    System.out.println("是否有上一页" + hasPrevious);
}

上面就是关于分页的代码,输出结果如下:

==>  Preparing: SELECT COUNT(*) FROM user
==> Parameters: 
<==    Columns: COUNT(*)
<==        Row: 5
<==      Total: 1
==>  Preparing: SELECT id,name,age,email FROM user LIMIT ?
==> Parameters: 3(Long)
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2f651f93]
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
总页数2
当前页1
表中的总记录数5
是否有下一页true
是否有上一页false

3. 插入记录

关于插入相关一共有三方面的内容,分别是插入数据、主键生成和自动填充。

先来进行一次简单的数据插入,代码如下:

/**
 * 添加
 */
@Test
void testInsert()
{
    User user = new User();
    user.setName("张三");
    user.setAge(20);
    user.setEmail("test@qq.com");

    int insert = userMapper.insert(user);

    System.out.println(insert);
}

通过 insert 方法就可以将创建的实体类插入,我们来看一下插入时的 SQL 语句,代码如下:

==>  Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
==> Parameters: 1431985765138706433(Long), 张三(String), 20(Integer), test@qq.com(String)
<==    Updates: 1

可以看到,插入时的 ID 非常的长,因为 MP 默认生成的 ID 使用了雪花算法。

这里,就引出了关于主键生成的内容。我们如何不使用雪花算法呢。我们的在建表的时候并没有给 id 设置 auto_increment,我们给 id 添加 auto_increment,然后修改实体类,在 id 属性上添加 @TableId(type = IdType.AUTO) 来改变生成 ID 的算法。修改上面的代码,如下:

/**
 * 添加
 */
@Test
void testInsert()
{
    User user = new User();
    user.setName("李斯");
    user.setAge(20);
    user.setEmail("test@qq.com");

    int insert = userMapper.insert(user);

    System.out.println(insert);
}

执行上面的代码,观察 SQL 的输出,代码如下:

==>  Preparing: INSERT INTO user ( name, age, email ) VALUES ( ?, ?, ? )
==> Parameters: 李斯(String), 20(Integer), test@qq.com(String)
<==    Updates: 1

可以看到上面的 SQL 语句中,在 insert 时并没有在显示的插入 id 了,打开数据库观察 id 的值,它是在上一条记录的 id 值上做了加 1 的操作。

最后一个点是在表设计时应该有三个必备的字段,分别是 id、create_time 和 update_time,这个可以查看阿里的《Java 开发手册》,具体如下:

【强制】表必备三字段:id, create_time, update_time。 说明:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。create_time, update_time 的类型均为 datetime 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。

那么,我们需要在 User 表中添加 create_time 和 update_time 两个字段,那么这两个字段可以在数据库中设置默认值,在做 insert 操作时给 create_time 和 update_time 赋值为当前时间,在做 update 操作时给 update_time 赋值为当前时间。

虽然这样的方式可以,但是这样的作法并不推荐,推荐的做法是使用程序去控制,而不是数据库去控制。但是在每次进行插入的时候,都要手动给 create_time 和 update_time 进行赋值又显麻烦。MP 为我们提供了自动填充的功能,正好为我们解决了该问题。

首先来修改 User 的实体类,修改后如下:

@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}

自动填充的需要创建一个实现了 MetaObjectHandler 接口的类,我们创建的类如下:

@Component
public class MyMpMetaObjectHandler 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);
    }
}

实现 MetaObjectHandler 需要具体实现其中的两个接口,分别是 insertFill 和 updateFill,它们分别是 插入时填充 和 修改时填充。我们再次修改我们的 testInsert 测试方法来进行测试,观察 SQL 输出如下:

==>  Preparing: INSERT INTO user ( name, age, email, create_time, update_time ) VALUES ( ?, ?, ?, ?, ? )
==> Parameters: 赵高(String), 20(Integer), test@qq.com(String), 2021-08-29 22:48:00.132(Timestamp), 2021-08-29 22:48:00.132(Timestamp)
<==    Updates: 1

在修改代码时我将 name 设置为了 赵高,其余的并没有进行修改,而在 SQL 的输出中可以看到 MP 为我们自动填充了 create_time 和 update_time 两个字段。

4. 更新记录

在更新记录时,也有两个知识点,第一个是使用 MP 的 updateById 方法来对记录进行更新,另外一个问题是通过 版本号(在 MP 中称为 乐观锁) 解决 ABA 的问题。

首先来使用 updateById 进行更新,我们将数据表中 id 为 1 的 age 设置为 59 岁,代码如下:

/**
 * 修改
 */
@Test
void testUpdate()
{
    User user = new User();

    user.setId(1L);
    user.setAge(59);

    int i = userMapper.updateById(user);

    // 影响行数
    System.out.println(i);
}

在数据表中进行查看,id 为 1 记录的 age 变成了 59。

在某些情况下可能存在多个人同时操作一条记录,也可能一个人对同一条数据进行两次修改请求,因为某些情况第一个请求失败,而第二个请求成功了,失败的第一个请求再次重试时将第二个请求的修改覆盖掉了。这样是就有问题了。通常情况下,可以增加一个版本号,每次更新时比对一下版本号,如果版本号一样则更新(更新相关数据的同时,将版本号也自动增加),如果版本号不一致则不更新。在 MP 中称为 乐观锁。

其实这点在阿里的《Java 开发手册》中也有提到,如下所示:

【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加 锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。

使用乐观锁需要在表中增加一个版本号,比如 version,类型为 int,然后 version 的默认值给一个 1,当然也可以使用 insertFill 自动填充为 version 赋值为 1。然后修改实体类,修改后的实体类代码如下:

@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    @Version
    @TableField(fill = FieldFill.INSERT)
    private Integer version;
}

然后增加乐观锁插件的配置,乐观锁的配置和分页的配置在同一位置,代码如下:

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

在修改记录前,需要先对记录进行查询,然后再进行修改,这点在阿里的《Java 开发手册》中也有提到,如下:

【强制】数据订正(特别是删除或修改记录操作)时,要先 select,避免出现误删除,确认无 误才能执行更新语句。

我们先来插入一条记录,让 version 有值之后进行测试。插入记录如下:

==>  Preparing: INSERT INTO user ( name, age, email, create_time, update_time, version ) VALUES ( ?, ?, ?, ?, ?, ? )
==> Parameters: 秦始皇(String), 20(Integer), test@qq.com(String), 2021-08-29 23:16:03.012(Timestamp), 2021-08-29 23:16:03.012(Timestamp), 1(Integer)
<==    Updates: 1

这里插入了一个 秦始皇,查看数据库中的数据,如下图:

这里,我们来模拟一下 version 的使用,我们修改 秦始皇 的年龄,代码如下:

/**
 * 测试乐观锁
 */
@Test
void testUpdateVersion()
{
    // 根据ID进行查询
    User user = userMapper.selectById(1431985765138706436L);

    User user1 = userMapper.selectById(1431985765138706436L);
    // 修改
    user.setAge(1000);

    user1.setAge(2000);

    // 不需要手动setVersion(user.getVersion() + 1);
    userMapper.updateById(user);

    userMapper.updateById(user1);
}

我们来看一下 SQL 的输出,代码如下:

==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1431985765138706436(Long)
<==    Columns: id, name, age, email, create_time, update_time, version
<==        Row: 1431985765138706436, 秦始皇, 20, test@qq.com, 2021-08-29 23:16:03, 2021-08-29 23:16:03, 1
<==      Total: 1

==>  Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1431985765138706436(Long)
<==    Columns: id, name, age, email, create_time, update_time, version
<==        Row: 1431985765138706436, 秦始皇, 20, test@qq.com, 2021-08-29 23:16:03, 2021-08-29 23:16:03, 1
<==      Total: 1

==>  Preparing: UPDATE user SET name=?, age=?, email=?, create_time=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: 秦始皇(String), 1000(Integer), test@qq.com(String), 2021-08-29 23:16:03.0(Timestamp), 2021-08-29 23:19:17.954(Timestamp), 2(Integer), 1431985765138706436(Long), 1(Integer)
<==    Updates: 1

==>  Preparing: UPDATE user SET name=?, age=?, email=?, create_time=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: 秦始皇(String), 2000(Integer), test@qq.com(String), 2021-08-29 23:16:03.0(Timestamp), 2021-08-29 23:19:17.983(Timestamp), 2(Integer), 1431985765138706436(Long), 1(Integer)
<==    Updates: 0

代码中查询了两次,第一次设置秦始皇的年龄为 1000,后来第二个人设置秦始皇的年龄为 2000,但是在第一次 updateById 后 version 的版本后被修改了,因此第二次 updateById 时发现版本号被改变了,那么就不再更新了。

5. 删除记录

删除操作通常分为物理删除和逻辑删除,物理删除表示记录被直接删掉了,而逻辑删除则表示记录被做了删除标记,实际还是存在的。

先来看看物理删除,代码如下:

/**
 * 物理删除
 */
@Test
void testDelete()
{
    int i = userMapper.deleteById(1431985765138706436L);

    System.out.println(i);
}

这里通过 deleteById 将指定 id 的记录进行了删除。来看一下 SQL 语句,代码如下:

==>  Preparing: DELETE FROM user WHERE id=?
==> Parameters: 1431985765138706436(Long)
<==    Updates: 1

接着,我们来进行逻辑删除,逻辑删除同样需要在数据表中增加一个标志位,比如 deleted,在 MP 中,默认 0 为未删除,1 为已删除。这里我们在添加字段时给一个默认值为 0,同样也可以使用 insertFill 自动填充时赋值为 0。

添加字段后,修改实体类,修改后的实体类如下:

@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    @Version
    @TableField(fill = FieldFill.INSERT)
    private Integer version;

    @TableLogic
    @TableField(fill = FieldFill.INSERT)
    private Integer deleted;
}

然后写逻辑删除的代码,代码如下:

/**
 * 逻辑删除
 */
@Test
void testDeleteLogic()
{
    int i = userMapper.deleteById(1431985765138706435L);
    
    System.out.println(i);
}

看起来和物理删除的代码相同,但是观察 SQL 语句的输出,代码如下:

==>  Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 1431985765138706435(Long)
<==    Updates: 1

可以看到,逻辑删除实际上是对 deleted 字段的 update 操作。然后,我们执行最上面的查询操作(即:selectList 方法),查看执行的 SQL 输出,代码如下:

==>  Preparing: SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE deleted=0

可以看到,在我们使用了逻辑删除后,调用 MP 的查询方法时,会默认的在 SQL 语句中加入 deleted = 0 的条件。

五、总结

本篇内容整理了 MyBatis-Plus 的常用用法,其中涉及到了 自动填充、分页、乐观锁 和 逻辑删除 的内容。期间还涉及到了阿里的《Java 开发手册》中的内容。想必这些内容也都是实际项目中经常用到的功能,希望可以对大家有所帮助。觉得有用就点个“在看”吧!!

本文分享自微信公众号 - 码农UP2U(gh_3c91b47a82e0),作者:码农UP2U

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-08-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 从零搭建Spring Boot脚手架(5):整合 Mybatis Plus

    在上一文中我根据Mybatis中Mapper的生命周期手动实现了一个简单的通用Mapper功能,但是遗憾的是它缺乏实际生产的检验。因此我选择更加成熟的一个Myb...

    码农小胖哥
  • 小议mybatis plus相比传统mybatis手写SQL的好处

    1、 发送通知需求:某一特定类别的商品,购买成功后需要发送通知给下单的客户,下单后默认通知状态为未发送,发送成功标记已发送。

    宜信技术学院
  • 【快学springboot】SpringBoot整合Mybatis Plus

    本文首发于头条号【Happyjava】。Happy的掘金地址:juejin.im/user/5cc289…,Happy的个人博客:blog.happyjava....

    Happyjava
  • 给你的MyBatis-Plus装上批量插入的翅膀

    大家有用过MyBatis-Plus(简称MP)的都知道它是一个MyBatis的增强工具,旨在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。

    慕容千语
  • 还在手写CRUD代码?这款开源框架助你解放双手!

    MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis...

    macrozheng
  • 太火了!MyBatis Plus 为啥这么牛?

    大家有用过MyBatis-Plus(简称MP)的都知道它是一个MyBatis的增强工具,致力于MyBatis的基础上只做增强不做改变,为简化开发,提高效率而生。

    肉眼品世界
  • 太火了!MyBatis Plus 为啥这么牛?

    大家有用过MyBatis-Plus(简称MP)的都知道它是一个 MyBatis 的增强工具,致力于 MyBatis 的基础上只做增强不做改变,为简化开发,提高效...

    业余草
  • Mybatis-Plus的应用场景及注入SQL原理分析

    传统mybaits需要三步:首先需要在订单表里加个字段,然后在订单的实体类添加这个属性,并且将所有dao层设计该状态的的查询sql都修改一遍,加上这个字段。

    2020labs小助手
  • 太火了!MyBatis Plus 为啥这么牛?

    大家有用过MyBatis-Plus(简称MP)的都知道它是一个MyBatis的增强工具,致力于MyBatis的基础上只做增强不做改变,为简化开发,提高效率而生。

    程序员白楠楠
  • MyBatis Plus 为啥这么牛?

    大家有用过MyBatis-Plus(简称MP)的都知道它是一个MyBatis的增强工具,旨在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生.

    搜云库技术团队
  • Mybatis-Plus学习.,简化你的开发,提升开发效率.

    李家酒馆酒保
  • 太火了!MyBatis Plus 为啥这么牛?

    大家有用过MyBatis-Plus(简称MP)的都知道它是一个 MyBatis 的增强工具,致力于 MyBatis 的基础上只做增强不做改变,为简化开发,提高效...

    架构师修炼
  • 太火了!MyBatis Plus 为啥这么牛?

    大家有用过MyBatis-Plus(简称MP)的都知道它是一个MyBatis的增强工具,致力于MyBatis的基础上只做增强不做改变,为简化开发,提高效率而生。

    程序IT圈
  • SpringBoot学习笔记(十七:MyBatis-Plus )

    MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

    三分恶
  • mybatis-plus如何禁用一级缓存

    用过mybatis-plus的朋友可能会知道,mybatis-plus提供了多租户插件的功能,这个功能可以让开发人员不用手动写租户语句,由该插件自动帮你加上租户...

    lyb-geek
  • 原创 | 从Spring Boot 2.x整合Mybatis-Plus深入理解Mybatis解析Mapper底层原理

    最近在使用高版本Spring Boot 2.x整合mybatis-plus 3.4.1时,控制台出现大量的warn提示XxxMapper重复定义信息:Bean ...

    猿芯
  • mybatis-plus的使用 ------ 入门

    mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQ...

    贪挽懒月
  • Java代码生成

    在很多开源的后台管理系统当中都有代码生成的工具,帮助开发者完成通用代码的生成,比如生成 Controller、Service、Dao 和 XML ...

    码农UP2U
  • mybatis-plus思维导图,让mybatis-plus不再难懂

     Mybatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果...

    java思维导图

扫码关注云+社区

领取腾讯云代金券