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);
}
}
测试结果
使用 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。可以保证几乎全球唯一!
配置主键自增策略
在实体类的主键上加上
@TableId(type =IdType.AUTO)
-
将数据库中的主键字段设置为自增长
ALTER TABLE USER CHANGE id id BIGINT AUTO_INCREMENT
- 测试插入
其余字段详情见 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) 并且实现自动化
在工作中不允许我们修改数据库,此处仅为演示
方式一:使用数据库级别完成
- 在数据表中新增字段
create_time
,update_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 '修改时间';
-
在实体类中新增属性
private Date createTime; private Date updateTime;
-
执行更新操作
@Test void updateTest() { User user = User.builder() .id(4L) .name("UpdatedTime") .age(55) .build(); userMapper.updateById(user); }
-
更新查看结果
更新前
更新后
方式二:使用代码完成
-
将数据表的默认值和更新操作删除
ALTER TABLE USER CHANGE create_time create_time DATETIME NULL COMMENT '创建时间', CHANGE update_time update_time DATETIME NULL COMMENT '修改时间';
-
在实体类上增加注解
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
-
编写处理器处理注解
@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); } }
- 执行更新操作
乐观锁
乐观锁:顾名思义十分乐观,它总是认为不会出现问题,无论做什么都不上锁!如果出现了问题,再次更新值测试。
悲观锁:顾名思义十分悲观,它总是认为会出现问题,无论做什么都上锁!再去进行操作。
乐观锁实现方式:
- 取出记录时,获取当前的
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
乐观锁的使用
-
在数据库中新增
version
字段ALTER TABLE USER ADD VERSION INT DEFAULT 1 COMMENT '版本号';
-
在实体类中增加
version
字段@Version private Integer version;
-
注册组件
@MapperScan("com.hsh.study.mapper") @EnableTransactionManagement @Configuration public class MyBatisPlusConfig { /** * 注册乐观锁插件 * @return 实例 */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); } }
-
进行测试
测试方法
@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
字段
-
在配置文件
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
-
在实体类上添加
@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 中的自动填充,此处不做演示
方式二:使用代码完成
-
将数据表的默认值和更新操作删除
ALTER TABLE USER CHANGE create_time create_time DATETIME NULL COMMENT '创建时间', CHANGE update_time update_time DATETIME NULL COMMENT '修改时间';
-
在实体类上增加注解
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
-
编写处理器处理注解
@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); } }
-
编写
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>
- 执行更新操作
乐观锁
乐观锁:顾名思义十分乐观,它总是认为不会出现问题,无论做什么都不上锁!如果出现了问题,再次更新值测试。
悲观锁:顾名思义十分悲观,它总是认为会出现问题,无论做什么都上锁!再去进行操作。
乐观锁实现方式:
- 取出记录时,获取当前的
version
- 更新时,带上这个
version
- 执行更新时,
update xxx set version = newVersion where version = oldVersion
- 如果
version
不正确,则更新失败
乐观锁的使用
-
在数据库中新增
version
字段ALTER TABLE USER ADD VERSION INT DEFAULT 1 COMMENT '版本号';
-
在实体类中增加
version
字段@Version private Integer version;
-
注册组件,配置
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>
-
进行测试
测试方法
@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
字段
-
在配置文件
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>
-
在实体类上添加
@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
有两种不同的用法:
inSql("age","1,2,3,4,5")
->age IN (1,2,3,4,5)
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
|
升序排序 / 降序排序 |
其余详细字段参见官方文档 | 官方文档 | 其余详细字段参见官方文档 |