Fluent MyBatis使用入門

引言

Java中常用的ORM框架主要是mybatis, hibernate, JPA等框架帕棉。

國內(nèi)又以Mybatis用的多剿牺,基于mybatis上的增強框架,又有mybatis plus和TK mybatis等制跟。

今天我們介紹一個新的mybatis增強框架 fluent mybatis,

那既然JDBC --> Mybatis或Mybatis Plus無疑簡化了開發(fā)者的工作骆莹,而今天我們所講的 Fluent MyBatis又起到什么作用呢?

初識Fluent MyBatis

Fluent MyBatis是一個 MyBatis 的增強工具靶草,他只做了mybatis的語法糖封裝蹄胰,沒有對mybatis做任何修改。

通過編譯手段奕翔,提供了一系列輔助類來幫助開發(fā)簡化開發(fā)裕寨、提高效率。

入門初體驗

創(chuàng)建一個示例的數(shù)據(jù)庫表

```sql

DROP TABLE IF EXISTS `your_table`;

create table `your_table`

(

? ? id bigint auto_increment comment '主鍵ID' primary key,

? ? name varchar(30) charset utf8 null comment '姓名',

? ? age int null comment '年齡',

? ? email varchar(50) charset utf8 null comment '郵箱',

? ? gmt_create datetime null comment '記錄創(chuàng)建時間',

? ? gmt_modified datetime null comment '記錄最后修改時間',

? ? is_deleted tinyint(2) default 0 null comment '邏輯刪除標識'

);

```

初始化 SpringBoot 項目

設(shè)置項目依賴

1. spring boot: 基于spring boot開發(fā),肯定是必須的

2. lombok: 省略get, set, toString代碼的神器宾袜,個人比較喜歡捻艳;你也可以手動生成get set方法

3. mysql-connector-java: 數(shù)據(jù)庫驅(qū)動

4. fluent-mybatis: fluent-mybatis運行時依賴

5. fluent-mybatis-processor: fluent-mybatis編譯時依賴

6. fluent-mybatis-generator: fluent-mybatis代碼生成依賴

6. 測試依賴的jar包: spring-test, junit

[maven pom具體配置]

配置數(shù)據(jù)庫信息

```properties

spring.datasource.username=root

spring.datasource.password=password

spring.datasource.url=jdbc:mysql://localhost:3306/fluent_mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

```

[properties具體配置]

創(chuàng)建(生成)實體類

可以手工創(chuàng)建Entity類,或者任何手段創(chuàng)建的Entity類庆猫,然后加上注解

1. 在Entity類上加上 @FluentMybatis注解

2. 在主鍵字段加 @TableId注解

3. 在一般字段加 @TableField注解

這里直接使用fluent mybatis提供的工具類生成代碼

```java

public class AppEntityGenerator {

? ? static final String url = "jdbc:mysql://localhost:3306/fluent_mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8";

? ? public static void main(String[] args) {

? ? ? ? FileGenerator.build(Abc.class);

? ? }

? ? @Tables(

? ? ? ? /** 數(shù)據(jù)庫連接信息 **/

? ? ? ? url = url, username = "root", password = "password",

? ? ? ? /** Entity類parent package路徑 **/

? ? ? ? basePack = "cn.org.fluent.mybatis.springboot.demo",

? ? ? ? /** Entity代碼源目錄 **/

? ? ? ? srcDir = "spring-boot-demo/src/main/java",

? ? ? ? /** Dao代碼源目錄 **/

? ? ? ? daoDir = "spring-boot-demo/src/main/java",

? ? ? ? /** 如果表定義記錄創(chuàng)建认轨,記錄修改,邏輯刪除字段 **/

? ? ? ? gmtCreated = "gmt_create", gmtModified = "gmt_modified", logicDeleted = "is_deleted",

? ? ? ? /** 需要生成文件的表 **/

? ? ? ? tables = @Table(value = {"your_table"})

? ? )

? ? static class Abc {

? ? }

}

```

[具體代碼]

這里有3個特殊字段

1. gmt_create, 記錄創(chuàng)建時間月培,會設(shè)置記錄插入的默認值嘁字,對應(yīng)生成Entity字段上的注解 @TableField(insert="now()")

2. gmt_modified, 記錄最后更新時間,會設(shè)置記錄插入和更新默認值杉畜,對應(yīng)生成代碼Entity字段上注解? @TableField(insert="now()", update="now()")

3. is_deleted, 記錄邏輯刪除標識纪蜒,字段類型為Boolean,且設(shè)置記錄插入的默認值寻行,對應(yīng)注解 @TableField(insert="0")

執(zhí)行生成代碼main函數(shù), 在工程main/src/java目錄下產(chǎn)出 Entity, DaoIntf, DaoImpl文件霍掺;

觀察YourEntity的主鍵 id, gmt_create, gmt_modified, is_deleted這幾個字段的注解

```java

@Data

@Accessors(chain = true)

@FluentMybatis(table = "your_table")

public class YourEntity implements IEntity{

? ? private static final long serialVersionUID = 1L;

? ? @TableId(value = "id")

? ? private Long id;

? ? @TableField(value = "gmt_create", insert = "now()")

? ? private Date gmtCreate;

? ? @TableField(value = "gmt_modified", insert = "now()", update = "now()")

? ? private Date gmtModified;

? ? @TableField(value = "is_deleted", insert = "0")

? ? private Boolean isDeleted;

? ? @TableField(value = "age")

? ? private Integer age;

? ? @TableField(value = "email")

? ? private String email;

? ? @TableField(value = "name")

? ? private String name;

? ? @Override

? ? public Serializable findPk() {

? ? ? ? return id;

? ? }

}

```

生成的Dao文件,引用到了YourTableBaseDao類拌蜘,這個類需要根據(jù)Entity類編譯生成杆烁,在重新編譯前會有編譯錯誤,所以生成代碼后需要重新Rebuild下

