天天看點

Mybatis-Flex 一個優雅的 Mybatis 增強架構

作者:GitHub精選
Mybatis-Flex 一個優雅的 Mybatis 增強架構

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