你是否经历过这样的绝望:为了给一个简单的查询加个 status != -1 的条件,你不得不在 xml 里写一堆 <if> 标签? 或者,为了同时查询 mysql 和 mongodb,你的 service 层里充斥着风格迥异的 usermapper 和 mongotemplate 代码?
mybatis 很好,陪伴了我们十年。它稳定、强大,是 java 该领域的绝对霸主。但面对现在的多数据源架构、轻量化开发需求,这艘巨轮是不是显得有点“重”了?
dbvisitor 的诞生并非为了重复造轮子,而是为了在保持 mybatis 核心开发习惯(mapper/xml)的同时,解决那些让你头疼已久的痛点。
| 功能体验 | mybatis | mybatis-plus | dbvisitor | 备注 |
|---|---|---|---|---|
| mapper 接口开发 | ✅ | ✅ | ✅ | 都支持定义 interface 自动代理 |
| xml sql 定义 | ✅ | ✅ | ✅ | 标签结构高度兼容 (resultmap/select...) |
| 注解 sql 定义 | ✅ | ✅ | ✅ | @select / @insert 等注解支持 |
| dto/bean 映射 | ✅ | ✅ | ✅ | 自动驼峰转换、resultmap 映射 |
| lambda 链式调用 | ❌ | ✅ | ✅ | 类型安全的 crud 构建器 |
| 分页查询 | ❌ | ✅ | ✅ | 直接在 api 或 sql 中控制分页 |
| 即时 sql 执行 | ❌ | ❌ | ✅ | 无需定义 mapper 也能直接运行 sql |
| 动态规则 (rules) | ❌ | ❌ | ✅ | 脚本式动态 sql @{and ...} |
| nosql 支持 | ❌ | ❌ | ✅ | 一套 api 操作 mysql/mongo/es |
1. 熟悉的配方
通过对比 dbvisitor 和 mybatis 的 xsd 定义文件,可以发现惊人的相似性(两者都以 <mapper> 为根节点, 核心标签 <select>, <insert> 完全一致)。 这意味着:mybatis 开发者无需阅读文档,凭借直觉即可编写 dbvisitor 的 xml 文件,甚至可以直接复用现有的 xml 文件。接下来让我们详细看看两者在经典用法上有何差异?
1.1 mapper 接口开发
dbvisitor 沿用了 mapper 接口的开发模式,对于习惯了 mybatis 的开发者来说几乎没有学习成本。
mybatis
使用 @mapper 标记接口,并配合 @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 标记接口,使用 @query 代替 @select(语义更宽泛,涵盖所有查询),其他如 @insert 等保持一致。
@simplemapper
public interface usermapper {
// 对应 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 在结构和核心标签上高度一致。
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)的场景下,映射方式的繁简程度决定了开发效率。
mybatis (xml 派)
mybatis 倾向于通过 xml 中的 resultmap 来解决字段映射问题。虽然开启驼峰配置 (mapunderscoretocamelcase) 能解决简单场景,但面对差异较大的字段名,开发者不得不编写冗长的 resultmap 标签。
<!-- 必须显式定义映射关系 -->
<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 引入了注解驱动,允许直接在实体类上定义映射关系,从而摆脱对 resultmap 的依赖。
public class user {
@tablefield("user_name") // 使用注解解决映射
private string name;
}dbvisitor (全兼容)
dbvisitor 选择了 “我全都要” 的兼容策略,你可以在三种模式中任意切换:
- 自动映射(默认):内置智能的驼峰转换,90% 的场景无需任何配置。
- 注解模式(mp 风格):使用
@column注解定义特定字段,无需 xml。public class user { @column("user_name") // 类似 mp 的 @tablefield private string name; } - resultmap 模式(mybatis 风格):完全兼容 mybatis 的
<resultmap>标签。如果你正在迁移旧项目,或者需要非常复杂的嵌套映射,直接把旧的 xml 拿来用即可。<!-- 完全兼容 mybatis 语法的 resultmap --> <resultmap id="usermap" type="com.example.user"> <result column="user_name" property="name"/> </resultmap>
1.5 lambda 链式调用
dbvisitor 提供了一套类型安全的 lambda 构建器,它的核心价值不仅仅是像 mybatis-plus 那样避免手写 sql,更在于它实现了 "one api access any database" 的理念。 无论底层是 mysql、oracle 还是 mongodb、elasticsearch,你都可以使用同一套 lambda api 进行操作。这意味着你的团队只需要掌握一种 crud 语法。
用法对比
- mybatis-plus:强依赖
mapper接口,api 设计主要面向 sql 数据库。 - dbvisitor:基于
lambdatemplate,无需定义接口即可直接使用。
// 统一入口:无论操作什么数据库,起点都是 template new 出来即可 jdbctemplate template = ...;
场景 1:标准 sql 查询 (mysql/pg)
// 使用 java 方法引用避免字段拼写错误
userinfo user = template.lambdaquery(userinfo.class)
.eq(userinfo::gettype, "admin")
.gt(userinfo::getage, 18)
.list();
场景 2:统一关系型非关系型数据库
神奇之处在于,完全相同的代码,也可以用于操作 elasticsearch 或 mongodb,dbvisitor 会自动将查询条件转译为对应的 dsl。
// 操作 elasticsearch (自动转换为 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();
这种统一性极大地降低了多数据源混合架构的维护成本。
2. 拒绝 xml 地狱
2.1 即时 sql 执行
即时 sql 执行能力不仅是 “能执行 sql”,更代表了框架在不同抽象层级上的灵活度。dbvisitor 允许你在三种层级上自由切换,无需被 xml 束缚。
a. 纯血 sql
这是 dbvisitor 最基础的模式。它高度复刻了 spring jdbc 的能力(如 queryforlist, query, execute 等)
// 1. 获取 template
jdbctemplate jdbc = ...;
// 2. 直接执行 sql,利用 dbvisitor 强大的 mapper 映射结果
list<userinfo> users = jdbc.queryforlist(
"select * from user where age > ?", userinfo.class, 18);
// 3. 甚至执行脚本
jdbc.execute("delete from user where age < 10");b. lambda api 上使用 sql
lambdatemplate 虽然主打类型安全的构建器,但它同样允许你在 lambda 中直接执行原生 sql。
lambdatemplate lambda = ...;
lambda.jdbc().queryforlist(
"select * from user where status = ?", user.class, 1);c. mapper 接口上使用 sql
这是 mybatis 及 mybatis-plus 不具备的,同时也被诟病最多的地方。
- 用户:我就是想用一下 sql 不要给我搞注解、搞 xml 好不好!
- mybatis/mp:抱歉不行!
- dbvisitor:没问题马上安排!
@simplemapper
public interface usermapper extends basemapper<userinfo> {
// 方式 1: 传统的注解模式
@query("select * from user where type = #{type}")
list<userinfo> findbytype(@param("type") string type);
// 方式 2: default 方法 + 内置 jdbc 能力
// 适合:逻辑复杂,不想写 xml,但又想封装在 mapper 里的场景
default list<userinfo> findactiveusers() {
return this.jdbc().queryforlist(
"select * from user where active = 1", userinfo.class);
}
}2.2 动态规则
本质是对动态 sql 拼接的一种创新解决方案。它改变了 mybatis 系列中 <if> 标签泛滥的问题。
- mybatis 系列:依赖 xml 标签(
<if>,<where>,<choose>)来实现动态 sql,导致 xml 结构臃肿且难以维护。 - dbvisitor:引入
@{...}语法,将条件动态嵌入 sql 字符串中,极大简化了动态 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);
}
优势总结
- 极简主义:没有
<if>,没有wrapper,只有纯粹的 sql。 - 直观:看一眼就知道这段 sql 包含哪些条件逻辑。
- 复用性:这行 sql 字符串可以写在 xml 里,也可以写在注解里,甚至写在纯血 sql 里面。
3. 一套 api 统一访问
仅仅十年前,一个 mysql 可能就承载了一个系统的所有数据。但现在的应用架构往往是混合的。
❌ mybatis/mp 的困境:割裂
mybatis 设计之初就是为了 rdbms(sql)服务的。在一个混合架构(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 构建
searchrequest request = new searchrequest("goods");
request.source(new searchsourcebuilder().query(querybuilders.matchquery("name", "手机")));
client.search(request, requestoptions.default);痛点:
- 认知负荷高:团队需要掌握 3 套不同的 api 风格(mapper, criteria, builder)。
- 维护困难:无法统一处理事务、日志监控和异常体系。
- 代码割裂:业务逻辑中充斥着不同风格的 dal 代码。
✅ dbvisitor 的解法:统一
dbvisitor 通过双层适配器设计实现了 “one api access any database”。无论是关系型数据库还是 nosql,你都可以用 完全相同的 lambda 语法 进行操作。
同一套 api,操作所有数据源:
// 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": "手机" } } }
list<goods> goods = template.lambdaquery(goods.class)
.eq(goods::getname, "手机")
.list();优势:
- 零学习成本:只要会写 mysql 查询,就会写 es 查询。
- 统一管控:所有的查询(无论底层是 sql 还是 dsl)都走统一的执行链路,这意味着可以轻松实现统一的 sql 耗时打印、慢查询报警。
4. 结语:该如何选择?
回到最初的问题:dbvisitor 和 mybatis 你会怎么选?
- 坚守 mybatis 的理由:
- 你的项目是单纯的 rdbms(关系型数据库) 项目,且团队成员对 mybatis 倒背如流。
- 你需要极其精细的 sql 控制能力,或者项目依赖了大量基于 mybatis 的老旧插件(如特定的 pagehelper 版本)。
- 追求绝对的稳健,认为“也是一种美”,不愿意尝试新框架。
- 拥抱 dbvisitor 的理由:
- 你的项目是 混合架构(mysql + mongodb/es),你受够了在 mapper、repository 和各种 client 之间精神分裂般的切换。
- 你厌倦了 mybatis 繁琐的 xml
<if>标签,渴望更现代、更简洁的动态 sql 写法。 - 你需要 更高的灵活性,比如在 service 层直接执行 sql,或者不写 xml 直接用 lambda 梭哈。
- 你是一个追求开发效率的“实用主义者”,希望用 20% 的代码量完成 80% 的工作。
一句话总结: 如果你想要 mybatis 的 稳,请继续使用 mybatis;如果你想要 mybatis 的稳,再加上 spring data 的 全 和 lambda 的 快 ,那么 dbvisitor 是你当下最好的选择。
- 项目首页:www.dbvisitor.net/
- 项目源码:gitee.com/zycgit/dbvi…
到此这篇关于dbvisitor 和 mybatis有什么区别的文章就介绍到这了,更多相关dbvisitor 和 mybatis区别内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论