```java

@Repository

public class YourDaoImpl extends YourBaseDao implements YourDao {

? ? // 在這里添加你自己的業(yè)務(wù)邏輯代碼

}

```


在Rebuild后简卧,會在target目錄下就會多出幾個文件, 重新刷新一下工程把target/generated-sources加到源目錄上即可兔魂。


啟動SpringBoot測試,驗證效果

這時工程已經(jīng)具備fluent mybatis強大的增刪改查功能了举娩。我們創(chuàng)建一個測試類來驗證一下析校,在測試類中注入 YourMapper,這里演示一個查詢所有的方法铜涉,所以使用了 listEntity 智玻,其參數(shù)是一個Query對象。

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void contextLoads() {

? ? ? ? List<YourEntity> list = yourMapper.listEntity(yourMapper.query());

? ? ? ? for (YourEntity entity : list) {

? ? ? ? ? ? System.out.println(entity);

? ? ? ? }

? ? }

}

```

你可以手工往數(shù)據(jù)庫中插入幾條記錄芙代,驗證一下效果吊奢。

Entity對應(yīng)的Mapper提供的數(shù)據(jù)操作方法

下面我們分別介紹FluentMybatis提供的insert, select, update和delete方法,內(nèi)容的介紹基本按4部分解析

1. 方法的Mapper定義(**編譯生成的代碼**)

2. Mapper對應(yīng)的動態(tài)SQL組裝SQLProvider(**編譯生成的代碼**)

3. 一個驗證測試例子

4. 根據(jù)例子打印的SQL語句和信息輸出纹烹,對照查看

FluentMybatis提供的insert方法

insert:單條插入操作

-Mapper方法

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? /**

? * 插入一條記錄

? *

? * @param entity

? * @return

? */

? @Override

? @InsertProvider(

? ? ? type = YourSqlProvider.class,

? ? ? method = "insert"

? )

? @Options(

? ? ? useGeneratedKeys = true,

? ? ? keyProperty = "id",

? ? ? keyColumn = "id"

? )

? int insert(YourEntity entity);

}

```

- 動態(tài)SQL組裝

```java

public class YourSqlProvider {

? ? public String insert(YourEntity entity) {

? ? ? ? assertNotNull("entity", entity);

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.INSERT_INTO("your_table");

? ? ? ? List<String> columns = new ArrayList<>();

? ? ? ? List<String> values = new ArrayList<>();

? ? ? ? if (entity.getId() != null) {

? ? ? ? ? ? columns.add("id");

? ? ? ? ? ? values.add("#{id}");

? ? ? ? }

? ? ? ? columns.add("gmt_create");

? ? ? ? if (entity.getGmtCreate() != null) {

? ? ? ? ? ? values.add("#{gmtCreate}");

? ? ? ? } else {

? ? ? ? ? ? values.add("now()");

? ? ? ? }

? ? ? ? columns.add("gmt_modified");

? ? ? ? if (entity.getGmtModified() != null) {

? ? ? ? ? ? values.add("#{gmtModified}");

? ? ? ? } else {

? ? ? ? ? ? values.add("now()");

? ? ? ? }

? ? ? ? columns.add("is_deleted");

? ? ? ? if (entity.getIsDeleted() != null) {

? ? ? ? ? ? values.add("#{isDeleted}");

? ? ? ? } else {

? ? ? ? ? ? values.add("0");

? ? ? ? }

? ? ? ? if (entity.getAge() != null) {

? ? ? ? ? ? columns.add("age");

? ? ? ? ? ? values.add("#{age}");

? ? ? ? }

? ? ? ? if (entity.getEmail() != null) {

? ? ? ? ? ? columns.add("email");

? ? ? ? ? ? values.add("#{email}");

? ? ? ? }

? ? ? ? if (entity.getName() != null) {

? ? ? ? ? ? columns.add("name");

? ? ? ? ? ? values.add("#{name}");

? ? ? ? }

? ? ? ? sql.INSERT_COLUMNS(columns);

? ? ? ? sql.VALUES();

? ? ? ? sql.INSERT_VALUES(values);

? ? ? ? return sql.toString();

? ? }

}

```

組裝過程中页滚,對對應(yīng)了 @TableField(insert="默認值")的3個字段:gmt_crate, gmt_modified, is_deleted做了特殊判斷。

- 編寫insert test驗證下

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void insert() {

? ? ? ? // 構(gòu)造一個對象

? ? ? ? YourEntity entity = new YourEntity();

? ? ? ? entity.setName("Fluent Mybatis");

? ? ? ? entity.setAge(1);

? ? ? ? entity.setEmail("darui.wu@163.com");

? ? ? ? entity.setIsDeleted(false);

? ? ? ? // 插入操作

? ? ? ? int count = yourMapper.insert(entity);

? ? ? ? System.out.println("count:" + count);

? ? ? ? System.out.println("entity:" + entity);

? ? }

}

```

- 執(zhí)行insert測試方法, 查看控制臺輸出log信息

```text

DEBUG - ==>? Preparing: INSERT INTO your_table(gmt_create, gmt_modified, is_deleted, age, email, name) VALUES (now(), now(), ?, ?, ?, ?)?

DEBUG - ==> Parameters: false(Boolean), 1(Integer), darui.wu@163.com(String), Fluent Mybatis(String)

DEBUG - <==? ? Updates: 1

count:1

entity:YourEntity(id=18, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis)

```

- 這里有幾個需要注意的地方

1. Entity主鍵值的自增和回寫

根據(jù)控制臺輸出铺呵,可以看到Entity的id屬性已經(jīng)是根據(jù)數(shù)據(jù)庫自增主鍵值回寫過的裹驰。

自增主鍵的設(shè)置是通過 @TableId 注解來的,其屬性方法auto()默認值是true片挂。

2. fluent mybatis根據(jù)@TableId注解生成的Mapper類上@Options注解如下:

``` java

