Mybatis Plus | 快速入门
?wei_shuo的个人主页
?wei_shuo的学习社区
?Hello World !
Mybatis Plus
MyBatis-Plus(简称 MP)是一个基于 MyBatis 的增强工具,它对 Mybatis 的基础功能进行了增强,但未做任何改变。使得我们可以可以在 Mybatis 开发的项目上直接进行升级为 Mybatis-plus,正如它对自己的定位,它能够帮助我们进一步简化开发过程,提高开发效率;
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,自定义拦截规则,预防误操作支持数据库
环境准备
数据库准备
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’);创建springboot-mybatis-plus依赖导入
com.baomidou
mybatis-plus-boot-starter
3.0.5
mysql
mysql-connector-java
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-web
数据源、日志配置application.properties
数据库连接配置
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc//localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
初步使用
pojo/User.java类配置
package com.wei.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
mapper/UserMapper.java配置;继承mybatis-plus接口BaseMapper
package com.wei.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wei.pojo.User;
import org.springframework.stereotype.Repository;@Repository
public interface UserMapper extends BaseMapper{
}
application中配置注释扫描mapper路径
//扫描mapper文件夹
@MapperScan(“com.wei.mapper”)test测试
package com.wei;
import com.wei.mapper.UserMapper;
import com.wei.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;
@SpringBootTest
class SpringbootMybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
//查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
主键生成策略
主键生成策略:UUID、自增ID、雪花算法、Redis、zookeeper解释他们的区别和作用
- UUID(Universally Unique Identifier):UUID是一种算法,用于生成全局唯一的标识符。UUID是由一组随机数字和字母组成,长度为36个字符,通常以连字符分隔。UUID的优点是非常安全,几乎不可能重复,但是它的缺点是生成的标识符比较长,不太适合作为数据库主键
- 自增ID:自增ID是指在数据库中自动递增的整数,通常作为表的主键。自增ID的优点是简单易用,易于维护和管理,但是它的缺点是在分布式系统中可能会产生冲突,因为每个节点的自增ID都是独立的
- 雪花算法(Snowflake):雪花算法是Twitter开源的分布式ID生成算法。它使用一个64位的整数来表示唯一标识符,其中包括一个时间戳和一些节点信息。雪花算法的优点是生成的标识符比较短,可以在分布式系统中安全使用,但是它的缺点是需要维护节点信息,同时需要考虑时间戳的溢出问题
- Redis:Redis是一种基于内存的键值对存储系统。它可以用作分布式系统中的数据存储和缓存。在Redis中,可以使用自增命令INCR来生成唯一标识符。Redis的优点是非常快速和可靠,但是它的缺点是需要额外的系统维护和管理
- Zookeeper:Zookeeper是一个分布式的开源协调系统。它可以用于分布式系统中的数据存储和协调。在Zookeeper中,可以使用序列节点来生成唯一标识符。Zookeeper的优点是可以实现分布式系统中的数据同步和协调,但是它的缺点是需要额外的系统维护和管理
总的来说,不同的主键生成策略适用于不同的场景。如果需要生成全局唯一的标识符,可以使用UUID。如果需要简单易用的主键,可以使用自增ID。如果需要在分布式系统中生成唯一标识符,可以使用雪花算法、Redis或Zookeeper
默认方案,全局唯一ID
//默认方案,全局唯一ID
@TableId(type = IdType.ID_WORKER)
主键自增
public enum IdType {
AUTO(0), //数据库id自增
NONE(1), //未设置主键
INPUT(2), //手动输入
ID_WORKER(3), //默认全局id
UUID(4), //全局唯一id uuid
ID_WORKER_STR(5); //ID_WORKER字符串表示
}
对应id字段添加主键生成策略
package com.wei.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.INPUT)
private Long id;
private String name;
private Integer age;
private String email;
}
CURD
查询
@Test
void contextLoads() {
//查询全部用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
插入
@Test
public void testInsert(){
User user = new User();
user.setName("wei_shuo");
user.setAge(18);
user.setEmail("11096075493@qq.com");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println(user);
}
更新
@Test
public void testUpdate(){
User user = new User();
//通过条件自动拼接动态sql
user.setId(6L);
user.setAge(10);
user.setName("wei");
userMapper.updateById(user);
}
查询
//查询单个查询
@Test
public void TestSelectById() {
User user = userMapper.selectById(1L);
System.out.println("输出:" + user);
}
//查询多个查询
@Test
public void TestSelectByBatchIds(){
List<User> list = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
list.forEach(System.out::println);
}
//条件查询
@Test
public void TestSelectByBatchId(){
HashMap<String, Object> map = new HashMap<>();
//自定义查询
map.put("name","Tom");
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
分页查询
- limit分页
- pageHelper插件
MP插件
分页配置
package com.wei.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;@MapperScan(“con.wei.mapper”) //mapper文件夹扫描
@EnableTransactionManagement //默认事务管理
@Configuration //配置类注解
public class MybatisPlusConfig {
/**
自动分页: PaginationInnerInterceptor
多租户: TenantLineInnerInterceptor
动态表名: DynamicTableNameInnerInterceptor
乐观锁: OptimisticLockerInnerInterceptor
sql 性能规范: IllegalSQLInnerInterceptor
防止全表更新与删除: BlockAttackInnerInterceptor
**/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//乐观锁配置
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//分页配置
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
测试
//分页查询
@Test
public void TestPage(){
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
System.out.println(page.getTotal());
}
删除
//删除
@Test
public void TestDeleteById(){
userMapper.deleteById(1641610894939250691L);
}
//批量删除
@Test
public void TestDeleteBatchIds(){
userMapper.deleteBatchIds(Arrays.asList(1641610894939250690L,1641027507828424706L));
}
//条件删除
@Test
public void TestDeleteMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","THY222");
userMapper.deleteByMap(map);
}
逻辑删除
- 物理删除:数据库中直接移除
逻辑删除:数据库中没有直接移除,使用变量使其失效
数据库增加字段deleted默认值为0
实体类增添字段
@TableLogic //逻辑删除
private Integer deleted;application.properties配置逻辑删除
配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0执行删除
//逻辑删除
@Test
public void TestDeleteLogicById(){
userMapper.deleteById(6L);
}
查看数据库:数据库中没有直接移除,使用变量使其失效
自动填充
数据库级别
- 数据表中新增字段create_time、update_time
代码级别
实体类字段添加注解
//字段填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
编写处理器MyMetaObjectHandler.java
package com.wei.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.util.Date;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
//设置字段的值 setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
乐观锁 | 悲观锁
乐观锁假设多个线程之间不会产生冲突,因此在读取数据时不加锁,而只有在更新数据时才加锁。在更新数据时,先检查当前状态是否与预期一致,如果一致则进行更新,否则认为数据已被其他线程修改,需要回滚或重试
悲观锁则相反,它假设多个线程之间会产生冲突,因此在读取数据时就会加锁,防止其他线程同时修改数据。在更新数据时,悲观锁也会继续保持锁定状态,直到操作完成后才释放锁
总体来说,乐观锁更适合读多写少的场景,悲观锁则更适合写多读少的场景。但具体使用哪种锁机制还需要根据具体情况进行选择
乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
如果 version 不对,就更新失败
实体类注解
@Version //乐观锁注解
private Integer version;
注册组件:mybatis-plus 版本:3.5.3
package com.wei.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;@MapperScan(“con.wei.mapper”) //mapper文件夹扫描
@EnableTransactionManagement //默认事务管理
@Configuration //配置类注解
public class MybatisPlusConfig {
/**
自动分页: PaginationInnerInterceptor
多租户: TenantLineInnerInterceptor
动态表名: DynamicTableNameInnerInterceptor
乐观锁: OptimisticLockerInnerInterceptor
sql 性能规范: IllegalSQLInnerInterceptor
防止全表更新与删除: BlockAttackInnerInterceptor
**/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//乐观锁配置
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//分页配置
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
测试
//乐观锁测试成功
@Test
public void TestOptimisticLockerInterceptor() {
//查询用户信息
User user = userMapper.selectById(1L);
//修改用户信息
user.setName("THY");
user.setEmail("123456789@qq.com");
//执行更新操作
userMapper.updateById(user);
}
//乐观锁测试失败
@Test
public void FailOptimisticLockerInterceptor() {
//线程一
User user = userMapper.selectById(1L);
user.setName("THY");
user.setEmail("123456789@qq.com");
//线程二,插队更新user2
User user2 = userMapper.selectById(1L);
user2.setName("THY222");
user2.setEmail("123456789@qq.com");
userMapper.updateById(user2);
//自旋锁多次尝试提交
userMapper.updateById(user); //如果没有乐观锁就会覆盖插队线程的值
}
性能分析插件
依赖导入
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
spy.properties配置
3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
3.2.1以下使用或者不配置
modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
使用日志系统记录 sql
appender=com.p6spy.engine.spy.appender.Slf4JLogger
设置 p6spy driver 代理
deregisterdrivers=true
取消JDBC URL前缀
useprefix=true
配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
日期格式
dateformat=yyyy-MM-dd HH
ss
实际驱动可多个
driverlist=org.h2.Driver
是否开启慢SQL记录
outagedetection=true
慢SQL记录标准 2 秒
outagedetectioninterval=2
application.properties配置
设置开发环境
spring.profiles.active=dev
配置p6spy数据库连接
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbcmysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver效果展示
开发环境、测试环境、生产环境的作用和区别
开发环境、测试环境、生产环境之间的主要区别在于它们所涉及的目标和需求不同;开发环境注重于代码编写和调试,测试环境注重于软件质量和功能测试,而生产环境则注重于可靠性和性能优化。同时,这些环境中的设置和配置也可以不同,以满足其不同的目的
- 开发环境是用于软件开发和调试的环境,它通常包括代码编辑器、编译器、调试器以及其他必要的工具
- 测试环境是用于软件测试的环境,其中包含了模拟生产环境的硬件、软件和网络设置,可以对软件进行各种测试,以确保其质量和稳定性。
- 生产环境是用于部署和运行实际应用程序的环境。它是最终用户使用软件的环境,因此需要具有高可用性、可扩展性、安全性和性能等特征
代码生成器
依赖导入
com.baomidou
mybatis-plus-generator
最新版本 快速生成
FastAutoGenerator.create(“url”, “username”, “password”)
.globalConfig(builder -> {
builder.author("baomidou") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.baomidou.mybatisplus.samples.generator") // 设置父包名
.moduleName("system") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("t_simple") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
交互式生成
FastAutoGenerator.create(DATA_SOURCE_CONFIG)
// 全局配置
.globalConfig((scanner, builder) -> builder.author(scanner.apply("请输入作者名称?")).fileOverride())
// 包配置
.packageConfig((scanner, builder) -> builder.parent(scanner.apply("请输入包名?")))
// 策略配置
.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
.controllerBuilder().enableRestStyle().enableHyphenStyle()
.entityBuilder().enableLombok().addTableFills(
new Column("create_time", FieldFill.INSERT)
).build())
/*
模板引擎配置,默认 Velocity 可选模板引擎 Beetl 或 Freemarker
.templateEngine(new BeetlTemplateEngine())
.templateEngine(new FreemarkerTemplateEngine())
*/
.execute();
// 处理 all 情况
protected static List<String> getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}
? 结语:创作不易,如果觉得博主的文章赏心悦目,还请——
点赞
?收藏
⭐️评论
?
还没有评论,来说两句吧...