MyBatis-Plus 学习笔记

MyBatis Plus

本文档基于 MyBatis-Plus 官方文档编写,详情请参见 MyBatis-Plus 官网 ,通过 Spring 和 Spring Boot 两种方式分别进行了实现。

MyBatis Plus 概述

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上增强了功能而不做改变,为了简化开发、开发效率而生。

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

支持数据库

  • mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver
  • 达梦数据库 、 虚谷数据库 、 人大金仓数据库

入门程序

基本步骤

1、创建项目

2、导入依赖

3、编写相应配置文件

4、使用

数据库语句

数据库语句

CREATE DATABASE mybatisplus;

USE mybatisplus;

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

数据库 Data 脚本

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

使用 Spring 步骤

使用 Spring 步骤

1、创建 Maven 项目

2、 引入相应的依赖

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <spring.version>5.0.2.RELEASE</spring.version>
    <slf4j.version>1.6.6</slf4j.version>
    <log4j.version>1.2.12</log4j.version>
    <mysql.version>8.0.16</mysql.version>
    <mybatis.version>3.4.5</mybatis.version>
</properties>

<dependencies>
    
    <!-- spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- log start -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <!-- log end -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus</artifactId>
        <version>3.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.3.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>compile</scope>
    </dependency>

</dependencies>

3、编写配置文件 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 扫描的位置 -->
    <context:component-scan base-package="com.mybatis.study" />
    <!-- 导入 JDBC 配置文件 -->
    <context:property-placeholder location="jdbc.properties" />
    <!-- 配置连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!-- 配置 MyBatis-Plus 类 -->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 扫描 Mapper 所在的包 -->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.mybatis.study.mapper" />
    </bean>
    <!-- 配置事务 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

4、编写 pojo 类和 mapper 接口

pojo 类

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;

}

mapper 接口

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

5、 测试运行

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class UserTest {
    @Autowired
    private UserMapper userMapper;
    @Test
    public void test01(){
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

测试结果

1587958492400.png

使用 Spring Boot 步骤

使用 Spring Boot 步骤

1、创建 Spring Boot 项目

2、添加依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.16</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</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>
</dependencies>

3、编写配置文件 application.yml

# DataSource Config
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatisplus
    username: root
    password: 123

4、在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 mapper 文件夹

@MapperScan("com.mybatis.study.mapper")
@SpringBootApplication
public class MybatisPlusStudyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusStudyApplication.class, args);
    }
}

5、编写 pojo 类和 mapper 接口

pojo 类

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

mapper 接口

public interface UserMapper extends BaseMapper<User> {}

6、运行测试

测试类

@SpringBootTest
class MybatisPlusStudyApplicationTests {
    @Autowired
    private UserMapper userMapper;
    @Test
    void contextLoads() {
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

运行结果

配置 Spring 日志

导入 Log4J 日志的坐标

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>${log4j.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j.version}</version>
</dependency>

编写 log4j 的配置文件

log4j.rootLogger=DEBUG,Console

#Console  
log4j.appender.Console=org.apache.log4j.ConsoleAppender  
log4j.appender.console.Target=System.out
log4j.appender.Console.layout=org.apache.log4j.PatternLayout  
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n  

log4j.logger.org.apache=ERROR
log4j.logger.org.mybatis=ERROR
log4j.logger.org.springframework=ERROR
#这个需要
log4j.logger.log4jdbc.debug=ERROR
log4j.logger.com.gk.mapper=ERROR

log4j.logger.jdbc.audit=ERROR
log4j.logger.jdbc.resultset=ERROR
#这个打印SQL语句非常重要
log4j.logger.jdbc.sqlonly=DEBUG
log4j.logger.jdbc.sqltiming=ERROR
log4j.logger.jdbc.connection=FATAL

配置 Spring Boot 日志

在 Spring Boot 中配置日志(编写配置文件 application.yml

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

运行程序

MyBatis-Plus 常用注解

@TableName

  • 描述:表名注解
属性 类型 必须指定 默认值 描述
value String "" 表名
schema String "" schema
keepGlobalPrefix boolean false 是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)
resultMap String "" xml 中 resultMap 的 id
autoResultMap boolean false 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入)

@TableId

  • 描述:主键注解
属性 类型 必须指定 默认值 描述
value String "" 主键字段名
type Enum IdType.NONE 主键类型

IdType

描述
AUTO 数据库ID自增
NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT insert前自行set主键值
ASSIGN_ID 分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID 分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)
ID_WORKER 分布式全局唯一ID 长整型类型(please use ASSIGN_ID)
UUID 32位UUID字符串(please use ASSIGN_UUID)
ID_WORKER_STR 分布式全局唯一ID 字符串类型(please use ASSIGN_ID)

@TableField

  • 描述:字段注解(非主键)
属性 类型 必须指定 默认值 描述
value String "" 字段名
el String "" 映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分
exist boolean true 是否为数据库表字段
condition String "" 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s},参考
update String "" 字段 update set 部分注入, 例如:update="%s+1":表示更新时会set version=version+1(该属性优先级高于 el 属性)
insertStrategy Enum N DEFAULT 举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
updateStrategy Enum N DEFAULT 举例:IGNORED: update table_a set column=#{columnProperty}
whereStrategy Enum N DEFAULT 举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
fill Enum FieldFill.DEFAULT 字段自动填充策略
select boolean true 是否进行 select 查询
keepGlobalFormat boolean false 是否保持使用全局的 format 进行处理
jdbcType JdbcType JdbcType.UNDEFINED JDBC类型 (该默认值不代表会按照该值生效)
typeHandler Class<? extends TypeHandler> UnknownTypeHandler.class 类型处理器 (该默认值不代表会按照该值生效)
numericScale String "" 指定小数点后保留的位数

FieldStrategy

描述
IGNORED 忽略判断
NOT_NULL 非NULL判断
NOT_EMPTY 非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
DEFAULT 追随全局配置

FieldFill

描述
DEFAULT 默认不处理
INSERT 插入时填充字段
UPDATE 更新时填充字段
INSERT_UPDATE 插入和更新时填充字段

@Version

  • 描述:乐观锁注解、标记 @Verison 在字段上

@EnumValue

  • 描述:通枚举类注解(注解在枚举字段上)

@TableLogic

  • 描述:表字段逻辑处理注解(逻辑删除)
属性 类型 必须指定 默认值 描述
value String "" 逻辑未删除值
delval String "" 逻辑删除值

@SqlParser

  • 描述:租户注解,支持method上以及mapper接口上
属性 类型 必须指定 默认值 描述
filter boolean false true: 表示过滤SQL解析,即不会进入ISqlParser解析链,否则会进解析链并追加例如tenant_id等条件

@KeySequence

  • 描述:序列主键策略 oracle
  • 属性:value、resultMap
属性 类型 必须指定 默认值 描述
value String "" 序列名
clazz Class Long.class id的类型, 可以指定String.class,这样返回的Sequence值是字符串"1"

常见 CRUD 操作及扩展

Spring Boot 基本操作

前期准备步骤参见[使用 Spring Boot 步骤](#使用 Spring Boot 步骤)

插入操作

简单的插入操作

@Test
void insertTest(){
    User user = new User();
    user.setName("Mybatis");
    user.setAge(11);
    user.setEmail("123456@qq.com");
    int insert = userMapper.insert(user);
    System.out.println(insert);
}

运行结果

由于我们在插入操作时并没有指定 user 表的主键,但是查询数据库后发现 MyBatis-Plus 帮我们自动生成了主键。这是 MyBatis-Plus 的主键生成策略!

主键生成策略

默认 ID_WORKER 全局唯一 ID,在 MyBatis-Plus 3.3.0 版本后被 ASSIGN_ID 代替,详情见 MyBatis-Plus 常用注解-IdType

雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096 个ID),最后还有一个符号位,永远是0。可以保证几乎全球唯一!

配置主键自增策略

  1. 在实体类的主键上加上 @TableId(type =IdType.AUTO)

  2. 将数据库中的主键字段设置为自增长

    ALTER TABLE USER CHANGE id id BIGINT AUTO_INCREMENT 
    
  1. 测试插入

其余字段详情见 MyBatis-Plus 常用注解-IdType

更新操作

简单的更新操作

@Test
void updateTest() {
    User user = User.builder()
        .id(5L)
        .name("Updated")
        .age(99)
        .build();
    userMapper.updateById(user);
}
自动填充

阿里开发手册:所有的数据库表基本都需要配置上:gmt_create(create_time)、gmt_update(update_time) 并且实现自动化

在工作中不允许我们修改数据库,此处仅为演示

方式一:使用数据库级别完成

  1. 在数据表中新增字段 create_timeupdate_time