@Options(

? useGeneratedKeys = true,

? keyProperty = "id",

? keyColumn = "id"

)

```

3. gmt_created, gmt_modified, is_deleted 默認值插入處理

我們先看一下Entity上這3個字段的@TableField注解, 他們都定義了一個屬性方法insert幻林,設(shè)置了insert的默認值(即程序編碼insert時贞盯,如果沒有設(shè)置該字段,則使用默認值)

``` java

? ? @TableField(value = "gmt_create", insert = "now()")

? ? private Date gmtCreate;

? ? @TableField(value = "gmt_modified", insert = "now()", update = "now()")

? ? private Date gmtModified;

? ? @TableField(value = "is_deleted", insert = "0")

? ? private Boolean isDeleted;

```

在測試例子中沪饺,gmt_created和gmt_modified在初始化Entity時邻悬,沒有設(shè)置任何值; is_deleted設(shè)置了值false。

在構(gòu)建sql是随闽,gmt_created, gmt_modified直接使用默認值 "now()", is_deleted使用預(yù)編譯變量(?)設(shè)置(實際值false)。

```sql

INSERT INTO your_table

(gmt_create, gmt_modified, is_deleted, age, email, name)

VALUES

(now(), now(), ?, ?, ?, ?)

```

我們再看一下對應(yīng)的SQLProvider的SQL構(gòu)造, 我們只看著3個字段的構(gòu)造

```java

public class YourSqlProvider {

? ? public String insert(YourEntity entity) {

? ? ? ? List<String> columns = new ArrayList<>();

? ? ? ? List<String> values = new ArrayList<>();

? ? ? ? // 省略 ... ...

? ? ? ? columns.add("gmt_create");

? ? ? ? if (entity.getGmtCreate() != null) {

? ? ? ? ? ? values.add("#{gmtCreate}");

? ? ? ? } else {

? ? ? ? ? ? values.add("now()");

? ? ? ? }

? ? ? ? columns.add("gmt_modified");

? ? ? ? if (entity.getGmtModified() != null) {

? ? ? ? ? ? values.add("#{gmtModified}");

? ? ? ? } else {

? ? ? ? ? ? values.add("now()");

? ? ? ? }

? ? ? ? columns.add("is_deleted");

? ? ? ? if (entity.getIsDeleted() != null) {

? ? ? ? ? ? values.add("#{isDeleted}");

? ? ? ? } else {

? ? ? ? ? ? values.add("0");

? ? ? ? }

? ? ? ? if (entity.getAge() != null) {

? ? ? ? ? ? columns.add("age");

? ? ? ? ? ? values.add("#{age}");

? ? ? ? }

? ? ? ? // 省略... ...

? ? ? ? return sql.toString();

? ? }

}

```

我們看到肝谭,沒有 insert屬性的字段掘宪,只判斷了是否為空; 有insert屬性的字段,如果entity不為空攘烛,則把默認值賦值給sql語句魏滚。

insertBatch:批量插入

-? 查看Mapper對應(yīng)的SqlProvider中insertBatch動態(tài)SQL的構(gòu)造

```java

public class YourSqlProvider {

? ? public String insertBatch(Map map) {

? ? ? ? assertNotEmpty("map", map);

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? List<YourEntity> entities = getParas(map, "list");

? ? ? ? sql.INSERT_INTO("your_table");

? ? ? ? sql.INSERT_COLUMNS(ALL_ENTITY_FIELDS);

? ? ? ? sql.VALUES();

? ? ? ? for (int index = 0; index < entities.size(); index++) {

? ? ? ? ? ? if (index > 0) {

? ? ? ? ? ? ? ? sql.APPEND(", ");

? ? ? ? ? ? }

? ? ? ? ? ? sql.INSERT_VALUES(

? ? ? ? ? ? ? ? "#{list[" + index + "].id}",

? ? ? ? ? ? ? ? entities.get(index).getGmtCreate() == null ? "now()" : "#{list[" + index + "].gmtCreate}",

? ? ? ? ? ? ? ? entities.get(index).getGmtModified() == null ? "now()" : "#{list[" + index + "].gmtModified}",

? ? ? ? ? ? ? ? entities.get(index).getIsDeleted() == null ? "0" : "#{list[" + index + "].isDeleted}",

? ? ? ? ? ? ? ? "#{list[" + index + "].age}",

? ? ? ? ? ? ? ? "#{list[" + index + "].email}",

? ? ? ? ? ? ? ? "#{list[" + index + "].name}"

? ? ? ? ? ? );

? ? ? ? }

? ? ? ? return sql.toString();

? ? }

}

```

SQL構(gòu)造語句是通過一個for循環(huán)遍歷實體列表,構(gòu)造出下列SQL語句, 其中對有insert默認值屬性處理方式同單條insert一樣, 這里就不再重復(fù)坟漱。

```sql

INSERT INTO your_table ('Entity對應(yīng)的字段列表') VALUES ('實例1值'), ('實例2值')

```

- 寫個測試看看具體效果

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;


? ? void insertBatch(){

? ? ? ? List<YourEntity> entities = new ArrayList<>();

? ? ? ? entities.add(new YourEntity().setName("Fluent Mybatis").setEmail("darui.wu@163.com"));

? ? ? ? entities.add(new YourEntity().setName("Fluent Mybatis Demo").setEmail("darui.wu@163.com"));

? ? ? ? entities.add(new YourEntity().setName("Test4J").setEmail("darui.wu@163.com"));

? ? ? ? int count = yourMapper.insertBatch(entities);

? ? ? ? System.out.println("count:" + count);

? ? ? ? System.out.println("entity:" + entities);

? ? }

}

```

