文章目录
MyBatis-Plus
简介
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
SpringBoot整合MP
快速搭建MP
导入依赖
在springboot项目中如果导入了mybatisplus相关依赖,那么maven中mybatis就不会在生效了
<dependency>
<groupId>com.baomidou</groupId>
<!--下面坐标根据自己使用的SpringBoot版本二选一-->
<!--SpringBoot2使用此版本-->
<artifactId>mybatis-plus-boot-starter</artifactId>
<!--3.5.4开始,支持SpringBoot3使用此版本-->
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>最新版本</version>
</dependency>
创建数据连接
spring:
datasource:
druid:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/db19
username: root
password: 123456
编写Mapper文件
package com.ry.sofewin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ry.sofewin.entity.Student;
import org.apache.ibatis.annotations.Mapper;
public interface StudentMapper extends BaseMapper<Student> {
}
运行
@Autowired
StudentMapper studentMapper;
@Test
void getAllStudents(){
List<Student> students = studentMapper.selectList(null);
System.out.println(students);
}
CRUD操作
insert
@Test
void add() {
Student student = new Student();
student.setStuNo("10003");
student.setStuName("wangwu");
student.setGender(0);
student.setAddress("太原市娄烦县");
int insert = studentMapper.insert(student);
System.out.println(insert);
}
delete
@Test
public void del() {
根据id删除信息
int count = studentMapper.deleteById(1);
System.out.println(count);
//批量删除
ArrayList lists=new ArrayList();
lists.add(2);
lists.add(3);
int count = studentMapper.deleteBatchIds(lists);
System.out.println(count);*/
HashMap map=new HashMap();
map.put("gender","1");
map.put("address","娄烦");
int count = studentMapper.deleteByMap(map);
System.out.println(count);*/
}
update
@Test
void add() {
Student student = new Student();
student.setStuNo("10003");
student.setStuName("wangwu");
student.setGender(0);
student.setAddress("太原市娄烦县");
int insert = studentMapper.insert(student);
System.out.println(insert);
}
select
@Test
void selByWrapper(){
QueryWrapper<Student> wrapper=new QueryWrapper();
wrapper.like("address","小");
List<Student> students = studentMapper.selectList(wrapper);
System.out.println(students);
}
持久层接口 Service Interface
介绍
IService是MP提供的一个通用的Service层接口,它封装了常见的CRUD操作,包括插入、删除、查询、分页等。通过继承IService接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。
快速使用
service接口
public interface UserService extends IService<User> {
}
service实现类
@Service
public class UserServiceImpl extends ServiceImpl<BaseMapper<User>,User> implements UserService {
}
注:这里的BaseMapper可以换成UserMapper,因为你的mapper层的接口会继承BaseMapper
运行
@Test
public void testSave(){
//新增
User user = new User();
user.setName("赵六");
user.setAge(26);
user.setSex(1);
user.setPassword("123777");
user.setUserName("zhaoliu");
boolean result = userService.save(user);
System.out.println(result);
}
@Test
public void testSaveOrUpdate(){
//新增或修改(数据库表中没有这个便新增)
User user = new User();
user.setId(11L);
user.setName("赵六1");
user.setAge(26);
user.setSex(1);
user.setPassword("123777");
user.setUserName("zhaoliu1");
boolean result = userService.saveOrUpdate(user);
System.out.println(result);
}
@Test
public void testList(){
//查询所有
List<User> userList = userService.list();
userList.forEach(s -> System.out.println(s));
}
@Test
public void testGet(){
//查询单个
Optional<User> user = userService.getOptById(11L);
System.out.println(user);
}
@Test
public void testUpdate(){
//修改
User user = new User();
user.setId(11L);
user.setName("赵六2");
user.setAge(26);
user.setSex(1);
user.setPassword("123888");
user.setUserName("zhaoliu22");
boolean result = userService.updateById(user);
System.out.println(result);
}
@Test
public void testDelete(){
//根据id进行删除
boolean result = userService.removeBatchByIds(Arrays.asList(11, 1813457884667904001L));
System.out.println(result);
}
@Test
public void testUpdateBatch(){
//批量更新
List<User> userList = new ArrayList<>();
userList.add(new User(4L,"zhangwei1","123"));
userList.add(new User(5L,"zhangwei2","1231"));
boolean result = userService.updateBatchById(userList, userList.size());
System.out.println(result);
}
条件构造器
介绍
MP 提供了一套强大的条件构造器(Wrapper),用于构建复杂的数据库查询条件。Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率并减少 SQL 注入的风险。
在MP中,Wrapper 类是构建查询和更新条件的核心工具。
- AbstractWrapper:是一个抽象类,提供了所有Wrapper类共有的方法和熟悉。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。
- QueryWrapper:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以链式调用的方式添加多个查询条件,并且可以组合使用
and
和or
逻辑。 - UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
- LambdaQueryWrapper:这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下
- LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题。
使用
它可以和UserMapper或者UserService搭配一起使用,提高效率,常见的有eq、gt、ge、lt、le、between、like、isNull、in、insql、exists、apply
@Test
public void test1() {
QueryWrapper qw = new QueryWrapper();
qw.in("id", 1, 3, 4, 5, 6);
List list = userService.list(qw);
list.forEach(System.out::println);
}
@Test
public void test7() {
QueryWrapper qw = new QueryWrapper();
qw.inSql("sex", "select sex from tb_user where age < 100 and name = '张三'");
List list = userService.list(qw);
list.forEach(System.out::println);
}
@Test
public void test10() {
QueryWrapper qw = new QueryWrapper();
qw.eq("age", 21);
qw.eq("sex", 0);
qw.isNotNull("name");
qw.apply("date_format(dateColumn, '%Y-%m-%d') = {0}", "2008-08-08");
List list = userService.list(qw);
list.forEach(System.out::println);
}
使用配置
在 Spring Boot 项目中,可以通过 application.yml 或 application.properties 文件来配置 MyBatis-Plus。
mybatis-plus:
configuration:
# MyBatis 配置
map-underscore-to-camel-case: true
cache-enabled: false # 是否开启二级缓存
mapper-locations: classpath:/mapper/**.xml
type-aliases-package: com.your.domain
常用注解配置
@TableName
该注解用于指定实体类对应的数据库表名。当实体类名与数据库表名不一致,或者实体类名不是数据库表名的驼峰写法时,可以使用这个注解来明确指定表名。
@TableId
该注解用于标记实体类中的主键字段。如果你的主键字段名为 id,你可以省略这个注解
- value:来指定数据库表的主键字段名,如果主键字段名不为id,可以用value来指定
- type:指定主键的生成策略,有5种枚举类型
IdType.AUTO
:使用数据库自增 ID 作为主键。如果数据库表中主键没有设置自增,这种策略写上相当于0IdType.NONE
:无特定生成策略,如果全局配置中有 IdType 相关的配置,则会跟随全局配置。IdType.INPUT
:在插入数据前,由用户自行设置主键值。IdType.ASSIGN_ID
:自动分配ID
,适用于Long
、Integer
、String
类型的主键。默认使用雪花算法通过IdentifierGenerator
的nextId
实现。IdType.ASSIGN_UUID
:自动分配UUID
,适用于String
类型的主键。默认实现为IdentifierGenerator
的nextUUID
方法。
@TableField
该注解用于标记实体类中的非主键字段,它告诉MP如何映射实体类字段到数据库表字段。如果你的实体类字段名遵循驼峰命名规则,并且与数据库表字段名一致,你可以省略这个注解。
@Version
该注解用于标记实体类中的字段作为乐观锁版本号字段。
乐观锁是一种并发控制机制,它假设多个事务可以同时进行而不会互相干扰,只在提交事务时检查是否有冲突。通过在实体类中使用@Version
注解,MyBatis-Plus 会在更新操作时自动检查版本号,确保在更新过程中数据没有被其他事务修改。
@TableLogic
该注解用于标记实体类中的字段作为逻辑删除字段。逻辑删除是一种数据管理策略,它不是真正地从数据库中删除记录,而是在记录中标记该记录为已删除状态。通过使用@TableLogic
注解,MyBatis-Plus 可以在查询、更新和删除操作中自动处理逻辑删除字段的值。
@TableLogic(value = "0", delval = "1") // 逻辑删除字段
private Integer deleted;
deleted字段被标记为逻辑删除字段。@TableLogic注解的value属性指定了逻辑未删除的值(在这个例子中是0),而delval属性指定了逻辑删除的值(在这个例子中是1)。
- 当执行查询操作时,MyBatis-Plus 会自动过滤掉标记为逻辑删除的记录,只返回未删除的记录。
- 在执行更新操作时,如果更新操作会导致逻辑删除字段的值变为逻辑删除值,MyBatis-Plus 会自动将该记录标记为已删除。
- 在执行删除操作时,MyBatis-Plus 会自动将逻辑删除字段的值更新为逻辑删除值,而不是物理删除记录。
数据库主键不采用自增策略,mybatis和mybatisplus怎么来进行实现
//1.新增数据时候,查询最后一条新增记录的id是多少,然后+1
//2.mybatis自动生成
// <insert id="insertCategory" keyColumn="id" useGeneratedKeys="true">
// insert into category values(#{pid},#{name},#{remark},#{status})
// </insert>
//3.使用mybatisplus的自定义id生成器
//4.商业场景下的ID策略,redis序列号生成器,采用redis常用方法的incrment()方法
插件主体
分页插件
注:版本要求:3.4.0 版本以上,如果版本低于3.4,就不能使用MybatisPlusInterceptor,而是直接使用各自的类
3.4版本分页配置
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
return interceptor;
}
}
3.4版本以下配置
@Configuration
@MapperScan("com.ry.sofewin.mapper")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor(){
PaginationInterceptor paginationInterceptor=new PaginationInterceptor();
paginationInterceptor.setDbType(DbType.MYSQL);
return paginationInterceptor;
}
代码运行
@Test
public void testPage(){
Page<Book> page = new Page<>(1,10);
List<Book> bookList = bookService.list(page);
page.setRecords(bookList);
System.out.println(page.getTotal());
System.out.println(page.getRecords());
}
乐观锁插件
介绍:乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。
实现原理:
- 读取记录时,获取当前的版本号(version)。
- 在更新记录时,将这个版本号一同传递。
- 执行更新操作时,设置
version = newVersion
的条件为version = oldVersion
。 - 如果版本号不匹配,则更新失败。
配置:
@Configuration
@MapperScan("按需修改")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
在实体类上加上**@Version注解**
public class YourEntity {
@Version
private Integer version;
// 其他字段...
}
代码生成器
添加依赖
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!--mybatis-plus代码生成器插件-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!--代码生成器模板引擎依赖-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
代码书写
public class MybatisPlusGeneration {
/**
* 根据表名生成相应结构代码
* @param databaseName 数据库名
* @param tableName 表名
*/
public static void Generation(String databaseName,String userName,String password,String... tableName){
FastAutoGenerator.create("jdbc:mysql://localhost:3306/"+databaseName+"?&useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai",userName,password)
.globalConfig(builder -> {
builder.author("软赢科技")
//启用swagger
//.enableSwagger()
//指定输出目录
.outputDir(System.getProperty("user.dir")+"/src/main/java");
})
.packageConfig(builder -> {
builder.entity("entity")//实体类包名
.parent("com.ry.springbootmybatisplus2403")//父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名
.controller("controller")//控制层包名
.mapper("mapper")//mapper层包名
//.other("dto")//生成dto目录 可不用
.service("service")//service层包名
.serviceImpl("service.impl")//service实现类包名
//自定义mapper.xml文件输出目录
.pathInfo(Collections.singletonMap(OutputFile.mapperXml,System.getProperty("user.dir")+"/src/main/resources/mapper"));
})
.strategyConfig(builder -> {
//设置要生成的表名
builder.addInclude(tableName)
//.addTablePrefix("sys_")//设置表前缀过滤
.entityBuilder()
.enableLombok()
.enableChainModel()
.naming(NamingStrategy.underline_to_camel)//数据表映射实体命名策略:默认下划线转驼峰underline_to_camel
.columnNaming(NamingStrategy.underline_to_camel)//表字段映射实体属性命名规则:默认null,不指定按照naming执行
.idType(IdType.AUTO)//添加全局主键类型
.formatFileName("%s")//格式化实体名称,%s取消首字母I,
.mapperBuilder()
.enableMapperAnnotation()//开启mapper注解
.enableBaseResultMap()//启用xml文件中的BaseResultMap 生成
.enableBaseColumnList()//启用xml文件中的BaseColumnList
.formatMapperFileName("%sMapper")//格式化Dao类名称
.formatXmlFileName("%sMapper")//格式化xml文件名称
.serviceBuilder()
.formatServiceFileName("%sService")//格式化 service 接口文件名称
.formatServiceImplFileName("%sServiceImpl")//格式化 service 接口文件名称
.controllerBuilder()
.enableRestStyle();
})
// .injectionConfig(consumer -> {
// Map<String, String> customFile = new HashMap<>();
// // 配置DTO(需要的话)但是需要有能配置Dto的模板引擎,比如freemarker,但是这里我们用的VelocityEngine,因此不多作介绍
// customFile.put("DTO.java", "/templates/entityDTO.java.ftl");
// consumer.customFile(customFile);
// })
.execute();
}
public static void main(String[] args) {
System.out.println(System.getProperty("user.dir")+"/src/main/java");
Generation("erpdb","root","root123456","measure","member");
}
}
代码生成器加强版
使用mybatisx生成
需要自己生成实体类