ALTER TABLE USER 
ADD create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间';
ALTER TABLE USER ADD update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间';
  1. 在实体类中新增属性

    private Date createTime;
    private Date updateTime;
    
  2. 执行更新操作

    @Test
    void updateTest() {
        User user = User.builder()
            .id(4L)
            .name("UpdatedTime")
            .age(55)
            .build();
        userMapper.updateById(user);
    }
    
  1. 更新查看结果

    更新前

更新后

方式二:使用代码完成

  1. 将数据表的默认值和更新操作删除

    ALTER TABLE USER
      CHANGE create_time create_time DATETIME NULL COMMENT '创建时间',
      CHANGE update_time update_time DATETIME NULL COMMENT '修改时间';
    
  2. 在实体类上增加注解

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
  3. 编写处理器处理注解

    @Slf4j
    @Component
    public class MyDateObjectHandler implements MetaObjectHandler {
        /**
         * 插入时的策略
         * @param metaObject 源数据对象
         */
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill......");
            this.setFieldValByName("createTime",new Date(),metaObject);
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    
        /**
         * 更新时的策略
         * @param metaObject 源数据对象
         */
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill......");
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    }
    
  1. 执行更新操作
乐观锁

乐观锁:顾名思义十分乐观,它总是认为不会出现问题,无论做什么都不上锁!如果出现了问题,再次更新值测试。

悲观锁:顾名思义十分悲观,它总是认为会出现问题,无论做什么都上锁!再去进行操作。

乐观锁实现方式:

  • 取出记录时,获取当前的 version
  • 更新时,带上这个 version
  • 执行更新时, update xxx set version = newVersion where version = oldVersion
  • 如果 version 不正确,则更新失败
乐观锁:1、先查询,获得版本号 version = 1
-- A
update user set name = "test", version = version + 1
where id = 2 and version = 1
-- B 线程抢先完成,这个时候version = 2,会导致A 修改失败!
update user set name = "test", version = version + 1
where id = 2 and version = 1

乐观锁的使用

  1. 在数据库中新增 version 字段

    ALTER TABLE USER ADD VERSION INT DEFAULT 1 COMMENT '版本号';
    
  1. 在实体类中增加 version 字段

    @Version
    private Integer version;
    
  2. 注册组件

    @MapperScan("com.hsh.study.mapper")
    @EnableTransactionManagement
    @Configuration
    public class MyBatisPlusConfig {
        /**
         * 注册乐观锁插件
         * @return 实例
         */
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor(){
            return new OptimisticLockerInterceptor();
        }
    }
    
  1. 进行测试

    测试方法

    @Test
    void optimisticLockerTest() {
        User user = userMapper.selectById(1L);
        user.setName("lockedTest");
        user.setEmail("110@test.com");
        userMapper.updateById(user);
    }
    

    测试结果

删除操作

删除操作常见的三种方式

/**
 * 根据 id 删除
 */
@Test
void deleteTest(){
    userMapper.deleteById(1255372915881406466L);
}
/**
 * 通过 Id 集合进行删除
 */
@Test
void deleteIdsTest(){
    userMapper.deleteBatchIds(Arrays.asList(1255373036727685122L,1255373036727685123L));
}
/**
 * 通过 Map 来删除
 */
@Test
void deleteMapTest(){
    Map<String, Object> map = new HashMap<>();
    map.put("name","张三");
    userMapper.deleteByMap(map);
}
逻辑删除

逻辑删除:通过一个标记表示此条数据已被删除,不会再数据库中直接删除数据!deleted = 0 -> deleted = 1

物理删除:从数据库中直接删除

1、修改数据库,添加 deleted 字段

alter table `user` add deleted int default 0 comment '逻辑删除';

2、在实体类中添加 deleted 字段

  1. 在配置文件 application.yml 中加入配置

    # 配置 MyBatis-Plus
    mybatis-plus:
      configuration:
        # 配置日志
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      # 配置逻辑删除
      global-config:
        db-config:
          logic-delete-value: 1
          logic-not-delete-value: 0
    
  2. 在实体类上添加 @TableLogic 注解

    @TableLogic
    private Integer deleted;
    

4、测试删除

查看数据库

查询操作

常见的查询操作

@Test
void test1(){
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}
@Test
void test2(){
    User user = userMapper.selectById(3L);
    System.out.println(user);
}
@Test
void test3(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 1255379859656523778L));
    // 查询不到 1255379859656523778L 是因为之前被逻辑删除了!
    users.forEach(System.out::println);
}
@Test
void test4(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("email","123456@qq.com");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}
分页查询