- 執(zhí)行測試鼠次,查看控制臺輸出

```text

DEBUG - ==>? Preparing: INSERT INTO your_table(id, gmt_create, gmt_modified, is_deleted, age, email, name) VALUES (?, now(), now(), 0, ?, ?, ?) , (?, now(), now(), 0, ?, ?, ?) , (?, now(), now(), 0, ?, ?, ?)?

DEBUG - ==> Parameters: null, null, darui.wu@163.com(String), Fluent Mybatis(String), null, null, darui.wu@163.com(String), Fluent Mybatis Demo(String), null, null, darui.wu@163.com(String), Test4J(String)

DEBUG - <==? ? Updates: 3

count:3

entity:[YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=null, email=darui.wu@163.com, name=Fluent Mybatis), YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=null, email=darui.wu@163.com, name=Fluent Mybatis Demo), YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=null, email=darui.wu@163.com, name=Test4J)]

```

FluentMybatis提供的select查詢方法

findById:根據(jù)id查找單條數(shù)據(jù)

- 系統(tǒng)生成的Mapper方法定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? ? String ResultMap = "YourEntityResultMap";

? ? @SelectProvider(

? ? ? ? type = YourSqlProvider.class,

? ? ? ? method = "findById"

? ? )

? ? @Results(

? ? ? ? id = ResultMap,

? ? ? ? value = {

? ? ? ? ? ? @Result(column = "id", property = "id", javaType = Long.class, id = true),

? ? ? ? ? ? @Result(column = "gmt_create", property = "gmtCreate", javaType = Date.class),

? ? ? ? ? ? @Result(column = "gmt_modified", property = "gmtModified", javaType = Date.class),

? ? ? ? ? ? @Result(column = "is_deleted", property = "isDeleted", javaType = Boolean.class),

? ? ? ? ? ? @Result(column = "age", property = "age", javaType = Integer.class),

? ? ? ? ? ? @Result(column = "email", property = "email", javaType = String.class),

? ? ? ? ? ? @Result(column = "name", property = "name", javaType = String.class)

? ? ? ? }

? ? )

? ? YourEntity findById(Serializable id);

}

```

在findById上,除了定義了提供動態(tài)SQL語句的SQLProvider類和方法外芋齿,還定義的數(shù)據(jù)映射關(guān)系 @Results腥寇。

這個ResultMap映射在單個Mapper里是通用的,其他的查詢方法返回Entity對象時也會用到觅捆。

- 系統(tǒng)生成的動態(tài)sql構(gòu)造方法

```java

public class YourSqlProvider {

? ? public String findById(Serializable id) {

? ? ? ? assertNotNull("id", id);

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.SELECT("your_table", ALL_ENTITY_FIELDS);

? ? ? ? sql.WHERE("id = #{id}");

? ? ? ? return sql.toString();

? ? }

}

```

這個SQL拼接比較簡單

1. 根據(jù)Entity字段拼接了查詢字段列表

2. 設(shè)置 id = #{id}

- 寫個測試實際使用下

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void findById(){

? ? ? ? YourEntity entity = yourMapper.findById(8L);

? ? ? ? System.out.println(entity);

? ? }

}

```

- 查看控制臺輸出log

```text

DEBUG - ==>? Preparing: SELECT id, gmt_create, gmt_modified, is_deleted, age, email, name FROM your_table WHERE id = ??

DEBUG - ==> Parameters: 8(Long)

DEBUG - <==? ? ? Total: 1

YourEntity(id=8, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis)

```

listByIds:根據(jù)id列表批量查詢實例

- Mapper定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? ? String ResultMap = "YourEntityResultMap";

? ? @Override

? ? @SelectProvider(

? ? ? ? type = YourSqlProvider.class,

? ? ? ? method = "listByIds"

? ? )

? ? @ResultMap(ResultMap)

? ? List<YourEntity> listByIds(@Param(Param_Coll) Collection ids);

}

```

輸入是一個id列表集合赦役,返回是一個Entity列表, 數(shù)據(jù)的映射復(fù)用了findById中定義的ResultMap。

- 動態(tài)SQL提供方法

```java

public class YourSqlProvider {

? ? public String listByIds(Map map) {

? ? ? ? Collection ids = getParas(map, "coll");

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.SELECT("your_table", ALL_ENTITY_FIELDS);

? ? ? ? sql.WHERE_PK_IN("id", ids.size());

? ? ? ? return sql.toString();

? ? }

}

```

1. 根據(jù)Entity字段拼接了查詢字段列表

2. 根據(jù)傳入的id數(shù)量(size), 設(shè)置 id IN (#{coll[0]}, ..., #{coll[size - 1]})

- 寫測試驗證下

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void listByIds() {

? ? ? ? List<YourEntity> entities = yourMapper.listByIds(Arrays.asList(8L, 9L));

? ? ? ? System.out.println(entities);

? ? }

}

```

- 查看控制臺輸出

```text

DEBUG - ==>? Preparing: SELECT id, gmt_create, gmt_modified, is_deleted, age, email, name FROM your_table WHERE id IN (?, ?)?

DEBUG - ==> Parameters: 8(Long), 9(Long)

DEBUG - <==? ? ? Total: 2

[YourEntity(id=8, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis),

YourEntity(id=9, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis)]

```

findOne:根據(jù)自定義條件查詢單條記錄

- Mapper方法定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? ? @SelectProvider(

? ? ? ? type = YourSqlProvider.class,

? ? ? ? method = "findOne"

? ? )

? ? @ResultMap(ResultMap)

? ? YourEntity findOne(@Param(Param_EW) IQuery query);

}

```

- 動態(tài)sql組裝

```java

