找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1775

积分

0

好友

290

主题
发表于 14 小时前 | 查看: 0| 回复: 0

你是否经历过这样的绝望:为了给一个简单的查询加个 status != -1 的条件,你不得不在 XML 里写一堆标签?或者,为了同时查询 MySQL 和 MongoDB,你的 Service 层里充斥着风格迥异的 UserMapperTemplate 代码?

MyBatis 很好,陪伴了我们十年。它稳定、强大,是 Java 该领域的绝对霸主。但面对现在的多数据源架构、轻量化开发需求,这艘巨轮是不是显得有些“重”了呢?

dbVisitor 的诞生并非为了重复造轮子,而是在保持 MyBatis 核心开发习惯(Mapper/XML)的同时,致力于解决那些让开发者头疼已久的痛点。下面的表格可以让你快速了解它与 MyBatis、MyBatis-Plus(MP)的核心差异。

功能体验 MyBatis MP dbVisitor 备注
Mapper 接口 都支持定义 Interface 自动代理
XML SQL 定义 标签结构高度兼容 (ResultMap/select...)
注解 SQL 定义 @Select / @Insert 等注解支持
Bean 映射 自动驼峰转换、ResultMap 映射
Lambda 链式 类型安全的 CRUD 构建器
分页查询 直接在 API 或 SQL 中控制分页
即时 SQL 执行 无需定义 Mapper 也能直接运行 SQL
动态规则 脚本式动态 SQL @{and ...}
NoSQL 支持 一套 API 操作 MySQL/Mongo/ES

熟悉的配方

通过对比 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 选择了“我全都要”的兼容策略,你可以在三种模式中任意切换。

// 注解使用 Column
public class User{
    @Column("user_name") // 类似 MP 的 @TableField
    private String name;
}

ResultMap 模式(MyBatis 风格)
完全兼容 MyBatis 的标签。如果你正在迁移旧项目,或者需要非常复杂的嵌套映射,直接把旧的 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,无需定义接口即可直接使用,API 设计主要面向 CRUD 场景。

场景 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();

这种统一性极大地降低了多数据源混合架构的维护成本。

拒绝 XML 地狱

2.1 即时 SQL 执行

即时 SQL 执行能力不仅是“能执行 SQL”,更代表了框架在不同抽象层级上的灵活度。dbVisitor 允许你在三种层级上自由切换,无需被 XML 束缚。

A. 纯血 SQL
这是 dbVisitor 最基础的模式。它高度复刻了 Spring JDBC 的能力(如 queryForListqueryexecute 等),有了它就可以把 dbutils 扔掉了。

// 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 查询 DTO 不要搞注解、搞 XML 好不好!
  • MyBatis:抱歉不行!
  • MyBatis-Plus:要不你换成 Map 用我的 SqlRunner
  • 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 系列

<!-- 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
引入 @{...} 语法,在支持相同标签情况下将条件动态嵌入 SQL 字符串中,极大简化了动态 SQL 的编写。彻底解决 MyBatis/MyBatis-Plus 在使用过程中 XML 标签泛滥的问题。

<!-- 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 里面。

一套 API 统一访问数据库

仅仅十年前,一个 MySQL 可能就承载了一个系统的所有数据。但现在的应用架构往往是混合的。因此 MyBatis/MP 遇到了 “割裂” 的困境。

例如在一个混合架构(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);

dbVisitor 通过双层适配器实现了 “One API Access Any DataBase”。无论是关系型数据库还是 NoSQL,你都可以用 完全相同的 Lambda 语法 进行操作。

// 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)都走统一的执行链路。
  • 减轻认知负荷:从掌握 3 套不同的 API 风格,降低为 1 套 API。

结语:该如何选择?

回到最初的问题,我用一句话总结:如果你想要 MyBatis 的 ,请继续使用 MyBatis;如果你想要 MyBatis 的 ,再加上 Spring Data 的 和 Lambda 的方便,并且你还想在如今多数据架构下游刃有余,那么 dbVisitor 是你当下值得尝试的选择。

对于追求技术效率和统一性的开发者,dbVisitor 提供了清晰的演进路径。你可以将它理解为一个增强版、现代化的数据访问层工具。想要了解更多关于类似 dbVisitor 这样的前沿开源项目实战与应用,欢迎到 云栈社区 进行交流探讨。




上一篇:基于YOLOv8与ESP32的AI宠物监控系统:从摄像头到手机推送完整实现
下一篇:告别iPhone一个月:我的31天诺基亚数字排毒实验,更健康还是更焦虑?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-25 21:04 , Processed in 0.249997 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表