Mybatis-Flex 是一个优雅的 Mybatis 增强框架,它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库,其内置的 QueryWrapper 亮点帮助我们极大的减少了 SQL 编写的工作的同时,减少出错的可能性。
1.轻量:除了 MyBatis,没有任何第三方依赖、没有任何拦截器,在执行的过程中,没有任何的 Sql 解析(Parse)。 这带来了几个好处:极高的性能、极易对代码进行跟踪和调试、把控性更高。
2.灵活:支持 Entity 的增删改查、以及分页查询的同时,MyBatis-Flex 提供了 Db + Row 工具,可以无需实体类对数据库进行增删改查以及分页查询。 与此同时,MyBatis-Flex 内置的 QueryWrapper 可以轻易的帮助我们实现 多表查询、链接查询、子查询 等等常见的 SQL 场景。
3.强大:支持任意关系型数据库,还可以通过方言持续扩展,同时支持 多(复合)主键、逻辑删除、乐观锁配置、数据脱敏、数据审计、 数据填充 等等功能。
简单来说,Mybatis-Flex 相比 Mybatis-Plus 等框架 速度更快、功能更多、代码更简洁~
1)功能对比:
2)性能对比:
这里直接贴测试结果:
MyBatis-Flex 的查询单条数据的速度,大概是 MyBatis-Plus 的 5 ~ 10+ 倍。
MyBatis-Flex 的查询 10 条数据的速度,大概是 MyBatis-Plus 的 5~10 倍左右。
Mybatis-Flex 的分页查询速度,大概是 Mybatis-Plus 的 5~10 倍左右。
Mybatis-Flex 的数据更新速度,大概是 Mybatis-Plus 的 5~10+ 倍。
官方文档:快速开始 - MyBatis-Flex
以 Spring Boot + Maven + Mysql 项目做演示
此处省略~
此时需要创建 Spring Boot 项目,并添加 Maven 依赖;此处我通过 IDEA 使用 Spring Initializer 快速初始化一个 Spring Boot 工程。
项目创建省略~
往 pom.xml 文件中添加以下依赖。
-
org.springframework.boot -
spring-boot-starter-jdbc -
-
-
org.springframework.boot -
spring-boot-starter-web -
-
-
-
org.springframework.boot -
spring-boot-starter-test -
test -
-
-
-
-
-
mysql -
mysql-connector-java -
-
-
com.mybatis-flex -
mybatis-flex-spring-boot-starter -
1.7.3 -
-
-
com.mybatis-flex -
mybatis-flex-processor -
1.7.3 -
provided -
-
-
org.projectlombok -
lombok -
-
-
-
-
-
com.github.pagehelper -
pagehelper -
5.3.0 -
true -
-
-
org.apache.commons -
commons-lang3 -
3.12.0 -
在 application.properties 或 application.yml 中配置数据源:
-
- server.port=8999
-
- spring.application.name=mybatisPlus
-
- spring.datasource.username=root
- spring.datasource.password=root3306
- spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
- spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
📌 User 实体类
- package com.mybatisflex.flex.domain;
-
- import com.mybatisflex.annotation.Column;
- import com.mybatisflex.annotation.Table;
- import lombok.AllArgsConstructor;
- import lombok.Builder;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- import java.io.Serializable;
- import java.util.Date;
-
- /**
- */
- @Data
- @Builder
- @NoArgsConstructor
- @AllArgsConstructor
- //使用 @Table("tb_account") 设置实体类与表名的映射关系
- @Table("user")
- public class User implements Serializable {
- private Integer id;
-
- @Column(value = "name")
- private String name;
- @Column(value = "age")
- private Integer age;
- @Column(value = "email")
- private String email;
- @Column(value = "create_time", onInsertValue = "now()")
- private Date createTime;
- @Column(value = "update_time", onUpdateValue = "now()")
- private Date updateTime;
- @Column(value = "del_flag")
- private int delFlag;
-
- @Column(value = "dept_code")
- private String deptCode;
- }
📌 Mapper 接口继承 BaseMapper 接口
- package com.mybatisflex.flex.mapper;
-
- import com.mybatisflex.core.BaseMapper;
- import com.mybatisflex.flex.domain.User;
-
- /**
- *
- */
- public interface UserMapper extends BaseMapper
{ - }
用于扫描 Mapper 文件夹:
- package com.mybatisflex.flex;
-
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- @SpringBootApplication
- @MapperScan("com.mybatisflex.flex.mapper")
- public class FlexApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(FlexApplication.class, args);
- }
-
- }
- package com.mybatisflex.flex.service;
-
- import com.mybatisflex.core.query.QueryWrapper;
- import com.mybatisflex.core.update.UpdateChain;
- import com.mybatisflex.flex.domain.User;
- import com.mybatisflex.flex.domain.UserDto;
- import com.mybatisflex.flex.domain.table.SysDeptTableDef;
- import com.mybatisflex.flex.domain.table.UserTableDef;
- import com.mybatisflex.flex.mapper.UserMapper;
- import com.mybatisflex.spring.service.impl.ServiceImpl;
- import org.springframework.stereotype.Service;
-
- import java.util.List;
-
- /**
- * @Description:
- * @Date Create in 10:39 2023/11/22
- * @Modified By:
- */
-
- @Service
- public class UserService extends ServiceImpl
{ - /**
- * 查询全部
- * @return
- */
- public List
selectAll(){ - return this.getMapper().selectAll();
- }
-
-
- public List
selectList(){ - QueryWrapper wrapper = QueryWrapper.create()
- // 这里可以指定查询字段
- .select()
- // sql from表名
- .from(User.class)
- .where(User::getName).like("徐")
- .or(UserTableDef.USER.ID.in(2,3).and(UserTableDef.USER.NAME.like("o")));
- return this.getMapper().selectListByQuery(wrapper);
- }
-
- /**
- * 根据userId获取User数据
- * @param userId
- * @return
- */
- public User listById(Integer userId){
- QueryWrapper wrapper = QueryWrapper.create()
- // 这里可以指定查询字段
- .select()
- // sql from表名
- .from(User.class)
- .where(User::getId).eq(userId).and(User::getName).like("徐");
- return this.getMapper().selectOneByQuery(wrapper);
- }
-
- /**
- * 关联查询--链式查询
- */
- public List
getInfo(Integer userId){ - QueryWrapper query = QueryWrapper.create()
- .select(UserTableDef.USER.ALL_COLUMNS)
- .select(SysDeptTableDef.SYS_DEPT.DEPT_NAME)
- .from(UserTableDef.USER).as("u")
- .leftJoin(SysDeptTableDef.SYS_DEPT).as("d").on(UserTableDef.USER.DEPT_CODE.eq(SysDeptTableDef.SYS_DEPT.DEPT_CODE))
- .where(UserTableDef.USER.ID.eq(userId));
- return this.getMapper().selectListByQueryAs(query,UserDto.class);
- }
-
- /**
- * 新增
- * @param user
- */
- public void insert(User user){
- this.getMapper().insert(user);
- }
-
- /**
- * 更新User
- * @param user
- */
- public void updateEntity(User user){
- this.getMapper().update(user);
- }
-
- /**
- * 局部更新
- * @param userId
- * @param userName
- */
- public void updateRow(Integer userId, String userName){
- UpdateChain.of(User.class)
- .set(User::getName, userName)
- .where(User::getId).eq(userId).update();
- }
-
- /**
- * 删除
- * @param userName
- */
- public void deleteByWrapper(String userName){
- QueryWrapper queryWrapper = QueryWrapper.create().where(User::getName).eq(userName);
- this.getMapper().deleteByQuery(queryWrapper);
- }
-
- }
- package com.mybatisflex.flex.controller;
-
- import com.mybatisflex.flex.domain.User;
- import com.mybatisflex.flex.domain.UserDto;
- import com.mybatisflex.flex.page.TableDataInfo;
- import com.mybatisflex.flex.service.UserService;
- import com.mybatisflex.flex.utils.ResponseUtils;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.annotation.Resource;
- import java.util.List;
-
- /**
- * @Author: best_liu
- * @Description:
- * @Date Create in 10:33 2023/11/22
- * @Modified By:
- */
- @RestController
- @RequestMapping("/user")
- public class SysUserController {
-
- @Resource
- private UserService userService;
-
- /**
- * 查询全部
- * @return
- */
- @GetMapping("listall")
- public List
listall(){ - return userService.selectAll();
- }
-
- /**
- * 分页查询
- * @return
- **/
- @GetMapping("/page")
- public TableDataInfo findPage() {
- ResponseUtils.startPage();
- return ResponseUtils.getDataTable(userService.selectAll());
- }
-
-
- /**
- * 按条件查询
- * @return
- */
- @GetMapping("getList")
- public List
selectList(){ - return userService.selectList();
- }
-
- /**
- * 按userId查询
- * @return
- */
- @GetMapping("listById")
- public User listById(){
- return userService.listById(0);
- }
-
- /**
- * 按userId关联查询部门
- * @return
- */
- @GetMapping("getInfo")
- public List
getInfo(){ - return userService.getInfo(0);
- }
-
- /**
- * 新增
- * @return
- */
- @GetMapping("insert")
- public Boolean insert(){
- User user = User.builder().id(10).name("张三").age(100).email("zhangsan@163.com").build();
- userService.insert(user);
- return Boolean.TRUE;
- }
-
- /**
- * 更新
- * @return
- */
- @GetMapping("update")
- public Boolean update(){
- userService.updateRow(10, "张三三");
- return Boolean.TRUE;
- }
-
- /**
- * 删除
- * @return
- */
- @GetMapping("delete")
- public Boolean delete(){
- userService.deleteByWrapper("张三三");
- return Boolean.TRUE;
- }
- }
若想使用链式查询还得需要 APT 配置,MyBatis-Flex 使用了 APT(Annotation Processing Tool)技术,在项目编译的时候,会自动根据 Entity/pojo 类定义的字段帮你生成 "USER" 类(可用于链式查询)
通过开发工具构建项目(如下图),或者执行 maven 编译命令: mvn clean package 都可以自动生成。
正常情况下,会在 target 包下生成如下资源
若生成该资源并导入成功,那么此时,可使用链式查询
- /**
- * 关联查询
- */
- public List
getInfo(Integer userId){ - QueryWrapper query = QueryWrapper.create()
- .select(UserTableDef.USER.ALL_COLUMNS)
- .select(SysDeptTableDef.SYS_DEPT.DEPT_NAME)
- .from(UserTableDef.USER).as("u")
- .leftJoin(SysDeptTableDef.SYS_DEPT).as("d").on(UserTableDef.USER.DEPT_CODE.eq(SysDeptTableDef.SYS_DEPT.DEPT_CODE))
- .where(UserTableDef.USER.ID.eq(userId));
- return this.getMapper().selectListByQueryAs(query,UserDto.class);
- }
总的来说,MyBatis-Flex 的链式查询相比 MyBatis-Plus 多了一步配置环节,目前来看其他步骤类似。
接下来看一下MyBatis-Flex 和 MyBatis-Plus 各部分功能代码的差别,Employee、Account、Article 都是实体类。
MyBatis-Flex:
- QueryWrapper query = QueryWrapper.create()
- .where(EMPLOYEE.LAST_NAME.like(searchWord)) //条件为null时自动忽略
- .and(EMPLOYEE.GENDER.eq(1))
- .and(EMPLOYEE.AGE.gt(24));
- List
employees = employeeMapper.selectListByQuery(query);
MyBatis-Plus:
- QueryWrapper
queryWrapper = Wrappers.query() - .like(searchWord != null, "last_name", searchWord)
- .eq("gender", 1)
- .gt("age", 24);
- List
employees = employeeMapper.selectList(queryWrapper); -
- //lambda 写法:
- LambdaQueryWrapper
queryWrapper = Wrappers.lambdaQuery() - .like(StringUtils.isNotEmpty(searchWord), Employee::getUserName,"B")
- .eq(Employee::getGender, 1)
- .gt(Employee::getAge, 24);
- List
employees = employeeMapper.selectList(queryWrapper);
MyBatis-Flex:
- QueryWrapper query = QueryWrapper.create()
- .select(
- ACCOUNT.ID,
- ACCOUNT.USER_NAME,
- max(ACCOUNT.BIRTHDAY),
- avg(ACCOUNT.SEX).as("sex_avg")
- );
- List
employees = employeeMapper.selectListByQuery(query);
MyBatis-Plus:
- QueryWrapper
queryWrapper = Wrappers.query() - .select(
- "id",
- "user_name",
- "max(birthday)",
- "avg(birthday) as sex_avg"
- );
- List
employees = employeeMapper.selectList(queryWrapper);
缺点:字段硬编码,容易拼错。无法使用 IDE 的字段进行重构,无法使用 IDE 自动提示,发生错误不能及时发现,不过MyBatis-Plus的 lambdawrapper 也是能解决这个问题。
假设我们要构建如下的 SQL 进行查询(需要在 SQL 中添加括号)。
- SELECT * FROM tb_account
- WHERE id >= 100
- AND (sex = 1 OR sex = 2)
- OR (age IN (18,19,20) AND user_name LIKE "%michael%" )
MyBatis-Flex:
- QueryWrapper query = QueryWrapper.create()
- .where(ACCOUNT.ID.ge(100))
- .and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2)))
- .or(ACCOUNT.AGE.in(18, 19, 20).and(ACCOUNT.USER_NAME.like("michael")));
MyBatis-Plus:
- QueryWrapper
query = Wrappers.query() - .ge("id", 100)
- .and(i -> i.eq("sex", 1).or(x -> x.eq("sex", 2)))
- .or(i -> i.in("age", 18, 19, 20).like("user_name", "michael"));
- // or lambda
- LambdaQueryWrapper
query = Wrappers.lambdaQuery() - .ge(Employee::getId, 100)
- .and(i -> i.eq(Employee::getSex, 1).or(x -> x.eq(Employee::getSex, 2)))
- .or(i -> i.in(Employee::getAge, 18, 19, 20).like(Employee::getUserName, "michael"));
MyBatis-Flex:
- QueryWrapper query = QueryWrapper.create()
- .select().from(ACCOUNT)
- .leftJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
- .where(ACCOUNT.AGE.ge(10));
-
- List
accounts = mapper.selectListByQuery(query);
- QueryWrapper query = new QueryWrapper()
- .select(
- ACCOUNT.ID
- , ACCOUNT.USER_NAME
- , ARTICLE.ID.as("articleId")
- , ARTICLE.TITLE)
- .from(ACCOUNT.as("a"), ARTICLE.as("b"))
- .where(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID));
MyBatis-Plus:不支持
假设一个实体类 Account 中,我们要更新其内容如下:
其他字段保持数据库原有内容不变,要求执行的 SQL 如下:
- update tb_account
- set user_name = "michael", age = 18, birthday = null
- where id = 100
MyBatis-Flex 代码如下:
- Account account = UpdateEntity.of(Account.class);
- account.setId(100); //设置主键
- account.setUserName("michael");
- account.setAge(18);
- account.setBirthday(null);
-
- accountMapper.update(account);
MyBatis-Plus 代码如下(或可使用 MyBatis-Plus 的 LambdaUpdateWrapper,但性能没有 UpdateWrapper 好):
- UpdateWrapper
updateWrapper = new UpdateWrapper<>(); - updateWrapper.eq("id", 100);
- updateWrapper.set("user_name", "michael");
- updateWrapper.set("age", 18);
- updateWrapper.set("birthday", null);
-
- accountMapper.update(null, updateWrapper);
如上,MyBatis-Flex 在代码编写来说更加灵活,编写方式更多一些,还是有些优势。