public class YourSqlProvider {

? ? public String findOne(Map map) {

? ? ? ? WrapperData data = getWrapperData(map, "ew");

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.SELECT("your_table", data, ALL_ENTITY_FIELDS);

? ? ? ? sql.WHERE_GROUP_ORDER_BY(data);

? ? ? ? return byPaged(DbType.MYSQL, data, sql.toString());

? ? }

}

```

動態(tài)SQL組裝做了以下幾件事:

1. 根據(jù)query是否顯式設(shè)置了查詢字段栅炒,設(shè)置select字段列表掂摔,如果未設(shè)置,則取默認拼裝Entity全字段赢赊。

2. 根據(jù)query里面的where, group by, having by和order by設(shè)置查詢條件: sql.WHERE_GROUP_ORDER_BY(data)

3. 根據(jù)是否設(shè)置了分頁信息和數(shù)據(jù)庫類型乙漓,組裝分頁查詢語法: byPaged(DbType.MYSQL, data, sql.toString())

- 寫個測試驗證下

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void findOne() {

? ? ? ? YourEntity entity = yourMapper.findOne(new YourQuery()

? ? ? ? ? ? .where.id().eq(4L).end()

? ? ? ? );

? ? }

}

```

查看控制臺的輸出:

```text

DEBUG - ==>? Preparing: SELECT id, gmt_create, gmt_modified, is_deleted, age, email, name FROM your_table WHERE id = ??

DEBUG - ==> Parameters: 4(Long)

DEBUG - <==? ? ? Total: 1

YourEntity(id=4, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis)

```

這種情況下,數(shù)據(jù)庫中滿足條件的數(shù)據(jù)有一條或0條释移;如果符合條件的數(shù)據(jù)大于一條叭披,情況會怎樣呢,我們再寫一個測試實驗一下秀鞭。

- 如果findOne趋观,符合條件數(shù)據(jù)大于2條

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void findOne2() {

? ? ? ? YourEntity entity = yourMapper.findOne(new YourQuery()

? ? ? ? ? ? .where.name().eq("Fluent Mybatis").end()

? ? ? ? );

? ? ? ? System.out.println(entity);

? ? }

}

```

因為數(shù)據(jù)庫中有多條name='Fluent Mybatis'的數(shù)據(jù),調(diào)用這個方法會拋出異常

```text

DEBUG - ==>? Preparing: SELECT id, gmt_create, gmt_modified, is_deleted, age, email, name FROM your_table WHERE name = ??

DEBUG - ==> Parameters: Fluent Mybatis(String)

DEBUG - <==? ? ? Total: 14

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions

.TooManyResultsException: Expected one result (or null) to be returned by selectOne(),

but found: 14

```

listByMap

- Mapper方法定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? ? String ResultMap = "YourEntityResultMap";

? ? @SelectProvider(

? ? ? ? type = YourSqlProvider.class,

? ? ? ? method = "listByMap"

? ? )

? ? @ResultMap(ResultMap)

? ? List<YourEntity> listByMap(@Param(Param_CM) Map<String, Object> columnMap);

}

```

入?yún)ap<String, Object>, 用來表示查詢數(shù)據(jù)的條件锋边。具體條件是 key = value 的AND關(guān)系皱坛。

- 動態(tài)SQL拼接

```java

public class YourSqlProvider {

? ? public String listByMap(Map map) {

? ? ? ? Map<String, Object> where = getParas(map, "cm");

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.SELECT("your_table", ALL_ENTITY_FIELDS);

? ? ? ? sql.WHERE("cm", where);

? ? ? ? return sql.toString();

? ? }

}

```

1. 查詢Entity所有字段

2. 組裝map條件, (key1 = value1) AND (key2 = value2)

- 寫個測試demo驗證下

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void listByMap() {

? ? ? ? List<YourEntity> entities = yourMapper.listByMap(new HashMap<String, Object>() {

? ? ? ? ? ? {

? ? ? ? ? ? ? ? this.put("name", "Fluent Mybatis");

? ? ? ? ? ? ? ? this.put("is_deleted", false);

? ? ? ? ? ? }

? ? ? ? });

? ? ? ? System.out.println(entities);

? ? }

}

```

- 查看控制臺輸出

```text

DEBUG - ==>? Preparing: SELECT id, gmt_create, gmt_modified, is_deleted, age, email, name FROM your_table WHERE is_deleted = ? AND name = ??

DEBUG - ==> Parameters: false(Boolean), Fluent Mybatis(String)

DEBUG - <==? ? ? Total: 5

[YourEntity(id=4, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis),

YourEntity(id=5, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis),

YourEntity(id=6, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis),

YourEntity(id=7, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis),

YourEntity(id=8, gmtCreate=null, gmtModified=null, isDeleted=false, age=1, email=darui.wu@163.com, name=Fluent Mybatis)]

```

?listEntity:根據(jù)自定義條件查詢數(shù)據(jù),并把數(shù)據(jù)映射為對應(yīng)的Entity類

- Mapper方法定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? ? @SelectProvider(

? ? ? ? type = YourSqlProvider.class,

? ? ? ? method = "listEntity"

? ? )

? ? @ResultMap(ResultMap)

? ? List<YourEntity> listEntity(@Param(Param_EW) IQuery query);

}

```

- 動態(tài)SQL組裝

```java

public class YourSqlProvider {

? ? public String listEntity(Map map) {

? ? ? ? WrapperData data = getWrapperData(map, "ew");

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.SELECT("your_table", data, ALL_ENTITY_FIELDS);

? ? ? ? sql.WHERE_GROUP_ORDER_BY(data);

? ? ? ? return byPaged(DbType.MYSQL, data, sql.toString());

? ? }

}

```

同findOne方法, 動態(tài)SQL組裝做了下面幾件事:

1. 根據(jù)query是否顯式設(shè)置了查詢字段豆巨,設(shè)置select字段列表剩辟,如果未設(shè)置,則取默認拼裝Entity全字段。

2. 根據(jù)query里面的where, group by, having by和order by設(shè)置查詢條件: sql.WHERE_GROUP_ORDER_BY(data)

3. 根據(jù)是否設(shè)置了分頁信息和數(shù)據(jù)庫類型贩猎,組裝分頁查詢語法: byPaged(DbType.MYSQL, data, sql.toString())

- 寫個測試看下效果

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void listEntity() {

? ? ? ? List<YourEntity> entities = yourMapper.listEntity(new YourQuery()

? ? ? ? ? ? .select.name().age().email().end()

? ? ? ? ? ? .where.id().lt(6L)

? ? ? ? ? ? .and.name().like("Fluent").end()

? ? ? ? ? ? .orderBy.id().desc().end()

? ? ? ? );

? ? ? ? System.out.println(entities);

? ? }

}

```

- 查看控制臺log

```text

DEBUG - ==>? Preparing: SELECT name, age, email FROM your_table WHERE id < ? AND name LIKE ? ORDER BY id DESC?

DEBUG - ==> Parameters: 6(Long), %Fluent%(String)

DEBUG - <==? ? ? Total: 2

[YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=1, email=darui.wu@163.com, name=Fluent Mybatis),

YourEntity(id=null, gmtCreate=null, gmtModified=null, isDeleted=null, age=1, email=darui.wu@163.com, name=Fluent Mybatis)]

```

自定義查詢定義了

1. 要查詢的字段: name, age, email3個字段

2. 定義了具體條件: id < ? AND name LIKE ?

3. 定義了按id倒序排

listMaps

listMaps參數(shù)構(gòu)造和listEntity一樣熊户,不同的時返回時不映射為Entity,而且映射成Map對象

- 寫個測試驗證下

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void listMaps() {

? ? ? ? List<Map<String,Object>> maps = yourMapper.listMaps(new YourQuery()

? ? ? ? ? ? .select.name().age().email().end()

? ? ? ? ? ? .where.id().lt(6L)

? ? ? ? ? ? .and.name().like("Fluent").end()

? ? ? ? ? ? .orderBy.id().desc().end()

? ? ? ? );

? ? ? ? System.out.println(maps);

? ? }

}

```

- 查看控制臺輸出信息

```text

DEBUG - ==>? Preparing: SELECT name, age, email AS EMail FROM your_table WHERE id < ? AND name LIKE ? ORDER BY id DESC?

DEBUG - ==> Parameters: 6(Long), %Fluent%(String)

DEBUG - <==? ? ? Total: 2

[{name=Fluent Mybatis, EMail=darui.wu@163.com},

{name=Fluent Mybatis, EMail=darui.wu@163.com}]

```

listObjs

listObjs查詢參數(shù)構(gòu)造和listEntity吭服、listMaps一樣嚷堡,但只返回查詢對象的第一列,其余列被舍棄艇棕。

- 驗證例子

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void listObjs() {

? ? ? ? List<String> ids = yourMapper.listObjs(new YourQuery()

? ? ? ? ? ? .select.name().age().email().end()

? ? ? ? ? ? .where.id().lt(6L)

? ? ? ? ? ? .and.name().like("Fluent").end()

? ? ? ? ? ? .orderBy.id().desc().end()

? ? ? ? );

? ? ? ? System.out.println(ids);

? ? }

}

```

- 查看控制臺輸出信息

```text

DEBUG - ==>? Preparing: SELECT name, age, email AS EMail FROM your_table WHERE id < ? AND name LIKE ? ORDER BY id DESC?

DEBUG - ==> Parameters: 6(Long), %Fluent%(String)

DEBUG - <==? ? ? Total: 2

[Fluent Mybatis, Fluent Mybatis]

```

我們看到蝌戒,控制臺只打印出了查詢字段的第一列name: [Fluent Mybatis, Fluent Mybatis]

?count

count, 返回符合條件的記錄數(shù)

- Mapper定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? @SelectProvider(

? ? ? type = YourSqlProvider.class,

? ? ? method = "count"

? )

? Integer count(@Param(Param_EW) IQuery query);

}

```

- 驗證示例

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void count() {

? ? ? ? int count = yourMapper.count(new YourQuery()

? ? ? ? ? ? .where.id().lt(1000L)

? ? ? ? ? ? .and.name().like("Fluent").end()

? ? ? ? ? ? .limit(0, 10)

? ? ? ? );

? ? ? ? System.out.println(count);

? ? }

}

```

- 查看控制臺輸出信息

```text

DEBUG - ==>? Preparing: SELECT COUNT(*) FROM your_table WHERE id < ? AND name LIKE ? LIMIT ?, ??

DEBUG - ==> Parameters: 1000(Long), %Fluent%(String), 0(Integer), 10(Integer)

DEBUG - <==? ? ? Total: 1

5

```

countNoLimit

使用方法同count,只是SQL語句部分舍棄了limit設(shè)置(如果你設(shè)置了)

- 驗證示例

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void countNoLimit() {

? ? ? ? int count = yourMapper.countNoLimit(new YourQuery()

? ? ? ? ? ? .where.id().lt(1000L)

? ? ? ? ? ? .and.name().like("Fluent").end()

? ? ? ? ? ? .limit(0, 10)

? ? ? ? );

? ? ? ? System.out.println(count);

? ? }

}

```

- 查看控制臺輸出

```text

DEBUG - ==>? Preparing: SELECT COUNT(*) FROM your_table WHERE id < ? AND name LIKE ??

DEBUG - ==> Parameters: 1000(Long), %Fluent%(String)

DEBUG - <==? ? ? Total: 1

5

```

我們看到打印出的SQL語句和count方法相比,少了limit部分沼琉。

FluentMybatis提供的update更新方法

updateById

updateById 根據(jù)Entity id值北苟,更新Entity中非空屬性

- Mapper定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? @UpdateProvider(

? ? ? type = YourSqlProvider.class,

? ? ? method = "updateById"

? )

? int updateById(@Param(Param_ET) YourEntity entity);

}

```

入?yún)⑹荅ntity對象, 出參是更新記錄數(shù)打瘪,這里返回值只可能是0: 不存在id記錄,更新失斢驯恰;1: 更新id記錄成功闺骚。

- 動態(tài)SQL組裝

```java

public class YourSqlProvider {

? ? public String updateById(Map<String, Object> map) {

? ? ? ? YourEntity entity = getParas(map, "et");

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.UPDATE("your_table");

? ? ? ? List<String> sets = new ArrayList<>();

? ? ? ? if (entity.getGmtCreate() != null) {

? ? ? ? ? ? sets.add("gmt_create = #{et.gmtCreate}");

? ? ? ? }

? ? ? ? if (entity.getGmtModified() != null) {

? ? ? ? ? ? sets.add("gmt_modified = #{et.gmtModified}");

? ? ? ? } else {

? ? ? ? ? ? sets.add("gmt_modified = now()");

? ? ? ? }

? ? ? ? if (entity.getIsDeleted() != null) {

? ? ? ? ? ? sets.add("is_deleted = #{et.isDeleted}");

? ? ? ? }

? ? ? ? if (entity.getAge() != null) {

? ? ? ? ? ? sets.add("age = #{et.age}");

? ? ? ? }

? ? ? ? if (entity.getEmail() != null) {

? ? ? ? ? ? sets.add("email = #{et.email}");

? ? ? ? }

? ? ? ? if (entity.getName() != null) {

? ? ? ? ? ? sets.add("name = #{et.name}");

? ? ? ? }

? ? ? ? sql.SET(sets);

? ? ? ? sql.WHERE("id = #{et.id}");

? ? ? ? return sql.toString();

? ? }

}

```

我們看到彩扔,在設(shè)置set時,會判斷entity對象是否為null葛碧;但如果在Entity對象上設(shè)置了 @TableField( update = 'update默認值')借杰,

則entity屬性是空的情況下,會使用默認值代替进泼,比如上面gmtModified屬性

``` java

if (entity.getGmtModified() != null) {

? ? sets.add("gmt_modified = #{et.gmtModified}");

} else {

? ? sets.add("gmt_modified = now()");

}

```

where條件部分則比較簡單: id = #{et.id}

- 演示驗證例子

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;


? ? @Test

? ? void updateById() {

? ? ? ? int count = yourMapper.updateById(new YourEntity()

? ? ? ? ? ? .setId(2L)

? ? ? ? ? ? .setName("Powerful Fluent Mybatis")

? ? ? ? );

? ? ? ? System.out.println(count);

? ? }

}

```

- 查看控制臺輸出

```text

DEBUG - ==>? Preparing: UPDATE your_table SET gmt_modified = now(), name = ? WHERE id = ??

DEBUG - ==> Parameters: Powerful Fluent Mybatis(String), 2(Long)

DEBUG - <==? ? Updates: 1

1

```

我們看到update set部分蔗衡,除了設(shè)置了name=?,還設(shè)置了 gmt_modified = now()

updateBy

updateBy, 根據(jù)自定義set語句乳绕,where條件執(zhí)行更新操作

- Mapper定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? @UpdateProvider(

? ? ? type = YourSqlProvider.class,

? ? ? method = "updateBy"

? )

? int updateBy(@Param(Param_EW) IUpdate update);

}

```

入?yún)⑹且粋€IUpdate對象绞惦,出參是更新成功的記錄數(shù)。

- 動態(tài)SQL構(gòu)造

```java

public class YourSqlProvider {

? ? public String updateBy(Map<String, Object> map) {

? ? ? ? WrapperData data = getWrapperData(map, "ew");

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? Map<String, String> updates = data.getUpdates();

? ? ? ? assertNotEmpty("updates", updates);

? ? ? ? sql.UPDATE("your_table");

? ? ? ? List<String> sets = new ArrayList<>();

? ? ? ? if (!updates.containsKey("gmtModified")) {

? ? ? ? ? ? sets.add("gmt_modified = now()");

? ? ? ? }

? ? ? ? sets.add(data.getUpdateStr());

? ? ? ? sql.SET(sets);

? ? ? ? sql.WHERE_GROUP_ORDER_BY(data);

? ? ? ? sql.LIMIT(data, true);

? ? ? ? return sql.toString();

? ? }

}

```

動態(tài)構(gòu)造語句中對 @TableField( update = 'update默認值')字段(這里是gmtModified)做了單獨判斷,

如果條件中不包含gmtModified洋措,則追加默認值更新济蝉。

- 寫個例子驗證

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void updateBy() {

? ? ? ? int count = yourMapper.updateBy(new YourUpdate()

? ? ? ? ? ? .update.name().is("Powerful Fluent mybatis")

? ? ? ? ? ? .set.email().is("darui.wu@163.com")

? ? ? ? ? ? .set.age().is(1).end()

? ? ? ? ? ? .where.id().eq(2).end()

? ? ? ? );

? ? ? ? System.out.println(count);

? ? }

}

```

- 查看控制臺輸出

```text

DEBUG - ==>? Preparing: UPDATE your_table SET gmt_modified = now(), name = ?, email = ?, age = ? WHERE id = ??

DEBUG - ==> Parameters: Powerful Fluent mybatis(String), darui.wu@163.com(String), 1(Integer), 2(Integer)

DEBUG - <==? ? Updates: 1

1

```

注意 gmt_modified = now()更新默認值部分

FluentMybatis提供的delete方法

deleteById:根據(jù)主鍵Id物理刪除記錄

- 查看deleteById對應(yīng)的SqlProvider語句構(gòu)造方法

