简介
直接看官网
快速入门
前期准备工作
- 创建一个数据库
mybatis_plus
- 创建
User
表,在表中插入数据
-- 创建数据库
CREATE DATABASE IF NOT EXISTS mybatis_plus
-- 创建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');
mybatis-plus快速入门
- 创建一个SpringBoot项目
mybatis_plus
- 导入依赖
<dependencies>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis-plus-->
<!--mybatis-plus和mybatis最好不要一起导入-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</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>
</dependency>
</dependencies>
- IDEA连接数据库
- 在
application.properties
配置数据库
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.data-username=root
spring.datasource.data-password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
- 创建pojo包,在包下创建实体类
user
,实现OR映射
package com.example.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
接口
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.pojo.User;
import org.springframework.stereotype.Repository;
//BaseMapper完成了所有基本的CRUD
@Repository // 持久层 注入到bean
public interface UserMapper extends BaseMapper<User> {
}
- 在主启动类加上
@MapperScan("com.example.mapper")
,用来扫描 mapper 文件夹 - 编写测试类测试
package com.example;
import com.example.mapper.UserMapper;
import com.example.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 MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
// 查询全部用户
// 参数是一个Wrapper,条件构造器,我们先填null,不用它
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
}
- 测试结果:
这里直接就查询出来,但是我们不知道它是怎么执行查询的,这时候我们就需要用到日志了
怎么配置日志呢?
很简单,直接在application.properties
文件配置一下日志就可以了。
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
我们这里选择最简单的控制台输出日志
注意:
如果我们使用log4J啊什么的,要记得导入他们的依赖!!!
这时候,我们在启动测试,查看一下控制台的输出
MyBatis-Plus---CRUD扩展
插入测试
- 直接编写测试类
@Test
void insertTest(){
User user = new User();
user.setName("小米");
user.setAge(23);
user.setEmail("test@qq.com");
int rs = userMapper.insert(user);
System.out.println(rs);//插入结果
System.out.println(user);//查看我们插入的用户
}
- 结果
--> 发现,我们没有给主键id传值,它自动帮我们传了一个值!这个值就是分布式数据库的全局唯一id
这就涉及到了主键生成策略
不同主键策略的测试
在实体类上通过@TableId()
注解来实现不同的主键策略的测试
三种废弃的
- 手动输入:
@TableId(type = IdType.INPUT)
没有插入id,报错
手动插入id,成功
- 自增:
@TableId(type = IdType.AUTO)
启动测试,你会发现,会出现报错
注意点:当你使用自增策略时,数据库的主键也要勾选自增,一定要勾选
再启动测试
- 雪花算法:
@TableId(type = IdType.ASSIGN_ID)
- 更多策略:@TableId()注解详解
查询操作
查询单个用户
@Test
void selectTest1(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
查询多个用户
@Test
void selectTest2(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
users.forEach(System.out::println);
}
条件查询--map 单条件:
@Test
void selectTest3(){
HashMap<String, Object> map = new HashMap<>();
// 条件查询
map.put("name","xiaohong");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
多条件:
@Test
void selectTest3(){
HashMap<String, Object> map = new HashMap<>();
// 条件查询
map.put("name","xiaohong");
map.put("age",18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页查询
- 配置注册插件
//注册分页插件
@Bean
public MybatisPlusInterceptor PaginationInnerInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
- 测试
// 分页查询
@Test
void LimitTest(){
/** 参数解析:
* 参数一:当前页
* 参数二:当前页大小
*/
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page,null);
//当前页信息
page.getRecords().forEach(System.out::println);
//查询到的总信息数量
System.out.println(page.getTotal());
}
更新测试
- 更新一个属性:name
- 更新两个属性:age+email
--> 观察两个sql,我们会惊喜的发现,mybatis-plus帮我们实现了动态SQL的拼接
自动填充
作用位置:数据库的表中数据的创建与更新的时间,手动帮我们完成
实现步骤
- 在数据库表中填加创建时间和更新时间两个属性
- 在实体类更新属性
createTime
和updateTime
private LocalDateTime createTime;
private LocalDateTime updateTime;
- 在实体类的
createTime
和updateTime
两个属性上添加注解
//插入时更新创建时间
@TableField(fill = FieldFill.INSERT)
private Date createTime;
//更新时更新,一开始的插入也算更新,所以用INSERT_UPDATE
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
其他属性:
- 编写处理器来处理这两个注解
package com.example.Handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyMetaobjectHandler 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);
}
}
- 进行插入测试和更新测试,注意时间的变化。 插入:
更新:
- 注意点:
- 使用
LocalDateTime.now()
,获得当前时间 - 数据库使用
timestamp
- 实体类映射为
LocalDateTime
时间类型数据库和实体类映射关系参考blog
乐观锁 OptimisticLockerInnerInterceptor
简介
- 乐观锁:故名思意十分乐观,它总是认为不会出现问题,无**论干什么不去上锁!**如果出现了问题,再次更新值测试
- 悲观锁:故名思意十分悲观,它总是认为总是出现问题,**无论干什么都会上锁!**再去操作! 悲观锁 VS 乐观锁
乐观锁实现方式:版本控制
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
举例说明
比如说,现在有一个线程A要更新id=1的用户
A:
update user set name="haha",version = version + 1 where id = 1 and version = 1;
可以正常更新。但是,如果这时候突然再来一个B线程,插队抢先完成
B:
update user set name="xixi",version = version + 1 where id = 1 and version = 1;
这时候,就会先完成B线程的更新操作,完成之后,version变为2,A线程就无法进行更新操作
操作测试
- 在数据库添加
version
字段
- 在对应数据库加上version属性,实现映射
@Version // 代表这是一个乐观锁
private Integer version;
- 注册配置乐观锁
//注册乐观锁
@Bean
public MybatisPlusInterceptor OptimisticLockerInnerInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
- 编写代码测试
- 只有A线程的情况下
@Test
void OptimisticLockerTest(){
//1. 查询用户信息
User user = userMapper.selectById(1L);
//2. 修改用户信息
user.setName("xiaolong");
//3. 执行更新操作
userMapper.updateById(user);
}
- A、B两个线程都在的情况
@Test
void OptimisticLockerTest2(){
//A线程
User user = userMapper.selectById(1L);
user.setName("xiaohu");
//B线程 插队,率先执行
User user2 = userMapper.selectById(1L);
user2.setName("xiaohong");
userMapper.updateById(user2);
userMapper.updateById(user);//如果没有乐观锁,就会覆盖掉B线程的值
}
删除操作
物理删除
物理删除:直接从数据库删除
原先数据库数据
删除单用户
@Test
void deleteTest1(){
userMapper.deleteById(1385936574033739778L);
}
删除多用户
@Test
void deleteTest2(){
userMapper.deleteBatchIds(Arrays.asList(1385936784428417025L,1385936784428417026L));
}
条件删除--map
@Test
void deleteTest3(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","小米");
map.put("age",25);
userMapper.deleteByMap(map);
}
逻辑删除
逻辑删除:在数据库中没有被删除,但是通过一个变量让本该被删除的数据失效
实现步骤
- 在数据库中加deleted字段,默认值为0
- 在实体类编写对应的属性
@TableLogic // 逻辑删除
private Integer deleted;
- 配置逻辑删除
# 配置逻辑删除
mybatis-plus.global-config.db-config.logic-not-delete-value=0
mybatis-plus.global-config.db-config.logic-delete-value=1
- 测试
- 我们先删除id=1的用户
- 我们再查询id=1的用户
逻辑删除成功~~~~
条件构造器Wrapper
相信在之前的代码编写中,大家对于Wrapper这个参数有很多疑问,Wrapper到底是干嘛的呢?
很简单,一句话,Wrapper就是用来编写复杂的SQL语句的。
直接上代码体验一下这几种常见的SQL复杂语句。
@Test
void contextLoads() {
// 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于20
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name").isNotNull("email")
.ge("age", 20);
userMapper.selectList(wrapper).forEach(System.out::println); // 和我们刚才学习 的map对比一下 }
}
@Test
void test2() {
// 查询名字是小米的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "小米");
User user = userMapper.selectOne(wrapper); // 查询一个数据,出现多个结果使用List 或者 Map
System.out.println(user);
}
@Test
void test3() {
// 查询年龄在 18 ~ 23 岁之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 18, 23); // 区间
Integer count = userMapper.selectCount(wrapper);// 查询结果数
System.out.println(count);
}
@Test
void test4() {
// 查询名字没有a,且名字中以T开头的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
// notLike 没有
// likeRight T%
//likeLeft %T
wrapper.notLike("name", "a")
.likeRight("name", "T");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
//测试六
@Test
void test6() {
// 通过id进行排序 wrapper.orderByAsc("id");
QueryWrapper<User> wrapper = new QueryWrapper<>();
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
关于wrapper的使用,还有很多,远远不止上面这几种,还有很多,详情可以看官网
代码自动生成器
简介
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
一句话说明,帮我们写代码的。
实现步骤
导入依赖
- 添加 代码生成器 依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
- 添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。这里选择它默认的就行
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
编写配置 自己在测试类中创建一个自动编写的类
package com.example;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
import java.util.ArrayList;
public class AutoToEncoding {
public static void main(String[] args) {
// 需要构建一个 代码自动生成器 对象
AutoGenerator mpg = new AutoGenerator();
// 配置策略
// 1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("哈哈");
gc.setOpen(false);
gc.setFileOverride(false); // 是否覆盖
gc.setServiceName("%sService"); // 去Service的I前缀
gc.setIdType(IdType.ID_WORKER);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//2、设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/autoEncoding?useSSL = false & useUnicode = true & characterEncoding = utf-8 & serverTimezone = UTC");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、包的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("blog");
pc.setParent("com.kuang");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
//4、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("表名"); // 设置要映射的表名,这里可以填多个
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true); // 自动lombok;
strategy.setLogicDeleteFieldName("deleted");
// 自动填充配置
TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified",
FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 乐观锁
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute(); //执行
}
}
主要修改设置strategy.setInclude("表名");
其他的都可以照抄