MyBatis-Plus 中内置了分页插件

1、配置分页插件

/**
 * 分页插件
 * @return 实例
 */
@Bean
public PaginationInterceptor paginationInterceptor(){
    return new PaginationInterceptor();
}

2、使用

@Test
void pageLimitTest(){
    /* new Page(current,size)
     * current:当前页
     * size:每页大小
     */
    Page<User> page = new Page<>(1,5);
    // 执行分页查询 自动回写到 page 对象中
    userMapper.selectPage(page, null);
    page.getRecords().forEach(System.out::println);
    System.out.println(page.getTotal());
}

Spring 基本操作

前期准备步骤参见[使用 Spring 步骤](#使用 Spring 步骤)

插入操作

简单的插入操作

@Test
public void insertTest(){
    User user = User.builder()
        .name("hhh")
        .age(112)
        .email("123@11.11")
        .build();
    userMapper.insert(user);
}

运行结果

更新操作

简单的更新操作

@Test
void updateTest() {
    User user = User.builder()
        .id(1257194050558160897L)
        .name("1122")
        .age(36)
        .build();
    userMapper.updateById(user);
}
自动填充策略

阿里开发手册:所有的数据库表基本都需要配置上:gmt_create(create_time)、gmt_update(update_time) 并且实现自动化

方式一:使用数据库级别完成 参考 Spring Boot 中的自动填充,此处不做演示

方式二:使用代码完成

  1. 将数据表的默认值和更新操作删除

    ALTER TABLE USER
      CHANGE create_time create_time DATETIME NULL COMMENT '创建时间',
      CHANGE update_time update_time DATETIME NULL COMMENT '修改时间';
    
  2. 在实体类上增加注解

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
  3. 编写处理器处理注解

    @Slf4j
    @Component
    public class MyDateObjectHandler implements MetaObjectHandler {
        /**
         * 插入时的策略
         * @param metaObject 源数据对象
         */
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill......");
            this.setFieldValByName("createTime",new Date(),metaObject);
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    
        /**
         * 更新时的策略
         * @param metaObject 源数据对象
         */
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill......");
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    }
    
  4. 编写 applicationContext.xml 配置文件

    <!-- 配置 MyBatis-Plus 的全局配置 -->
    <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
        <property name="metaObjectHandler">
            <!-- 编写自己的 MyMetaObjectHandler -->
            <bean class="com.mybatis.study.handler.MyMetaObjectHandler" />
        </property>
    </bean>
    <!-- 配置 MyBatis-Plus 类 -->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="globalConfig" ref="globalConfig" />
    </bean>
    
  1. 执行更新操作
乐观锁

乐观锁:顾名思义十分乐观,它总是认为不会出现问题,无论做什么都不上锁!如果出现了问题,再次更新值测试。

悲观锁:顾名思义十分悲观,它总是认为会出现问题,无论做什么都上锁!再去进行操作。

乐观锁实现方式:

  • 取出记录时,获取当前的 version
  • 更新时,带上这个 version
  • 执行更新时, update xxx set version = newVersion where version = oldVersion
  • 如果 version 不正确,则更新失败

乐观锁的使用

  1. 在数据库中新增 version 字段

    ALTER TABLE USER ADD VERSION INT DEFAULT 1 COMMENT '版本号';
    
  2. 在实体类中增加 version 字段

    @Version
    private Integer version;
    
  3. 注册组件,配置 applicationContext.xml 配置文件

    <!-- 配置 MyBatis-Plus 类 -->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <!-- 配置数据源 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 配置全局配置 -->
        <property name="globalConfig" ref="globalConfig" />
        <!-- 配置插件 -->
        <property name="plugins">
            <list>
                <!-- 注册乐观锁插件 -->
                <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor" />
            </list>
        </property>
    </bean>
    
  4. 进行测试

    测试方法

    @Test
    public void optimisticLockerTest() {
        User user = userMapper.selectById(1257194050558160897L);
        user.setName("lockedTest");
        user.setEmail("120@test.com");
        userMapper.updateById(user);
    }
    

    测试结果

删除操作

删除操作常见的三种方式

/**
 * 根据 id 删除
 */
@Test
void deleteTest(){
    userMapper.deleteById(1255372915881406466L);
}
/**
 * 通过 Id 集合进行删除
 */
@Test
void deleteIdsTest(){
    userMapper.deleteBatchIds(Arrays.asList(1255373036727685122L,1255373036727685123L));
}
/**
 * 通过 Map 来删除
 */
@Test
void deleteMapTest(){
    Map<String, Object> map = new HashMap<>();
    map.put("name","张三");
    userMapper.deleteByMap(map);
}
逻辑删除

逻辑删除:通过一个标记表示此条数据已被删除,不会再数据库中直接删除数据!deleted = 0 -> deleted = 1

物理删除:从数据库中直接删除

1、修改数据库,添加 deleted 字段

alter table `user` add deleted int default 0 comment '逻辑删除';

2、在实体类中添加 deleted 字段

  1. 在配置文件 applicationContext.xml 中加入配置

    <!-- Mybatis-Plus 全局配置 -->
    <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
        <!-- 自动填充 -->
        <property name="metaObjectHandler">
        <!-- 编写自己的 MyMetaObjectHandler -->
             <bean class="com.mybatis.study.handler.MyMetaObjectHandler" />
        </property>
        <!-- 逻辑删除 -->
        <property name="dbConfig" >
            <bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
                <property name="logicDeleteValue" value="1" />
                <property name="logicNotDeleteValue" value="0" />
            </bean>
        </property>
    </bean>
    <!-- 配置 MyBatis-Plus 类 -->
    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="globalConfig" ref="globalConfig" />
    </bean>
    
  2. 在实体类上添加 @TableLogic 注解

    @TableLogic
    private Integer deleted;
    

4、测试删除

查看数据库

查询操作

常见的查询操作

@Test
void test1(){
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}
@Test
void test2(){
    User user = userMapper.selectById(3L);
    System.out.println(user);
}
@Test
void test3(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 1255379859656523778L));
    // 查询不到 1255379859656523778L 是因为之前被逻辑删除了!
    users.forEach(System.out::println);
}
@Test
void test4(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("email","123456@qq.com");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}
分页查询