```java

public class YourSqlProvider {

? ? public String deleteById(Serializable id) {

? ? ? ? MapperSql sql = new MapperSql();

? ? ? ? sql.DELETE_FROM("your_table");

? ? ? ? sql.WHERE("id = #{id}");

? ? ? ? return sql.toString();

? ? }

}

```

- deleteById的SQL構(gòu)造比較簡單,我們直接看測試演示例子

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void deleteById(){

? ? ? ? int count = yourMapper.deleteById(3L);

? ? ? ? System.out.println("count:" + count);

? ? }

}

```

- 查看控制臺輸出log:

```text

DEBUG - ==>? Preparing: DELETE FROM your_table WHERE id = ??

DEBUG - ==> Parameters: 3(Long)

DEBUG - <==? ? Updates: 1

count:1

```

deleteByIds:按id列表批量刪除, 用法同deleteById

- 直接寫個測試驗證下

``` java

@Test

void deleteByIds() {

? ? int count = yourMapper.deleteByIds(Arrays.asList(1L, 2L, 3L));

? ? System.out.println("count:" + count);

}

```

- 控制臺輸出

```text

DEBUG - ==>? Preparing: DELETE FROM your_table WHERE id IN (?, ?, ?)?

DEBUG - ==> Parameters: 1(Long), 2(Long), 3(Long)

```

#### delete

delete, 按自定義Query條件刪除記錄

- Mapper定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? ? @DeleteProvider(

? ? ? ? type = YourSqlProvider.class,

? ? ? ? method = "delete"

? ? )

? ? int delete(@Param(Param_EW) IQuery wrapper);

}

```

入?yún)⑹且粋€IQuery對象菠发,出參是刪除記錄數(shù)

- 驗證示例

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void delete() {

? ? ? ? int count = yourMapper.delete(new YourQuery()

? ? ? ? ? ? .where.id().in(new int[]{1, 2, 3}).end()

? ? ? ? );

? ? ? ? System.out.println("count:" + count);

? ? }

}

```

- 查看控制臺輸出

```text

DEBUG - ==>? Preparing: DELETE FROM your_table WHERE id IN (?, ?, ?)?

DEBUG - ==> Parameters: 1(Integer), 2(Integer), 3(Integer)

DEBUG - <==? ? Updates: 3

count:3

```

deleteByMap: 根據(jù)map中key=value條件集更新記錄

- Mapper定義

```java

public interface YourMapper extends IEntityMapper<YourEntity> {

? ? @DeleteProvider(

? ? ? ? type = YourSqlProvider.class,

? ? ? ? method = "deleteByMap"

? ? )

? ? int deleteByMap(@Param(Param_CM) Map<String, Object> cm);

}

```

- 測試演示例子

```java

@SpringBootTest(classes = QuickStartApplication.class)

public class FluentMybatisApplicationTest {

? ? @Autowired

? ? private YourMapper yourMapper;

? ? @Test

? ? void deleteByMap() {

? ? ? ? int count = yourMapper.deleteByMap(new HashMap<String, Object>() {

? ? ? ? ? ? {

? ? ? ? ? ? ? ? this.put("name", "Fluent Mybatis");

? ? ? ? ? ? ? ? this.put("email", "darui.wu@163.com");

? ? ? ? ? ? }

? ? ? ? });

? ? ? ? System.out.println("count:" + count);

? ? }

}

```

- 查看控制臺輸出

```text

DEBUG - ==>? Preparing: DELETE FROM your_table WHERE name = ? AND email = ??

DEBUG - ==> Parameters: Fluent Mybatis(String), darui.wu@163.com(String)

DEBUG - <==? ? Updates: 2

count:2

```

總結(jié)

本篇文章介紹完FluentMuybatis提供Mapper內(nèi)置方法王滤,我們后面接著介紹如何通過IQuery和IUpdate定義強大的動態(tài)SQL語句。

文章中提到示例驗證例子可以在 [FluentMybatis gitee docs上找到](https://gitee.com/fluent-mybatis/fluent-mybatis-docs/blob/master/spring-boot-demo/src/test/java/cn/org/fluent/mybatis/springboot/demo/FluentMybatisApplicationTest.java)

[Fluent Mybatis介紹系列]

[Fluent Mybatis文檔&示例]

[Fluent Mybatis源碼, github]

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滓鸠,一起剝皮案震驚了整個濱河市雁乡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糜俗,老刑警劉巖踱稍,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曲饱,死亡現(xiàn)場離奇詭異,居然都是意外死亡珠月,警方通過查閱死者的電腦和手機扩淀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啤挎,“玉大人驻谆,你說我怎么就攤上這事∏炱福” “怎么了旺韭?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掏觉。 經(jīng)常有香客問我,道長值漫,這世上最難降的妖魔是什么澳腹? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮杨何,結(jié)果婚禮上酱塔,老公的妹妹穿的比我還像新娘。我一直安慰自己危虱,他們只是感情好羊娃,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著埃跷,像睡著了一般蕊玷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弥雹,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天垃帅,我揣著相機與錄音,去河邊找鬼剪勿。 笑死贸诚,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的厕吉。 我是一名探鬼主播酱固,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼头朱!你這毒婦竟也來了运悲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤髓窜,失蹤者是張志新(化名)和其女友劉穎扇苞,沒想到半個月后欺殿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡鳖敷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年脖苏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片定踱。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡棍潘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出崖媚,到底是詐尸還是另有隱情亦歉,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布畅哑,位于F島的核電站肴楷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荠呐。R本人自食惡果不足惜赛蔫,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泥张。 院中可真熱鬧呵恢,春花似錦、人聲如沸媚创。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钞钙。三九已至鳄橘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芒炼,已是汗流浹背挥唠。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留焕议,地道東北人宝磨。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像盅安,于是被迫代替她去往敵國和親唤锉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344