前面我们体验了JPA带来的快速开发体验,但是我们发现,面对一些复杂查询时,JPA似乎有点力不从心,反观稍微麻烦一点的Mybatis却能够手动编写SQL,使用起来更加灵活,那么有没有一种既能灵活掌控逻辑又能快速完成开发的持久层框架呢?
MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MybatisPlus的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
官方网站地址
MybatisPlus具有以下特性:
框架整体结构如下:
不过,光说还是不能体会到它带来的便捷性,我们接着就来上手体验一下。
跟之前一样,还是添加依赖:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.3.1version>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
dependency>
配置文件依然只需要配置数据源即可:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
然后依然是实体类,可以直接映射到数据库中的表:
@Data
@TableName("user") //对应的表名
public class User {
@TableId(type = IdType.AUTO) //对应的主键
int id;
@TableField("name") //对应的字段
String name;
@TableField("email")
String email;
@TableField("password")
String password;
}
接着,就可以编写一个Mapper来操作了:
@Mapper
public interface UserMapper extends BaseMapper<User> {
//使用方式与JPA极其相似,同样是继承一个基础的模版Mapper
//这个模版里面提供了预设的大量方法直接使用,跟JPA如出一辙
}
这里我们就来写一个简单测试用例:
@SpringBootTest
class DemoApplicationTests {
@Resource
UserMapper mapper;
@Test
void contextLoads() {
System.out.println(mapper.selectById(1)); //同样可以直接selectById,非常快速方便
}
}
可以看到这个Mapper提供的方法还是很丰富的:
最后查到的结果
对于一些复杂查询的情况,MybatisPlus支持我们自己构造QueryWrapper用于复杂条件查询:
@Test
void contextLoads() {
QueryWrapper<User> wrapper = new QueryWrapper<>(); //复杂查询可以使用QueryWrapper来完成
wrapper
.select("id", "name", "email", "password") //可以自定义选择哪些字段
.ge("id", 2) //选择判断id大于等于1的所有数据
.orderByDesc("id"); //根据id字段进行降序排序
System.out.println(mapper.selectList(wrapper)); //Mapper同样支持使用QueryWrapper进行查询
}
通过使用上面的QueryWrapper对象进行查询,也就等价于下面的SQL语句:
select id,name,email,password from user where id >= 2 order by id desc
在配置中开启SQL日志打印:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
最后得到的结果如下:
有些时候我们遇到需要批处理的情况,也可以直接使用批处理操作:
@Test
void contextLoads() {
//支持批处理操作,我们可以一次性删除多个指定ID的用户
int count = mapper.deleteBatchIds(List.of(1, 3));
System.out.println(count);
}
也可以快速进行分页查询操作,不过在执行前我们需要先配置一下:
@Configuration
public class MybatisConfiguration {
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页拦截器到MybatisPlusInterceptor中
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
这样我们就可以愉快地使用分页功能了:
@Test
void contextLoads() {
//这里我们将用户表分2页,并获取第一页的数据
Page<User> page = mapper.selectPage(Page.of(1, 2), Wrappers.emptyWrapper());
System.out.println(page.getRecords()); //获取分页之后的数据
}
对于数据更新操作,我们也可以使用UpdateWrapper非常方便的来完成:
@Test
void contextLoads() {
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper
.set("username", "hHHH")
.eq("id",4);
System.out.println(mapper.update(null, wrapper));
}
这样就可以快速完成更新操作了:
QueryWrapper和UpdateWrapper还有专门支持Java 8新增的Lambda表达式的特殊实现,可以直接以函数式的形式进行编写,使用方法是一样的:
@Test
void contextLoads() {
LambdaQueryWrapper<User> wrapper = Wrappers
.<User>lambdaQuery()
.eq(User::getId, 2) //比如我们需要选择id为2的用户,前面传入方法引用,后面比的值
.select(User::getName, User::getId); //比如我们只需要选择name和id,那就传入对应的get方法引用
System.out.println(mapper.selectOne(wrapper));
}
虽然使用MybatisPlus提供的BaseMapper已经很方便了,但是我们的业务中,实际上很多时候也是一样的工作,都是去简单调用底层的Mapper做一个很简单的事情,MybatisPlus为我们提供了很方便的CRUD接口,直接实现了各种业务中会用到的增删改查操作。
我们只需要继承即可:
@Service
public interface UserService extends IService<User> {
//除了继承模版,我们也可以把它当成普通Service添加自己需要的方法
}
接着需要编写一个实现类,这个实现类就是UserService的实现:
@Service //需要继承ServiceImpl才能实现那些默认的CRUD方法
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
使用起来也很方便,整合了超多方法:
比如想批量插入一组用户数据到数据库中:
@Test
void contextLoads() {
List<User> users = List.of(new User("xxx"), new User("yyy"));
//预设方法中已经支持批量保存了,这相比我们直接用for效率高不少
service.saveBatch(users);
}
还有更加方便快捷的保存或更新操作,当数据不存在时(通过主键ID判断)则插入新数据,否则就更新数据:
@Test
void contextLoads() {
service.saveOrUpdate(new User("aaa"));
}
也可以直接使用Service来进行链式查询,写法非常舒服:
@Test
void contextLoads() {
User one = service.query().eq("id", 1).one();
System.out.println(one);
}
首先需要先把整个项目的数据库给创建好,创建好之后,我们继续下一步,这里我们从头开始创建一个项目,感受一下它的强大,首先创建一个普通的SpringBoot项目:
导入一会需要用到的依赖:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.5.3.1version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-generatorartifactId>
<version>3.5.3.1version>
dependency>
<dependency>
<groupId>org.apache.velocitygroupId>
<artifactId>velocity-engine-coreartifactId>
<version>2.3version>
dependency>
配置一下数据源:
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
开始编写自动生成脚本了,这里依然选择测试类,用到FastAutoGenerator
作为生成器:
@Resource
DataSource dataSource;
@Test
void contextLoads() {
FastAutoGenerator
//首先使用create来配置数据库链接信息
.create(new DataSourceConfig.Builder(dataSource))
.execute();
}
接着配置一下全局设置,这些会影响一会生成的代码:
@Test
void contextLoads() {
FastAutoGenerator
.create(new DataSourceConfig.Builder(dataSource))
.globalConfig(builder -> {
builder.author("rx"); //作者信息,一会会变成注释
builder.commentDate("2023-09-06"); //日期信息,一会会变成注释
builder.outputDir("src/main/java"); //输出目录设置为当前项目的目录
})
.execute();
}
然后是打包设置,也就是项目的生成的包等等,这里简单配置一下:
@Test
void contextLoads() {
FastAutoGenerator
...
//打包设置,这里设置一下包名就行,注意跟我们项目包名设置为一致的
.packageConfig(builder -> builder.parent("com.example"))
.strategyConfig(builder -> {
//设置为所有Mapper添加@Mapper注解
builder
.mapperBuilder()
.mapperAnnotation(Mapper.class)
.build();
})
.execute();
}
接着我们就可以直接执行了这个脚本了:
现在,可以看到我们的项目中已经出现自动生成代码了:
我们也可以直接运行这个项目:
速度可以说是非常之快,一个项目模版就搭建完成了,我们只需要接着写业务就可以了,当然如果各位小伙伴需要更多定制化的话,可以在官网查看其他的配置:配置
对于一些有特殊要求的用户来说,我们希望能够以自己的模版来进行生产,怎么才能修改它自动生成的代码模版呢,我们可以直接找到mybatis-plus-generator
的源码:
生成模版都在在这个里面有写,我们要做的就是去修改这些模版,变成我们自己希望的样子,由于默认的模版解析引擎为Velocity,我们需要复制以.vm
结尾的文件到resource
随便一个目录中,然后随便改:
接着我们配置一下模版:
@Test
void contextLoads() {
FastAutoGenerator
...
.strategyConfig(builder -> {
builder
.mapperBuilder()
.enableFileOverride() //开启文件重写,自动覆盖新的
.mapperAnnotation(Mapper.class)
.build();
})
.templateConfig(builder -> {
builder.mapper("/templates/mapper.java.vm");
})
.execute();
}
这样,新生成的代码中就是按照我们自己的模版来定义了: