Mybatis-Flex 一个优雅的 Mybatis 增强框架

发布时间 2023-03-22 21:13:12作者: 菜菜聊架构

Mybatis-Flex: 更灵活、更轻量、更好用

特征

  • 很轻量,整个框架只依赖 Mybatis 再无其他第三方依赖
  • 只增强,支持 Entity 的增删改查、及分页查询,但不丢失 Mybatis 原有功能
  • 内置 Db + Row 工具,可以无需实体类对数据库进行增删改查
  • 支持多种数据库类型,还可以通过方言持续扩展
  • 支持多(联合)主键,以及不同的主键内容生成策略
  • 支持逻辑删除设置、更新或插入的默认值配置以及大字段等设置
  • 支持乐观锁字段配置,在数据更新时自动进行乐观锁检测
  • 极其友好的 SQL 联动查询,IDE 自动提示不再担心出错

快速开始

Maven 依赖

以下的 xml maven 依赖示例中,可能并非最新的 Mybatis-Flex 版本,请自行查看最新版本,并修改版本号。

1、只用到了 Mybatis,没用到 Spring 的场景:

<dependency>
    <groupId>com.mybatis-flex</groupId>
    <artifactId>mybatis-flex-core</artifactId>
    <version>1.0.2</version>
</dependency>

2、用到了 Spring 的场景

<dependency>
    <groupId>com.mybatis-flex</groupId>
    <artifactId>mybatis-flex-spring</artifactId>
    <version>1.0.2</version>
</dependency>

3、用到了 Spring Boot 的场景

<dependency>
    <groupId>com.mybatis-flex</groupId>
    <artifactId>mybatis-flex-spring-boot-starter</artifactId>
    <version>1.0.2</version>
</dependency>

hello world(原生)

第一步:编写 Entity 实体类

@Table("tb_account")
public class Account {

    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    private Date birthday;
    private int sex;

    //getter setter
}

第二步,编写 Mapper 类,并继承 BaseMapper

public interface AccountMapper extends BaseMapper<Account> {
    //只需定义 Mapper 接口即可,可以无任何内容。
}

第三步:开始查询数据

示例 1:查询 1 条数据

class HelloWorld {
    public static void main(String... args) {

        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/mybatis-flex");
        dataSource.setUsername("username");
        dataSource.setPassword("password");

        MybatisFlexBootstrap.getInstance()
                .setDatasource(dataSource)
                .addMapper(AccountMapper.class)
                .start();


        //示例1:查询 id=100 条数据
        Account account = MybatisFlexBootstrap.getInstance()
                .execute(AccountMapper.class, mapper ->
                        mapper.selectOneById(100)
                );
    }
}

示例2:查询列表

//示例2:通过 QueryWrapper 构建条件查询数据列表
QueryWrapper query=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .where(ACCOUNT.ID.ge(100))
    .and(ACCOUNT.USER_NAME.like("张").or(ACCOUNT.USER_NAME.like("李")));

// 执行 SQL:
// ELECT * FROM tb_account
// WHERE tb_account.id >=  100
// AND (tb_account.user_name LIKE '%张%' OR tb_account.user_name LIKE '%李%' )
List<Account> accounts = MybatisFlexBootstrap.getInstance()
    .execute(AccountMapper.class,mapper->
        mapper.selectListByQuery(query)
    );

示例3:分页查询

// 示例3:分页查询
// 查询第 5 页,每页 10 条数据,通过 QueryWrapper 构建条件查询
QueryWrapper query=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .where(ACCOUNT.ID.ge(100))
    .and(ACCOUNT.USER_NAME.like("张").or(ACCOUNT.USER_NAME.like("李")))
    .orderBy(ACCOUNT.ID.desc());

// 执行 SQL:
// ELECT * FROM tb_account
// WHERE id >=  100
// AND (user_name LIKE '%张%' OR user_name LIKE '%李%' )
// ORDER BY `id` DESC
// LIMIT 40,10
Page<Account> accounts = MybatisFlexBootstrap.getInstance()
.execute(AccountMapper.class,mapper->
    mapper.paginate(5,10,query)
);

QueryWrapper示例

select *

QueryWrapper query=new QueryWrapper();
query.select().from(ACCOUNT)

// SQL: 
// SELECT * FROM tb_account

select columns

简单示例:

QueryWrapper query=new QueryWrapper();
query.select(ACCOUNT.ID,ACCOUNT.USER_NAME)
    .from(ACCOUNT)

// SQL: 
// SELECT id, user_name 
// FROM tb_account

多表查询(同时展现了功能强大的 as 能力):

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));

// SQL: 
// SELECT a.id, a.user_name, b.id AS articleId, b.title 
// FROM tb_account AS a, tb_article AS b 
// WHERE a.id = b.account_id

select functions

QueryWrapper query=new QueryWrapper()
    .select(
        ACCOUNT.ID,
        ACCOUNT.USER_NAME,
        max(ACCOUNT.BIRTHDAY),
        avg(ACCOUNT.SEX).as("sex_avg")
    ).from(ACCOUNT);

// SQL: 
// SELECT id, user_name, 
// MAX(birthday), 
// AVG(sex) AS sex_avg 
// FROM tb_account

where

QueryWrapper queryWrapper=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .where(ACCOUNT.ID.ge(100))
    .and(ACCOUNT.USER_NAME.like("michael"));

// SQL: 
// SELECT * FROM tb_account 
// WHERE id >=  ?  
// AND user_name LIKE  ? 

where select

QueryWrapper queryWrapper = QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .where(ACCOUNT.ID.ge(
       select(ARTICLE.ACCOUNT_ID).from(ARTICLE).where(ARTICLE.ID.ge(100))
    ));

// SQL: 
// SELECT * FROM tb_account
// WHERE id >= 
// (SELECT account_id FROM tb_article WHERE id >=  ? )

and (...) or (...)

QueryWrapper queryWrapper=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .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")));

// SQL: 
// SELECT * FROM tb_account 
// WHERE id >=  ?  
// AND (sex =  ? OR sex =  ? ) 
// OR (age IN (?,?,?) AND user_name LIKE ? )

group by

QueryWrapper queryWrapper=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .groupBy(ACCOUNT.USER_NAME);

// SQL: 
// SELECT * FROM tb_account 
// GROUP BY user_name

having

QueryWrapper queryWrapper=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .groupBy(ACCOUNT.USER_NAME)
    .having(ACCOUNT.AGE.between(18,25));

// SQL: 
// SELECT * FROM tb_account 
// GROUP BY user_name 
// HAVING age BETWEEN  ? AND ?

orderBy

QueryWrapper queryWrapper=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .orderBy(ACCOUNT.AGE.asc()
        , ACCOUNT.USER_NAME.desc().nullsLast());

// SQL: 
// SELECT * FROM tb_account
// ORDER BY age ASC, user_name DESC NULLS LAST

join

QueryWrapper queryWrapper=QueryWrapper.create()
    .select()
    .from(ACCOUNT)
    .leftJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
    .innerJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
    .where(ACCOUNT.AGE.ge(10));

// SQL: 
// SELECT * FROM tb_account 
// LEFT JOIN tb_article ON tb_account.id = tb_article.account_id 
// INNER JOIN tb_article ON tb_account.id = tb_article.account_id 
// WHERE tb_account.age >=  ?

Mybatis-Flex 乐观锁

使用场景

用于当有多个用户(或者多场景)去同时修改同一条数据的时候,只允许有一个修改成功。

实现原理

使用一个字段,用于记录数据的版本,当修改数据的时候,会去检测当前版本是否是正在修改的版本,同时修改成功后会把 版本号 + 1。

更新数据时,执行的 SQL 如下:

 UPDATE account SET nickname = ?, version = version + 1 
 WHERE id = ? AND version = ?

在以上的 SQL 中,若两个场景同时读取的是同一个 version 的内容,那么必然只最优先执行的 SQL 执行成功。

以下是 示例代码:


@Table("tb_account")
public class Account {

    @Column(version = true)
    private Long version;
    
    //Getter Setter...
}

需要注意的是:

  • 1、在同一张表中,只能有一个被 @Column(version = true) 修饰的字段。
  • 2、Account 在插入数据时,若 version 未设置值,那么会自动被 Mybatis-Flex 设置为 0。

-END-

开源协议:Apache-2.0

开源地址:
https://github.com/mybatis-flex/mybatis-flex