MyBatis-Plus 中内置了分页插件

1、配置分页插件

<!-- 配置 MyBatis-Plus 类 -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="globalConfig" ref="globalConfig" />
    <property name="plugins">
        <list>
            <!-- 配置分页插件 -->
            <bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor" />
        </list>
    </property>
</bean>

2、使用

@Test
void pageLimitTest(){
    /* new Page(current,size)
     * current:当前页
     * size:每页大小
     */
    Page<User> page = new Page<>(1,5);
    // 执行分页查询 自动回写到 page 对象中
    userMapper.selectPage(page, null);
    page.getRecords().forEach(System.out::println);
    System.out.println(page.getTotal());
}

扩展: Wrapper 的使用

当我们需要写一些复杂 SQL 语句时,可以使用 Wrapper 代替,所用 Wrapper 参数解释参见 条件构造器

下面列举常用 Wrapper 及其使用结果

allEq

allEq -> 全部相等或个别 isNull

@Test
void allEqTest(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    Map<String, Object> map = new HashMap<>();
    map.put("name","张三");
    map.put("age",null);
    wrapper.allEq(map);
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

eq

eq -> 相等(用于判断一个条件时)

@Test
void eqTest(){
    wrapper.eq("email","123456@qq.com");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

inSql

有两种不同的用法:

  1. inSql("age","1,2,3,4,5") -> age IN (1,2,3,4,5)

  2. inSql("id","select id from user where id < 5") -> id IN (SELECT id FROM user WHERE id < 5)

@Test
void inSqlTest1(){
    wrapper.inSql("age","10,11,20");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

@Test
void inSqlTest2(){
    wrapper.inSql("id","select id from user where id < 10");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

其余常见方法参见如下映射表

方法名 数据库名 作用
ne <> 不等于
gt > 大于
ge >= 大于等于
lt < 小于
le <= 小于等于
between BETWEEN 值1 AND 值2 在值 1 和值 2 之间
notBetween NOT BETWEEN 值1 AND 值2 不在值 1 和值 2 之间
like / notLike LIKE '%值%' / NOT LIKE '值' 模糊搜索
likeLeft / likeRight LIKE '%值' / LIKE '值%' 左右模糊匹配
isNull / isNotNull IS NULL / IS NOT NULL 是否为空
in / notIn IN (值1,2...) / NOT IN (值1,2...) 是否在集合中
inSql / notInSql 见例子 子查询或查询集合
groupBy GROUP BY 字段 根据字段分组
orderByAsc / orderByDesc ORDER BY 字段 ASC / ORDER BY 字段 DESC 升序排序 / 降序排序
其余详细字段参见官方文档 官方文档 其余详细字段参见官方文档
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345