dbVisitor 和 MyBatis有什么區(qū)別
你是否經(jīng)歷過這樣的絕望:為了給一個(gè)簡單的查詢加個(gè) status != -1 的條件,你不得不在 XML 里寫一堆 <if> 標(biāo)簽? 或者,為了同時(shí)查詢 MySQL 和 MongoDB,你的 Service 層里充斥著風(fēng)格迥異的 UserMapper 和 MongoTemplate 代碼?
MyBatis 很好,陪伴了我們十年。它穩(wěn)定、強(qiáng)大,是 Java 該領(lǐng)域的絕對霸主。但面對現(xiàn)在的多數(shù)據(jù)源架構(gòu)、輕量化開發(fā)需求,這艘巨輪是不是顯得有點(diǎn)“重”了?
dbVisitor 的誕生并非為了重復(fù)造輪子,而是為了在保持 MyBatis 核心開發(fā)習(xí)慣(Mapper/XML)的同時(shí),解決那些讓你頭疼已久的痛點(diǎn)。
| 功能體驗(yàn) | MyBatis | MyBatis-Plus | dbVisitor | 備注 |
|---|---|---|---|---|
| Mapper 接口開發(fā) | ? | ? | ? | 都支持定義 Interface 自動(dòng)代理 |
| XML SQL 定義 | ? | ? | ? | 標(biāo)簽結(jié)構(gòu)高度兼容 (ResultMap/select...) |
| 注解 SQL 定義 | ? | ? | ? | @Select / @Insert 等注解支持 |
| DTO/Bean 映射 | ? | ? | ? | 自動(dòng)駝峰轉(zhuǎn)換、ResultMap 映射 |
| Lambda 鏈?zhǔn)秸{(diào)用 | ? | ? | ? | 類型安全的 CRUD 構(gòu)建器 |
| 分頁查詢 | ? | ? | ? | 直接在 API 或 SQL 中控制分頁 |
| 即時(shí) SQL 執(zhí)行 | ? | ? | ? | 無需定義 Mapper 也能直接運(yùn)行 SQL |
| 動(dòng)態(tài)規(guī)則 (Rules) | ? | ? | ? | 腳本式動(dòng)態(tài) SQL @{and ...} |
| NoSQL 支持 | ? | ? | ? | 一套 API 操作 MySQL/Mongo/ES |
1. 熟悉的配方
通過對比 dbVisitor 和 MyBatis 的 XSD 定義文件,可以發(fā)現(xiàn)驚人的相似性(兩者都以 <mapper> 為根節(jié)點(diǎn), 核心標(biāo)簽 <select>, <insert> 完全一致)。 這意味著:MyBatis 開發(fā)者無需閱讀文檔,憑借直覺即可編寫 dbVisitor 的 XML 文件,甚至可以直接復(fù)用現(xiàn)有的 XML 文件。接下來讓我們詳細(xì)看看兩者在經(jīng)典用法上有何差異?
1.1 Mapper 接口開發(fā)
dbVisitor 沿用了 Mapper 接口的開發(fā)模式,對于習(xí)慣了 MyBatis 的開發(fā)者來說幾乎沒有學(xué)習(xí)成本。
MyBatis
使用 @Mapper 標(biāo)記接口,并配合 @Select, @Insert 等注解。
@Mapper
public interface UserMapper {
User selectById(Long id);
@Insert("INSERT INTO user (name, age) VALUES (#{name}, #{age})")
int insert(User user);
}dbVisitor
使用 @SimpleMapper 標(biāo)記接口,使用 @Query 代替 @Select(語義更寬泛,涵蓋所有查詢),其他如 @Insert 等保持一致。
@SimpleMapper
public interface UserMapper {
// 對應(yīng) XML 中的 id 或直接使用 @Query
User selectById(@Param("id") Long id);
@Insert("INSERT INTO user (name, age) VALUES (#{name}, #{age})")
int insert(User user);
}1.2 XML SQL 定義
dbVisitor 的 XML 定義與 MyBatis 在結(jié)構(gòu)和核心標(biāo)簽上高度一致。
MyBatis (UserMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectById" resultType="com.example.entity.User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>dbVisitor (UserMapper.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//dbvisitor.net//DTD Mapper 1.0//EN"
"https://www.dbvisitor.net/schema/dbvisitor-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectById" resultType="com.example.entity.User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>1.3 注解 SQL 定義
MyBatis
@Select("SELECT * FROM user WHERE name = #{name}")
User selectByName(String name);
dbVisitor
@Query("SELECT * FROM user WHERE name = #{name}")
User selectByName(@Param("name") String name);1.4 DTO/Bean 映射
在字段名不一致(如 DB 是 user_name,Java 是 name)的場景下,映射方式的繁簡程度決定了開發(fā)效率。
MyBatis (XML 派)
MyBatis 傾向于通過 XML 中的 ResultMap 來解決字段映射問題。雖然開啟駝峰配置 (mapUnderscoreToCamelCase) 能解決簡單場景,但面對差異較大的字段名,開發(fā)者不得不編寫冗長的 resultMap 標(biāo)簽。
<!-- 必須顯式定義映射關(guān)系 -->
<resultMap id="userMap" type="com.example.User">
<result column="user_name" property="name"/>
<result column="user_age" property="age"/>
</resultMap>
<select id="selectById" resultMap="userMap"> ... </select>MyBatis-Plus (注解派)
MP 引入了注解驅(qū)動(dòng),允許直接在實(shí)體類上定義映射關(guān)系,從而擺脫對 ResultMap 的依賴。
public class User {
@TableField("user_name") // 使用注解解決映射
private String name;
}dbVisitor (全兼容)
dbVisitor 選擇了 “我全都要” 的兼容策略,你可以在三種模式中任意切換:
- 自動(dòng)映射(默認(rèn)):內(nèi)置智能的駝峰轉(zhuǎn)換,90% 的場景無需任何配置。
- 注解模式(MP 風(fēng)格):使用
@Column注解定義特定字段,無需 XML。public class User { @Column("user_name") // 類似 MP 的 @TableField private String name; } - ResultMap 模式(MyBatis 風(fēng)格):完全兼容 MyBatis 的
<resultMap>標(biāo)簽。如果你正在遷移舊項(xiàng)目,或者需要非常復(fù)雜的嵌套映射,直接把舊的 XML 拿來用即可。<!-- 完全兼容 MyBatis 語法的 ResultMap --> <resultMap id="userMap" type="com.example.User"> <result column="user_name" property="name"/> </resultMap>
1.5 Lambda 鏈?zhǔn)秸{(diào)用
dbVisitor 提供了一套類型安全的 Lambda 構(gòu)建器,它的核心價(jià)值不僅僅是像 MyBatis-Plus 那樣避免手寫 SQL,更在于它實(shí)現(xiàn)了 "One API Access Any DataBase" 的理念。 無論底層是 MySQL、Oracle 還是 MongoDB、Elasticsearch,你都可以使用同一套 Lambda API 進(jìn)行操作。這意味著你的團(tuán)隊(duì)只需要掌握一種 CRUD 語法。
用法對比
- MyBatis-Plus:強(qiáng)依賴
Mapper接口,API 設(shè)計(jì)主要面向 SQL 數(shù)據(jù)庫。 - dbVisitor:基于
LambdaTemplate,無需定義接口即可直接使用。
// 統(tǒng)一入口:無論操作什么數(shù)據(jù)庫,起點(diǎn)都是 template new 出來即可 JdbcTemplate template = ...;
場景 1:標(biāo)準(zhǔn) SQL 查詢 (MySQL/PG)
// 使用 Java 方法引用避免字段拼寫錯(cuò)誤
UserInfo user = template.lambdaQuery(UserInfo.class)
.eq(UserInfo::getType, "admin")
.gt(UserInfo::getAge, 18)
.list();
場景 2:統(tǒng)一關(guān)系型非關(guān)系型數(shù)據(jù)庫
神奇之處在于,完全相同的代碼,也可以用于操作 Elasticsearch 或 MongoDB,dbVisitor 會(huì)自動(dòng)將查詢條件轉(zhuǎn)譯為對應(yīng)的 DSL。
// 操作 Elasticsearch (自動(dòng)轉(zhuǎn)換為 DSL)
// GET /user_index/_search { "query": { "bool": ... } }
EsUser user = template.lambdaQuery(EsUser.class)
.eq(EsUser::getId, "1001") // 通過 EsUser 的字段映射為 _id
.one();
// 操作 MongoDB
// db.access_log.insert({ ... })
template.lambdaInsert(AccessLog.class)
.applyEntity(new AccessLog(...))
.executeSumResult();
這種統(tǒng)一性極大地降低了多數(shù)據(jù)源混合架構(gòu)的維護(hù)成本。
2. 拒絕 XML 地獄
2.1 即時(shí) SQL 執(zhí)行
即時(shí) SQL 執(zhí)行能力不僅是 “能執(zhí)行 SQL”,更代表了框架在不同抽象層級上的靈活度。dbVisitor 允許你在三種層級上自由切換,無需被 XML 束縛。
A. 純血 SQL
這是 dbVisitor 最基礎(chǔ)的模式。它高度復(fù)刻了 Spring JDBC 的能力(如 queryForList, query, execute 等)
// 1. 獲取 Template
JdbcTemplate jdbc = ...;
// 2. 直接執(zhí)行 SQL,利用 dbVisitor 強(qiáng)大的 Mapper 映射結(jié)果
List<UserInfo> users = jdbc.queryForList(
"select * from user where age > ?", UserInfo.class, 18);
// 3. 甚至執(zhí)行腳本
jdbc.execute("delete from user where age < 10");B. Lambda API 上使用 SQL
LambdaTemplate 雖然主打類型安全的構(gòu)建器,但它同樣允許你在 Lambda 中直接執(zhí)行原生 SQL。
LambdaTemplate lambda = ...;
lambda.jdbc().queryForList(
"select * from user where status = ?", User.class, 1);C. Mapper 接口上使用 SQL
這是 MyBatis 及 MyBatis-Plus 不具備的,同時(shí)也被詬病最多的地方。
- 用戶:我就是想用一下 SQL 不要給我搞注解、搞 XML 好不好!
- MyBatis/MP:抱歉不行!
- dbVisitor:沒問題馬上安排!
@SimpleMapper
public interface UserMapper extends BaseMapper<UserInfo> {
// 方式 1: 傳統(tǒng)的注解模式
@Query("select * from user where type = #{type}")
List<UserInfo> findByType(@Param("type") String type);
// 方式 2: default 方法 + 內(nèi)置 JDBC 能力
// 適合:邏輯復(fù)雜,不想寫 XML,但又想封裝在 Mapper 里的場景
default List<UserInfo> findActiveUsers() {
return this.jdbc().queryForList(
"select * from user where active = 1", UserInfo.class);
}
}2.2 動(dòng)態(tài)規(guī)則
本質(zhì)是對動(dòng)態(tài) SQL 拼接的一種創(chuàng)新解決方案。它改變了 MyBatis 系列中 <if> 標(biāo)簽泛濫的問題。
- MyBatis 系列:依賴 XML 標(biāo)簽(
<if>,<where>,<choose>)來實(shí)現(xiàn)動(dòng)態(tài) SQL,導(dǎo)致 XML 結(jié)構(gòu)臃腫且難以維護(hù)。 - dbVisitor:引入
@{...}語法,將條件動(dòng)態(tài)嵌入 SQL 字符串中,極大簡化了動(dòng)態(tài) SQL 的編寫。
<!-- MyBatis 系列 -->
<select id="findUser">
SELECT * FROM user
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age > :age
</if>
</where>
</select><!-- dbVisitor 方式 -->
<select id="findUser">
SELECT * FROM user
@{and name = :name}
@{and age > :age}
</select>還能這么用?
// 在 SQL 中使用
JdbcTemplate jdbc = ...;
List<UserInfo> users = jdbc.queryForList(
"SELECT * FROM user @{and name = :name} @{and age > :age}",
UserInfo.class, 18);// 在 Mapper 接口注解中用
@SimpleMapper
public interface UserMapper extends BaseMapper<UserInfo> {
@Query("SELECT * FROM user @{and name = :name} @{and age > :age}")
List<UserInfo> findByType(UserInfo info);
}
優(yōu)勢總結(jié)
- 極簡主義:沒有
<if>,沒有Wrapper,只有純粹的 SQL。 - 直觀:看一眼就知道這段 SQL 包含哪些條件邏輯。
- 復(fù)用性:這行 SQL 字符串可以寫在 XML 里,也可以寫在注解里,甚至寫在純血 SQL 里面。
3. 一套 API 統(tǒng)一訪問
僅僅十年前,一個(gè) MySQL 可能就承載了一個(gè)系統(tǒng)的所有數(shù)據(jù)。但現(xiàn)在的應(yīng)用架構(gòu)往往是混合的。
? MyBatis/MP 的困境:割裂
MyBatis 設(shè)計(jì)之初就是為了 RDBMS(SQL)服務(wù)的。在一個(gè)混合架構(gòu)(MySQL 存用戶,MongoDB 存日志,ES 存商品)中,代碼往往是分裂的:
// 1. MySQL (MyBatis) - 面向 Mapper
userMapper.selectById(1L);
// 2. MongoDB (Spring Data) - 面向 Repository/Template
Query query = new Query(Criteria.where("userId").is(1L));
mongoTemplate.find(query, UserLog.class);
// 3. Elasticsearch (RestHighLevelClient) - 面向 Request 構(gòu)建
SearchRequest request = new SearchRequest("goods");
request.source(new SearchSourceBuilder().query(QueryBuilders.matchQuery("name", "手機(jī)")));
client.search(request, RequestOptions.DEFAULT);痛點(diǎn):
- 認(rèn)知負(fù)荷高:團(tuán)隊(duì)需要掌握 3 套不同的 API 風(fēng)格(Mapper, Criteria, Builder)。
- 維護(hù)困難:無法統(tǒng)一處理事務(wù)、日志監(jiān)控和異常體系。
- 代碼割裂:業(yè)務(wù)邏輯中充斥著不同風(fēng)格的 DAL 代碼。
? dbVisitor 的解法:統(tǒng)一
dbVisitor 通過雙層適配器設(shè)計(jì)實(shí)現(xiàn)了 “One API Access Any DataBase”。無論是關(guān)系型數(shù)據(jù)庫還是 NoSQL,你都可以用 完全相同的 Lambda 語法 進(jìn)行操作。
同一套 API,操作所有數(shù)據(jù)源:
// 1. MySQL - 查用戶
// 生成 SQL: select * from user where id = 1
UserInfo user = template.lambdaQuery(UserInfo.class)
.eq(UserInfo::getId, 1)
.one();
// 2. MongoDB - 查日志
// 生成 Mondo Shell: db.user_log.find({ "user_id": 1 })
List<UserLog> logs = template.lambdaQuery(UserLog.class)
.eq(UserLog::UserId, 1)
.list();
// 3. Elasticsearch - 查商品
// 生成 DSL: { "query": { "term": { "name": "手機(jī)" } } }
List<Goods> goods = template.lambdaQuery(Goods.class)
.eq(Goods::getName, "手機(jī)")
.list();優(yōu)勢:
- 零學(xué)習(xí)成本:只要會(huì)寫 MySQL 查詢,就會(huì)寫 ES 查詢。
- 統(tǒng)一管控:所有的查詢(無論底層是 SQL 還是 DSL)都走統(tǒng)一的執(zhí)行鏈路,這意味著可以輕松實(shí)現(xiàn)統(tǒng)一的 SQL 耗時(shí)打印、慢查詢報(bào)警。
4. 結(jié)語:該如何選擇?
回到最初的問題:dbVisitor 和 MyBatis 你會(huì)怎么選?
- 堅(jiān)守 MyBatis 的理由:
- 你的項(xiàng)目是單純的 RDBMS(關(guān)系型數(shù)據(jù)庫) 項(xiàng)目,且團(tuán)隊(duì)成員對 MyBatis 倒背如流。
- 你需要極其精細(xì)的 SQL 控制能力,或者項(xiàng)目依賴了大量基于 MyBatis 的老舊插件(如特定的 PageHelper 版本)。
- 追求絕對的穩(wěn)健,認(rèn)為“也是一種美”,不愿意嘗試新框架。
- 擁抱 dbVisitor 的理由:
- 你的項(xiàng)目是 混合架構(gòu)(MySQL + MongoDB/ES),你受夠了在 Mapper、Repository 和各種 Client 之間精神分裂般的切換。
- 你厭倦了 MyBatis 繁瑣的 XML
<if>標(biāo)簽,渴望更現(xiàn)代、更簡潔的動(dòng)態(tài) SQL 寫法。 - 你需要 更高的靈活性,比如在 Service 層直接執(zhí)行 SQL,或者不寫 XML 直接用 Lambda 梭哈。
- 你是一個(gè)追求開發(fā)效率的“實(shí)用主義者”,希望用 20% 的代碼量完成 80% 的工作。
一句話總結(jié): 如果你想要 MyBatis 的 穩(wěn),請繼續(xù)使用 MyBatis;如果你想要 MyBatis 的穩(wěn),再加上 Spring Data 的 全 和 Lambda 的 快 ,那么 dbVisitor 是你當(dāng)下最好的選擇。
- 項(xiàng)目首頁:www.dbvisitor.net/
- 項(xiàng)目源碼:gitee.com/zycgit/dbvi…
到此這篇關(guān)于dbVisitor 和 MyBatis有什么區(qū)別的文章就介紹到這了,更多相關(guān)dbVisitor 和 MyBatis區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中的Semaphore信號量使用方法代碼實(shí)例
這篇文章主要介紹了Java中的Semaphore信號量使用方法代碼實(shí)例,Semaphore是一種基于計(jì)數(shù)的信號量,它可以設(shè)定一個(gè)閾值,基于此,多個(gè)線程競爭獲取許可信號,做自己的申請后歸還,超過閾值后,線程申請?jiān)S可信號將會(huì)被阻塞,需要的朋友可以參考下2023-11-11
詳解Spring Data JPA使用@Query注解(Using @Query)
本篇文章主要介紹了詳解Spring Data JPA使用@Query注解(Using @Query),具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
springMVC如何將controller中Model數(shù)據(jù)傳遞到j(luò)sp頁面
本篇文章主要介紹了springMVC如何將controller中Model數(shù)據(jù)傳遞到j(luò)sp頁面,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07

