MyBatis-Plus
简介
MyBatis-Plus的官网上是这样介绍的: MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 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 操作智能分析阻断,也可自定义拦截规则,预防误操作。
1. 快速开始
地址:快速开始
步骤
1、创建数据库mybatis_plus
。
2、创建表
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)
);
添加数据
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');
3、编写项目,初始化项目!
搭建一个springboot项目,详情。
4、导入依赖
<!-- mybatis-plus 是自己开发,并非官方的! -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
说明:我们使用MP可以节省我们大量的代码,省时省力。另外尽量不要同时导入MP和MyBatis!会有版本差异。
5、连接数据库
application.properties文件
# 端口号
server.port=端口号
# 数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/数据库名?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
spring.datasource.username=******
spring.datasource.password=******
# 帮助MyBatis-Plus自动扫描并加载Mapper XML文件,从而实现自动映射的功能
mybatis-plus.mapper-locations=mapper/*.xml
application.yml
# 端口号
server:
port: 端口号
# 数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/数据库名?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
username: ******
password: ******
mybatis-plus:
#帮助MyBatis-Plus自动扫描并加载Mapper XML文件,从而实现自动映射的功能
mapper-locations: mapper/*.xml
两选其一,建议使用yml文件。
6、传统方式pojo > dao >service >controller,使用了MP后
- pojo
@TableName(value ="user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
/**
* 主键ID
*/
@TableId
private Long id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 邮箱
*/
private String email;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
- mapper接口
@Mapper
//@Repository//被该注解描述的事物属于持久层
public interface UserMapper extends BaseMapper<User> {
//继承BaseMapper后,所有的单表CRUD已经完成了,不需要和以前一样进行一大堆配置!
}
注意:如果你使用的不是@Mapper注入的话,需要在主启动类上添加@MapperScan("com.ly.mapper")
扫描我们的mapper问价夹。
- 使用
@SpringBootTest
class SpringBoot01ApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void selectList(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
}
}
2. 配置日志
application.properties文件
# 开启日志打印
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
application.yml文件
mybatis-plus:
# 开启日志打印
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
效果:
3. CRUD操作
3.1 intsert 插入
@Test
void insert(){
User user = new User();
user.setName("MrLiu");
user.setAge(21);
user.setEmail("*****@qq.com");
int i = userMapper.insert(user);
System.out.println("影响行数:" + i);
System.out.println(user);
}
结果:
数据库插入的id默认值为:全局的唯一id
主键生成策略
分布式系统唯一id生成策略 详情。
默认 IdType.ID_WORKER 全局唯一id
雪花算法(Snowflake Algorithm)是一种用于生成唯一ID的分布式算法。它由Twitter公司开发,被广泛用于Twitter的分布式系统中,用于为每个生成的Tweet和用户分配唯一的ID。它的设计思路是在分布式系统中生成全局唯一的ID,保证ID在多个服务器上同时生成时不会重复。它的核心思想是将时间戳与机器ID和序列号结合在一起生成唯一的ID。雪花算法生成的ID是一个64位的整数,可以同时生成多达4096个不同的ID。
type = IdType.AUTO 自增
条件:
- 实体类上加
@TableId(type = IdType.AUTO)
注解 - 数据库字段一定要是自增!
其余
3.2 update 更新
@Test
void update(){
User user = new User();
user.setId(6L);
user.setName("MrLiu");
user.setAge(22);
user.setEmail("*****@qq.com");
int i = userMapper.updateById(user);
System.out.println("影响行数:" + i);
System.out.println(user);
}
自动填充
创建时间/修改时间
这些操作都是自动化完成的,我们不希望手动更新!
阿里巴巴开发手册:所有的数据库表:gmt_create、gmt_modified几乎所有的表都要配置上!而且需要自动化!
1、方式一:数据库级别(工作中就别用了!你觉得你能该数据库的话除外 =.=)
① 在表中新增字段create_time、update_time
② 先把实体类字段同步,再使用insert方法进行测试
2、方式二:代码级别
① 删除上面添加的默认值、更新的操作!
② 在实体类字段属性上添加注解。
③ 自定义实现类 MyMetaObjectHandler
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("Insert start 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("update start fill...");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
④ 测试、观察、对比时间变化!
3.3 select 查询
void select(){
//根据id查询
User user = userMapper.selectById(1L);
System.out.println(user);
//根据多个id查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out :: println);
//条件查询之一 map
HashMap<String, Object> map = new HashMap<>();
//在此自定义要查询的条件
map.put("name", "Tom");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out :: println);
}
3.4 delect 删除
逻辑删除
物理删除:从数据库直接移除
物理删除:在数据库中没有被删除,但是通过一个变量来让它失效!deleted=0 -> deleted=1
比如:普通员工看不到已离职的人,但是管理员能看到。它类似于垃圾回收站,主要是用来防止数据丢失。
① 在数据库总添加deleted字段。
② 同步实体类中的字段。
/**
* 逻辑删除
*/
private int deleted;
③ 配置逻辑删除
在application.yml文件中配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
实体类字段上加上@TableLogic注解
/**
* 逻辑删除
*/
@TableLogic
private int deleted;
④ 运行以下根据id删除,查看日志
4. 分页查询
现在分页查询十分的常用,有些人用原始的limit进行分页,有些人使用pageHelper第三方插件,但是,MP也内置了分页插件呦!
如何使用?
1、配置拦截器组件
/* @Author : MrLiu
* @Date : 21:11 2023/6/20
* 插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
2、测试
/* @Author : MrLiu
* @Date : 20:59 2023/6/20
* 分页查询
*/
@Test
void selectPage(){
//参数一:当前页码 参数二:页面大小
Page<User> page = new Page<>(1, 5);
userMapper.selectPage(page, null);
page.getRecords().forEach(System.out :: println);
System.out.println("总数:" + page.getTotal());
page.hasPrevious();//是否有上一页
page.hasNext();//是否有下一页
page.getCurrent();//当前页码
page.getSize();//页面大小
page.getRecords();//获取所有记录
}
3、结果
5. 乐观锁
面试时,我们可能会被问到,乐观锁、悲观锁,这其实听容易的!
乐观锁(version):顾名思义它十分乐观,总认为不会出现问题,无论干什么都不加锁!如果出现了问题,则再次重新测试值
悲观锁:顾名思义十分悲观,总认为会出现问题,不管干嘛都会加锁,再去操作!
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
1、给数据库添加version字段。
2、将实体类字段与数据库同步。
3、注册组件
@EnableTransactionManagement//自动管理事务
@Configuration//代表这是一个配置类
@MapperScan("com.ly.mapper")//扫描我们的mapper问价夹
public class MyBatisPlusConfig {
//注册乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
4、测试
/* @Author : MrLiu
* @Date : 20:28 2023/6/20
* 测试乐观锁成功
*/
@Test
void innerInterceptor(){
//1、查询用户信息
User user = userMapper.selectById(1);
//2、修改用户信息
user.setName("流言蜚语");
//3、执行更新
userMapper.updateById(user);
}
/* @Author : MrLiu
* @Date : 20:28 2023/6/20
* 测试乐观锁失败
* 多线程情况下
*/
@Test
void innerInterceptor01(){
//线程1,更新了值当还没执行
User user1 = userMapper.selectById(1);
user1.setName("流言");
//线程2,突然插队
User user2 = userMapper.selectById(1);
user2.setName("蜚语");
userMapper.updateById(user2);
//更新结果为:蜚语; 如果没有乐观锁, 蜚语 就会被覆盖掉。
userMapper.updateById(user1);
}
}
6. 条件构造器
它十分重要,我们写一些复杂的sql,就可以使用它来替代!
声明
QueryWrapper<Object> wrapper = new QueryWrapper<>();
方法
建议去MP官网查看。
示例:
/* @Author : MrLiu
* @Date : 16:13 2023/6/20
* 查询:name不为空、邮箱不为空、年龄大于等20的
*/
@Test
void selectList(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.ge("age",20);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out :: println);
}
结果:
7. 代码生成器
导入依赖
<!--代码生成器依赖导入-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--模板引擎导入-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
在测试类中运行
@Test
void test(){
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
gc.setOutputDir( "项目路径/src/main/java");
gc.setAuthor("作者");
gc.setOpen(false);
gc.setSwagger2(true); //实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/数据库名?characterEncoding=utf8&serverTimezone=Asia/Shanghai");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("******");
dsc.setPassword("******");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("ly");
pc.setParent("com"); //com.ly
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
}
};
String templatePath = "/templates/mapper.xml.ftl";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return "项目路径" + "/src/main/resources/mapper/"
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
// \--转义字符 (\\window系统的路径 /linux系统中 但是现在可以混用)
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel); //user_name===userName
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// 写于父类中的公共字段
//strategy.setSuperEntityColumns("id");
//strategy.setControllerMappingHyphenStyle(true);
//strategy.setTablePrefix("tbl_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
具体情况可去MyBatisPlus官网查看文档,以做调整!