你是否经历过这样的绝望:
为了给一个简单的查询加个 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 |
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. 获取 TemplateJdbcTemplate 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:没问题马上安排!
@SimpleMapperpublic 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 接口注解中用@SimpleMapperpublic 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) - 面向 MapperuserMapper.selectById(1L);// 2. MongoDB (Spring Data) - 面向 Repository/TemplateQuery 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 = 1UserInfo 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% 的工作。