目录
概述
SpringBoot继承MyBatisPlus
CRUD
新增
删除
修改
查询
条件构造器
全局配置
相关注解
ActiveRecord
插件
分页插件
防止全表删除插件
乐观锁插件
乐观锁插件的使用
逻辑删除
使用逻辑删除
扩展
自动填充
Sql注入器
代码生成器Generator
代码生成器MyBatisX
部分图片来自百战程序员
概述
MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatisPlus的愿景是成为MyBatis最好的搭档,就像魂斗罗中的1P、2P,基友搭配,效率翻倍。
官方网址: MyBatis-Plus
SpringBoot继承MyBatisPlus
1、引入MyBatisPlus的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、在yml文件中创建数据源
# 数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql:///school?serverTimezone=UTC
username: root
password: root
3、创建pojo类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer id;
private String name;
private String email;
private String gender;
private Integer age;
}
4、创建mapper接口,继承BaseMapper,泛型为相关的pojo类
public interface StudentMapper extends BaseMapper<Student> {
}
5、在SpringBoot启动类中添加@MapperScan注解,扫描Mapper文件夹
@SpringBootApplication
@MapperScan("com.itbaizhan.mpdemo2.mapper")
public class SpringbootMpApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMpApplication.class, args);
}
}
6、测试Mapper方法
@SpringBootTest
public class StudentMapperTest {
@Autowired
private StudentMapper studentMapper;
@Test
public void testFindById(){
Student student = studentMapper.selectById(1);
System.out.println(student);
}
}
CRUD
在MyBatisPlus中只需要持久层接口继承BaseMapper,并且泛型为相关pojo类,那么MyBatisPlus将会自动帮你创建相关的增删改查方法
新增
insert方法作用为添加
- yml配置文件开启SQL日志打印
# 开启SQL日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 测试添加方法:
@Test public void testAdd(){ Student student = new Student(null,"baizhan","bz@bz.com","m",20); studentMapper.insert(student); // MyBatisPlus插入成功后,可直接直接获取主键的值 System.out.println(student.getId()); }
删除
deleteById方法的作用是根据ID删除数据
deleteBatchIds方法的作用是批量删除
deleteByMap方法的作用是根据字段条件删除,键为数据库列名
@Test
public void testDelete(){
studentMapper.deleteById(7);
}
@Test
public void testDeleteBatch(){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
studentMapper.deleteBatchIds(list);
}
@Test
public void testDeleteByMap(){
Map<String,Object> map = new HashMap<>();
map.put("address","北京");
studentMapper.deleteByMap(map);
}
删除前:
删除后:
修改
update方法作用为修改
updateById方法作用为根据Id修改
@Test
public void testUpdate(){
Student student = new Student(3,"张三","男","哈尔滨");
studentMapper.updateById(student);
}
修改前:
修改后:
查询
selectById方法的作用是根据Id查询
selectBatchIds方法的作用是根据id批量查询
selectByMap方法的作用是根据字段条件查询
数据库:
@Test
public void testSelectById(){
Student student = studentMapper.selectById(1);
System.out.println(student);
}
@Test
public void testSelectBatchIds(){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
List<Student> students = studentMapper.selectBatchIds(list);
students.forEach(System.out::println);
}
@Test
public void testSelectByMap(){
Map<String,Object> map = new HashMap<>();
map.put("sex","男");
List<Student> students = studentMapper.selectByMap(map);
students.forEach(System.out::println);
}
条件构造器
Mybatis-Plus通过QueryWrapper对象让用户自由的构建SQL条件,简单便捷,没有额外的负担,能够有效提高开发效率。
1、QueryWrapper对象构造了Sql条件过后,传递给selectList()方法即可查询
2、QueryWrapper构造的Sql条件之间默认为and连接
条件参数说明:
查询方式 | 说明 |
or | 或条件语句 |
and | 且条件语句 |
like | 模糊查询 like |
notLike | 模糊查询 not Like |
exists | exists 条件语句 |
notExists | not Exists 条件语句 |
isNull | null 值查询 |
isNotNull | is Not Null 查询 |
in | in 查询 |
notIn | not in 查询 |
groupBy | 分组查询 |
orderBy | 排序查询 |
having | 分组后筛选 |
eq | 等于 = |
ne | 不等于 <> |
between | between 条件语句 |
gt | 大于> |
ge | 大于等于>= |
lt | 小于< |
le | 小于等于<= |
@Test
public void testQueryWrapper(){
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.lt("id",5).gt("id",1);
List<Student> students = studentMapper.selectList(wrapper);
students.forEach(System.out::println);
}
全局配置
假如数据库的所有表都以tb_开头,主键都是自增的。如果针对每一个实体类都要添加相关注解比较麻烦,可以在SpringBoot配置文件中进行全局配置,该配置在所有的实体类中都生效。
mybatis-plus:
# 全局配置
global-config:
db-config:
#主键类型
id-type: AUTO
# 设置表名前缀
table-prefix: tb_
# 是否使用驼峰转下划线命名,默认开启
table-underline: true
相关注解
我们将表名改为tb_student,id字段名改为sid,name字段名改为sname,此时由于名字不同无法完成映射,我们可以通过相关注解为实体类对象指定表名和列名。
@TableName
作用:指定类为哪个表的实体类
位置:类上方
@TableId
作用:指定实体类的属性为主键
位置:属性上方
属性:
- value:主键字段名
- type:主键策略
值 | 描述 |
NONE | 无状态,不设置主键类型 |
AUTO | 自增主键 |
INPUT | 插入前自行设置主键值 |
ASSIGN_ID | 默认策略,使用雪花算法自动生成主键ID,主键类型为整形或字符串。(雪花算法:微博开源的分布式ID生成算法,使用一个64位的Long类型数字作为全局唯一ID。在分布式系统中的应用十分广泛,且ID引入了时间戳,基本上保持自增) |
ASSIGN_UUID | 自动生成排除中划线的UUID作为主键,主键类型为字符串。 |
@TableField
作用:在属性和列名不同的情况下,指定映射关系
位置:非主键属性上方
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_student")
public class Student{
@TableId(value="sid",type=IdType.AUTO)
private Integer id;
@TableField("sname")
private String name;
private String email;
private String gender;
private Integer age;
}
ActiveRecord
Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。
在MyBatisPlus中,AR模式即在实体类中封装了对数据库的访问,而不通过mapper类。
用法:
1、虽然操作数据库不通过mapper类,但需要编写mapper类并继承BaseMapper
public interface StudentMapper extends BaseMapper<Student>{
}
2、实体类继承Model类,开启AR模式
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student extends Model<Student> {
private Integer id;
private String name;
private String sex;
private String address;
}
3、接下来就可以测试了
@SpringBootTest
public class StudentTest {
@Test
public void testFindAll(){
Student student = new Student();
List<Student> students = student.selectAll();
students.forEach(System.out::println);
}
}
// AR新增
@Test
public void testAdd(){
Student student = new Student(10, "baizhan", "bz@bz.com", "m", 20);
student.insert();
}
// AR修改
@Test
public void testUpDate(){
// 创建实体类对象
Student student = new Student();
// 设置需要更新的属性
student.setName("baby");
// 设置需要修改的id
student.setId(10);
// 根据主键进行更新,没有设置的值忽略
student.updateById();
}
// AR根据id查询
@Test
public void testFindById(){
Student student = new Student();
student.setId(10);
Student student1 = student.selectById();
System.out.println(student1);
}
// AR删除
@Test
public void testDeleteById(){
Student student = new Student();
student.setId(10);
student.deleteById();
}
// AR查询所有
@Test
public void testFindAll(){
Student student = new Student();
List<Student> students = student.selectAll();
students.forEach(System.out::println);
}
插件
MyBatis插件机制
MyBatis插件就是对Executor、StatementHandler、ParameterHandler、ResultSetHandler这四个接口上的方法进行拦截,利用JDK动态代理机制,为这些接口的实现类创建代理对象,在执行方法时,先去执行代理对象的方法,从而执行自己编写的拦截逻辑。
- Executor
MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过 ResultSetHandler进行自动映射。 - StatementHandler
MyBatis直接让数据库执行sql脚本的对象。 - ParameterHandler
MyBatis实现Sql入参设置的对象。 - ResultSetHandler
MyBatis把ResultSet集合映射成POJO的接口对象。
MyBatisPlus常用插件
MyBatisPlus依据MyBatis插件机制,为我们提供了一些开发中常用的插件,我们在开发中使用即可。
常用插件:
- 自动分页: PaginationInnerInterceptor
- 防止全表更新与删除: BlockAttackInnerInterceptor
- 乐观锁: OptimisticLockerInnerInterceptor
这些插件都实现了InnerInterceptor接口
分页插件
在配置类或启动类配置分页插件:
// 注册插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
使用:
AR模式分页查询
@Test
public void testPage(){
//创建分页条件
Page page = new Page(0,2);
//查询构造器
QueryWrapper<Student> queryWrapper = new QueryWrapper<>();
queryWrapper.lt("id",10).gt("id",0);
Student student = new Student();
IPage<Student> iPage = student.selectPage(page,queryWrapper);
List<Student> records = iPage.getRecords();
records.forEach(System.out::println);
}
防止全表删除插件
作用:分析删除/更新语句,防止小白或者恶意进行删除/更新全表操作。
注意:
- 该插件只支持 MySQL5.6.3 以上版本
- 该插件只建议在开发环境中使用,不建议在生产环境使用
插件使用:
1、在配置类或启动类配置防止全表更新与删除插件
// 注册插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 防止全表更新与删除插件
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return interceptor;
}
2、测试全表删除
// AR删除所有
@Test
public void testDeleteAll() {
Student student = new Student();
//当条件构造器没有条件,代表删除所有数据
student.delete(new QueryWrapper());
}
乐观锁插件
修改数据库中的数据时,为了避免同时被其他人修改,最好的办法就是对该数据进行加锁以防止并发。
锁的设计分为悲观锁和乐观锁:
- 悲观锁:悲观锁对数据被外界修改持保守态度。即在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现往往依靠数据库提供的锁机制。
- 乐观锁:乐观锁在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。
MyBatisPlus乐观锁插件实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
乐观锁插件的使用
使用MyBatisPlus乐观锁插件:
1、在表中添加version列,且默认值为0
2、pojo类中添加version属性,并在属性上方添加@Version属性
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student extends Model<Student> {
private Integer id;
private String name;
private String sex;
private String address;
@Version
private int version;
}
测试修改功能
@Test
public void testVersion(){
Student student = new Student();
student.setId(1);
student.setName("索隆");
student.setVersion(10);
student.updateById();
}
通过输出的Sql语句可以发现,mybatisPlus在修改语句的后面添加了一个version条件,此时version为10,那么就根本不会修改数据库中路飞的名字
逻辑删除
在实际开发中,由于数据十分重要,为了避免误删除数据导致数据无法找回,我们往往不会使用物理删除,即从数据库中直接删除数据。而是采用逻辑删除的方式,即不会真正在数据库删除数据,而是通过一个变量代表它被删除。
deleted属性代表该数据是否删除,0代表未删除,1代表已删除。此时增删改查的Sql语句发生变化:
- 插入: 不作限制
- 查找: 追加where条件过滤掉已删除数据。
- 更新: 追加where条件防止更新到已删除数据。
- 删除: 转变为更新
例如:
- 删除: update user set deleted=1 where id = 1 and deleted=0
- 查找: select id,name,deleted from user where deleted=0
使用逻辑删除
- 在配置文件配置逻辑删除
# mybatis-plus相关配置 mybatis-plus: # 全局配置 global-config: db-config: # 全局逻辑删除的字段名 logic-delete-field: deleted # 逻辑已删除值(默认为 1) logic-delete-value: 1 # 逻辑未删除值(默认为 0) logic-not-delete-value: 0
- 修改实体类,添加逻辑删除属性
@Data @NoArgsConstructor @AllArgsConstructor public class Student extends Model<Student> { private Integer id; private String name; private String sex; private String address; @Version private int version; @TableLogic private int deleted; }
- 修改数据库表,添加一列整型deleted字段并设置默认值为0
- 测试删除和查询方法,会看到删除时将deleted字段变为1,查询时添加条件deleted=0
@Test
public void testDelete(){
studentMapper.deleteById(1);
}
扩展
自动填充
由于有了逻辑删除字段,那么向数据库插入数据时候,都需要设置deleted=0,而每次插入数据时都要设置该值十分繁琐,于是MyBatisPlus提供了自动填充功能。
- 为实体类的自动填充字段添加@TableField
@TableLogic
// 自动填充字段
@TableField(fill=FieldFill.INSERT)
private Integer deleted;
填充策略:
值 | 描述 |
DEFAULT | 默认不处理 |
INSERT | 插入操作填充字段 |
UPDATE | 更新操作填充字段 |
INSERT_UPDATE | 插入操作和更新操作均填充字段 |
- 自定义填充类实现MetaObjectHandler接口,填充类需要放到Spring容器中
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入时填充逻辑
* @param metaObject 元对象
*/
@Override
public void insertFill(MetaObject metaObject) {
/**
* 参数1:自动填充字段名
* 参数2:填充值
* 参数3:元对象
*/
this.setFieldValByName("deleted",0,metaObject);
}
/**
* 更新时填充逻辑
* @param metaObject 元对象
*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("deleted",0,metaObject);
}
}
测试
@Test
public void testFindAll(){
Student student = new Student();
List<Student> students = student.selectAll();
students.forEach(System.out::println);
}
Sql注入器
MyBatisPlus方法是有限的,我们可以使用SQL注入器自定义全局方法,注入到全局中,这样所有的Mapper类都能使用该方法,接下来我们自定义一个deleteAll方法。
1、创建注入方法类,继承AbstractMethod
2、创建Sql自动注入器,继承AbstractSqlInject,自动注入器需要放到Spring容器中
3、在mapper接口中定义deleteAll方法
4、测试deleteAll方法
public class DeleteAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
//定义Sql语句
String sql = "delete from "+ tableInfo.getTableName();
//定义方法名
String method = "deleteAll";
//构建SqlSource对象,他负责将Sql传递到数据库
SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration,sql,modelClass);
//构建删除方法
return this.addDeleteMappedStatement(mapperClass,method,sqlSource);
}
}
@Component
public class MySqlInject extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> list = new ArrayList<>();
list.add(new DeleteAll());
return list;
}
}
public interface StudentMapper extends BaseMapper<Student> {
void deleteAll();
}
@Test
public void testDeleteAll(){
studentMapper.deleteAll();
}
测试,查看数据库
代码生成器Generator
如果不想手动编写实体类等文件,MyBaitsPlus提供了代码生成器,它可以读取数据库的表信息,生成MyBaitsPlus代码供我们使用,之前我们学过MyBatis的代码生成器MyBatis Generator,这二者的区别是:
- MBG基于xml文件配置的,MyBaitsPlus代码生成器是基于Java代码配置的。
- MBG可生成实体类、Mapper接口、Mapper映射文件;
- MyBaitsPlus代码生成器可生成实体类、Mapper接口、Mapper映射文件、Service类、Controller类
使用:
1、添加代码生成器所需的依赖
<!-- MyBatisPlus代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!-- MyBatisPlus代码生成器需要的模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
2、编写代码生成器
public class MyGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql:///school", "root", "root")
// 全局配置
.globalConfig(builder -> {
builder.author("itbaizhan") // 设置作者
.commentDate("MM-dd") // 注释日期格式
.outputDir(System.getProperty("user.dir") + "/springboot_mybatisplus/src/main/java") // 指定输出目录(可能需要更改)
.fileOverride(); //覆盖文件
})
// 包配置
.packageConfig(builder -> {
builder.parent("com.itbaizhan.springboot_mp") // 包名前缀(可能需要更改)
.entity("domain") //实体类包名
.mapper("mapper") //mapper接口包名
.service("service") //service包名
.controller("controller") //controller包名
.xml("mapper"); //映射文件包名
})
// 策略配置
.strategyConfig(builder -> {
builder.addInclude("tb_student") // 设置需要生成的表名,可以有多个(可能需要更改)
.addTablePrefix("tb_") // 设置表名前缀
.entityBuilder() // 开始实体类配置
.enableLombok() // 开启lombok模型
.naming(NamingStrategy.underline_to_camel) //表名下划线转驼峰
.columnNaming(NamingStrategy.underline_to_camel);//列名下划线转驼峰
})
.execute();
}
}
运行即可生成
代码生成器MyBatisX
MybatisX是一款基于IDEA的快速开发插件,为效率而生。
安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Marketplace,输入 mybatisx 搜索并安装。
生成代码
MybatisX也可以生成Mybatis代码
- 在IDEA中连接数据库
- 如下操作可以根据数据库表生成Mybaits代码
生成映射配置
- 在Mapper接口中编写方法
public interface StudentMapper extends BaseMapper<Student>{
List<Student> selectAllBySname(String sname);
} - 如下操作即可在映射文件中自动生成映射配置
代码跳转
点击Mapper接口或映射文件前的小鸟图案,即可快速在Mapper接口与映射文件间跳转