目录
一.快速开始
1.创建数据库
2.插入数据
3.编写项目,导入依赖
4.配置数据库
5.pojo层:
6.mapper层:
7.编写启动类
8.编写测试类(和application相同路径创建):
8.测试结果:
二.配置日志
三.基础CRUD
1.插入测试
算法选用:
3.更新测试
4.自动填充
1.数据库级别(一般生产上不使用):
2.代码级别:
四.乐观锁插件
1.增加version字段
2.实体类添加字段
3.注册组件
4.测试
五.查询测试:
1.根据单个id查询:
2.根据多个ID查询:
3.条件查询:
4.分页查询:
1.配置拦截器:
2.编写测试类:
六.删除测试:
1.在数据库增加字段:
2.实体类中添加字段
3.配置:
4.测试删除:
代码生成器等用法在第二个
https://blog.csdn.net/m0_56289903/article/details/122039303?spm=1001.2014.3001.5501
最近在使用mybaties plus,本篇文章基于狂神的教程,地址如下:
【狂神说Java】MyBatisPlus最新完整教程通俗易懂_哔哩哔哩_bilibili笔记资料交流都在我们的平台:www.kuangstudy.com秦疆老师Java进阶系列课程之MyBatisPlus带你走进偷懒的大门;深入浅出的讲解了MyBatisPlus使用的全流程以及实战教学!狂神说Java系列,努力打造通俗易懂的教程QQ交流群 : 664386224https://www.bilibili.com/video/BV17E411N7KN?p=16 但是狂神的版本用的比较老,我就根据他的教程总结了一下新的版本的用法,以及解决了一些比较常见的坑。记录一下,方便查阅
通过Mybaties Plus官网可以快速开始
MyBatis-Plushttps://baomidou.com/
首先在数据库中创建一个mybaties_plus数据库:
导入SQL:
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)
);
//真实开发中:version(乐观锁),deleted(逻辑上次),gmt_create,gmt_modified
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
在pom中添加依赖:
mysql
mysql-connector-java
runtime
com.baomidou
mybatis-plus-boot-starter
3.4.3.4
org.projectlombok
lombok
1.18.22
provided
junit
junit
test
org.springframework.boot
spring-boot-starter-test
test
jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&characterEncodeing=utf-8&serverTimezone=GMT%2B8
jdbc:mysql://地址:端口号/数据库名?是否安全连接&unicode&字符集编码&时区配置(mysql8需要配置时区)
pojo层代表简单无规则java对象 纯的传统意义的java对象,最基本的Java Bean只有属性加上属性的get和set方法
可以额转化为PO、DTO、VO;比如POJO在传输过程中就是DTO
package space.pigeonessence.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName User.java
* @Description User pojo
* @createTime 2021年12月14日 09:38:00
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String Email;
}
mapper层我们直接继承了BaseMapper,所以不需要繁琐的XML文件了
package space.pigeonessence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import space.pigeonessence.pojo.User;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName UserMapper.java
* @Description Mybaties plus Mapper
* @createTime 2021年12月14日 09:40:00
*/
//持久层
@Repository
public interface UserMapper extends BaseMapper {
//CRUD编写完成
}
package space.pigeonessence;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("space.pigeonessence.mapper")//扫描mapper文件夹
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
package space.pigeonessence;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import space.pigeonessence.mapper.UserMapper;
import space.pigeonessence.pojo.User;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MybatiesApplicationTest.java
* @Description mybaties Unit test
* @createTime 2021年12月14日 09:43:00
*/
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class MybatiesApplicationTest {
@Autowired
private UserMapper userMapper;
@Test
public void contextLoads(){
List users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
注意:
此处有一个大坑,官方文档中没有@RunWith(SpringJUnit4ClassRunner.class)注释,但是在微服务项目中,普通的测试类是无法获取bean的,所以会报空指针。加上注释问题解决。
这样,我们的基本快速开始就完成了。
在bootstrap.yml中进行配置:
#标准控制台输出日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
再进行测试就可以在控制台看到相应的日志了
日志配置也可以用log4j之类的,按照需求进行配置。
了解基础之后,我们利用mybaties可以进行基础的CRUD处理,不用写mapper。
注意:3.0.7版本移除 对 mybatis-plus-generator 包的依赖,自己按需引入。我Mybaitos plus用的版本是3.4.3.4,所以需要引入模板依赖
org.apache.velocity
velocity-engine-core
2.3
@Test
public void testInsert(){
User user = new User();
user.setName("PigeonEssence");
user.setAge(21);
user.setEmail("[email protected]");
//直接调用insert函数,不用写mapper
int insert = userMapper.insert(user);
System.out.println(insert);//受影响的行数
System.out.println(user);
}
我们这里没有设定id,但是可以成功插入:
查看表:
可以看出,mybatiesplus为我们生成了一个id(全局唯一ID) 。
2.主键生成策略
id,一般对应数据库中的主键(primary key)
常用算法有:
1.uuid
优点:简单方便,性能好,全球唯一,有利于数据库迁移
缺点:没有排序,查询效率低(字符串),存储空间大,数据量大,不可读
2.自增id
优点:数据库自增列,有利于排序,在数据库的id选择自增,简单易用
缺点:可能出现单点故障,难以扩展,不好迁移,分库分表比较麻烦
3.雪花算法
开源ID生成算法,生成一个LONG类型的id
前面41bit作为毫秒数,10bit作为机器的ID(5bit是数据中心,5bit机器id),12bit作为毫 秒内的流水号,一个符号位永远是0,几乎可以保证全球唯一
4.redis
适合于生成流水号,订单号等,在集成的redis的开发环境中较为实用
优点:利用redis的单线程实现Id生成,不亦乐乎数据库,灵活方便性能好,数字ID天然排序,对分页有利
缺点:系统没有redis需要引入,增加复杂度,编码配置工作量大。
5.zookeeper
我们可以在pojo层中,规划构造器的时候选择id生成策略:
@TableId(type = IdType.)
private Long id;
我们再IdType中可以看到5类:
package com.baomidou.mybatisplus.annotation;
public enum IdType {
AUTO(0),
NONE(1),
INPUT(2),
ASSIGN_ID(3),
ASSIGN_UUID(4);
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
/*更新测试*/
@Test
public void testUpdate(){
User user = new User();
//通过条件自动拼接动态sql
user.setName("PigeonEssence");
user.setAge(22);
user.setEmail("[email protected]");
int update = userMapper.updateById(user);
System.out.println(update);
System.out.println(user);
}
虽然是update by id,但是传入的实际上是一个
所有的数据库表:gmt_create,gmt_modidied 几乎所有的表都要配置上,需要自动化
两种方式:
1.在表中新增字段create_time,update_time
设置默认值为: CURRENT_TIMESTAMP
2.实体类同步:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String Email;
private Date createTime;
private Date updateTime;
}
3.执行操作 :
在我们执行完更新以后,自动添加了时间戳
1.去掉数据库的默认值
2.在实体类上加上标签@TableField
我们可以看到在@TableField中,有一个方法;
FieldFill fill() default FieldFill.DEFAULT;
可以修改数据填充
package com.baomidou.mybatisplus.annotation;
public enum FieldFill {
DEFAULT,//不操作(默认)
INSERT,//插入时操作
UPDATE,//更新时操作
INSERT_UPDATE;//插入和更新的时候
private FieldFill() {
}
}
我们的crate_time是在插入的时候填充的,update_time是在更新的时候填充的,所以修改fill属性:
@TableField(fill = FieldFill.INSERT)//插入时操作
private Date createTime;//创建时间
@TableField(fill = FieldFill.INSERT_UPDATE)//插入和更新的时候操作
private Date updateTime;//更新时间
3.编写处理器处理
在handler文件创建TimeHandler。
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;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TimeHandler.java
* @Description handler for fill
* @createTime 2021年12月15日 13:46:00
*/
@Slf4j
@Component //处理器一定要加入IOC容器
public class TimeHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill success");
this.setFieldValByName("createTime",new Date(),metaObject); //设置字段的值
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill success");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
乐观锁:十分乐观,认为不会出现问题,无论干什么都不会上锁,出现问题载更新测试
相对的,悲观锁:总是悲观,认为会出现问题,无论什么先上锁再进行操作(影响性能)
主要用法是version(版本号),当我们进行修改的时候,version+1
在操作的时候,拿到版本号,version不对,更新失败
在mybaites中,集成了乐观锁插件:
@Version
private Integer version;//版本锁
添加一个配置类在config文件夹下(乐观锁插件 | MyBatis-Plus)
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName mbpConfig.java
* @Description version configuration
* @createTime 2021年12月16日 13:30:00
*/
@EnableTransactionManagement//自动管理事务
@MapperScan("space.pigeonessence.mapper")//扫描mapper文件夹
@Configuration//配置类
public class mbpConfig {
/**
* 新版乐观锁插件
*/
@Bean
//拦截器
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
//注册到插件
}
单线程测试:
@Test
public void testMybatisPlusInterceptor(){
//查询用户信息
User user = userMapper.selectById(1L);
//修改用户信息
user.setName("dashuaige");
//执行更新
userMapper.updateById(user);
}
我们可以看到,在查询的时候,mybaties plus先查询了version,再进行的update操作
模拟一下多线程插队的情况:
/*模拟了多线程插队操作*/
@Test
public void testMybatisPlusInterceptor2(){
//user1查询用户信息
User user = userMapper.selectById(1L);
//user1修改用户信息
user.setName("ChaoJiDaShuaiGe");
//模拟多线程:在user1 update操作之前,user2获取到了同一个对象,插队了
//user2查询用户信息
User user2 = userMapper.selectById(1L);
//user2修改用户信息
user2.setName("NiHaoChouA");
//user2先执行更新
userMapper.updateById(user2);
//user1再执行更新
userMapper.updateById(user);//没有乐观锁,就会覆盖插队线程的值
}
我们可以看到这个还是插队线程的值:
具体看着控制台分析一下,这一次的update进行了四次操作:
第一次:
user1查询出来的id为1的对象,找到了他的版本锁是2(之前的测试+了1)
第二次:
user2查询出来的id为1的对象,找到了他的版本锁是2(之前的测试+了1)
第三次:
user2进行修改参数并且update成功,并且把版本号(version)改成了3
第四次:
user1进行修改并且update操作,获取到的版本号3和之前的版本号2不对应,操作失败
我们可以看到这里的Updates是0.就是受影响的行数为0,更新失败。
/*单个ID查询测试*/
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
/*多个ID查询测试*/
@Test
public void testSelectByIds(){
//创建一个id的list
List idList = Arrays.asList(1, 2, 3);
//通过list查询对应的id
List users = userMapper.selectBatchIds(idList);
//遍历输出
users.forEach(System.out::println);
}
一般条件查询使用map进行封装
/*条件查询*/
@Test
public void testSelectByMap(){
//String 对应的是表中的字段名 , Obj,对应的要查的value
HashMap hashMap = new HashMap<>();
hashMap.put("name","Jack");
hashMap.put("age",21);
List users = userMapper.selectByMap(hashMap);
//遍历输出
users.forEach(System.out::println);
}
上面代码的意思就是我需要查找一个名字叫jack,年龄为21的user
数据库中的jack是20岁,所以查不到:
年龄改成20之后可以查出来:
我们通过mybatiesplus可以简单的进行多条件查询,自动拼接。
1.使用limit进行分页:
2.pagehelper等第三方插件:
3.Mybaties Plus自带分页插件
我们可以在mybaties的官网中找到最新的插件配置方法:
在2018-06-09之后的版本对插件更新了,我们可以直接在config中配置:
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName mbpConfig.java
* @Description Interceptor for Plug
* @createTime 2021年12月16日 13:30:00
*/
@EnableTransactionManagement//自动管理事务
@MapperScan("space.pigeonessence.mapper")//扫描mapper文件夹
@Configuration//配置类
public class mbpConfig {
@Bean
//拦截器
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//新版乐观锁插件
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
/*测试分页查询*/
@Test
public void selectByPage(){
IPage userIPage = new Page<>(1,5);
userMapper.selectPage(userIPage,null);
long pages = userIPage.getPages();
long current = userIPage.getCurrent();
long size = userIPage.getSize();
long total = userIPage.getTotal();
System.out.println("一共"+pages+"页, "+"一共"+total+"条数据, "+"当前第"+current+"页, "+"每页显示"+size+"条数据");
userIPage.getRecords().forEach(System.out::println);
}
在新版本中,引入了一个IPage的对象,用于代替之前的Page封装,但是使用方式基本不变。
IPage>后面的泛型就是你查询的pojo封装。和其他的分页插件没有本质的区别。
查询结果:
我们一般吧删除分为两种:
1.物理删除:直接把数据从数据库删除掉。
2.逻辑删除:数据实际没有被删除,通过变量来控制,可以恢复,防止数据丢失(回收站)。
主要说一说逻辑删除:
增加deleted字段,默认0
3.3.0之后配置了flag就不用配置@TableLogic
@TableLogic
private Integer deleted;//逻辑删除
在3.1.1后不需要Bean的配置了,2.0需要配置Bean。
在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)
/*测试单个删除*/
@Test
public void dele(){
userMapper.deleteById(1471409312647557121L);
}
我们可以看到,虽然执行的是dele操作,实际上是update。
记录没有被删掉,deleted变成了1
测试查询:
我们可以清楚的看到,sql语句拼接了AND deleted=0,所以修改了之后查不出来,过滤了逻辑删除的数据。
到这里,我们myBaties Plus的增删改查就完成了