MyBatis基础教程:从“手写JDBC”到“优雅持久化”的蜕变之路

栈里有风
2025-06-19 07:48
阅读 673

大家好,我是阿杰,一名全栈工程师,在后端服务和数据库交互方面有比较多的经验。今天想分享的是我在一个中型电商平台重构项目中,是如何一步步从传统的 JDBC 手动操作切换到使用 MyBatis 的过程。

这个过程中,我经历了数据层代码臃肿、SQL 与业务逻辑混杂、难以维护等真实挑战。希望通过这篇实战经验总结,能帮你少走弯路,快速掌握 MyBatis 的基础用法,并在日常开发中灵活运用。


初识MyBatis —— 为什么是它?

初识MyBatis —— 为什么是它?

事情得从一年前说起。当时我们公司正在对一个老电商系统做架构升级,原来的数据库交互方式全是通过裸 JDBC 写的 DAO 层,每个方法都充斥着 ConnectionPreparedStatementResultSet 这些底层对象。

说实话,那时候每天打开 DAO 类就像面对一团乱麻,特别是遇到复杂查询时,一堆 try-catch 嵌套让人根本不想维护。更糟糕的是,每次改动 SQL 都需要重新编译 Java 文件,效率低下不说,还容易出错。

我们团队最初也考虑过 Hibernate 和 JPA,但最终选择了 MyBatis,主要有几个理由:

  • 灵活性高:不强制 ORM 映射方式,适合我们这种已存在大量历史 SQL 的场景。
  • 性能可调优:可以精确控制 SQL 语句,避免了框架自动生成可能带来的性能问题。
  • 学习曲线平滑:对于熟悉 SQL 的开发者来说,上手快,文档丰富,社区活跃。

于是,我们在一次迭代中决定将用户中心模块作为试点,开始尝试使用 MyBatis 来重构这部分的 DAO 逻辑。


实战开整!第一个MyBatis接口实现

实战开整!第一个MyBatis接口实现

我们的第一个目标是把用户登录信息的获取接口从 JDBC 改造成 MyBatis 实现。原始的 JDBC 方法大概是这样的(简化版):

public User getUserById(int id) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;

    try {
        conn = dataSource.getConnection();
        ps = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
        ps.setInt(1, id);
        rs = ps.executeQuery();
        if (rs.next()) {
            User user = new User();
            user.setId(rs.getInt("id"));
            user.setUsername(rs.getString("username"));
            // ...其他字段设置
            return user;
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        closeResources(conn, ps, rs);
    }

    return null;
}

这看起来还算清晰,但在实际工程中,往往还会夹杂日志、异常处理、事务控制、参数校验等等,导致代码膨胀严重,可读性差。

接下来,我们引入MyBatis后的步骤如下:

  1. 添加MyBatis依赖(以Maven为例):
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.13</version>
</dependency>
  1. 配置MyBatis环境

创建一个 mybatis-config.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/shop"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>
  1. 编写Mapper接口
public interface UserMapper {
    User selectUserById(int id);
}
  1. 编写XML映射文件
    位置为 resources/mapper/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="selectUserById" resultType="com.example.model.User">
        SELECT *
        FROM user
        WHERE id = #{id}
    </select>
</mapper>
  1. 主函数测试调用
public class Main {
    public static void main(String[] args) throws IOException {
        String config = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(config);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            User user = mapper.selectUserById(1);
            System.out.println(user.getUsername());
        }
    }
}

短短几步之后,DAO 层变得简洁又直观,而且所有的 SQL 和 Java 对象绑定都在 XML 中统一管理,后续修改也方便多了。


踩坑实录:那些让我半夜抓头发的问题

当然,使用过程中也不可能一帆风顺,有几个常见的问题值得记录一下。

1. Mapper找不到?类路径配置没搞好!

刚开始的时候,我总是遇到 Invalid bound statement not found 的错误。排查了半天才发现是因为 MyBatis 没有扫描到 XML 文件或者接口路径不对。

解决方式:

  • XML 文件要放在 resources 目录下,并且配置中的 <mapper> 路径要对应。
  • Spring Boot 中可通过注解自动注册 Mapper,如:
@Mapper
public interface UserMapper { ... }

也可以在启动类加上:

@MapperScan("com.example.mapper")
@SpringBootApplication
public class Application {}

2. 自定义别名不起作用?

有时候我们不想每次都写长包路径,可以在 MyBatis 中定义别名:

<typeAliases>
    <typeAlias alias="User" type="com.example.model.User"/>
</typeAliases>

然后就可以在映射文件中直接写:

<select id="selectUserById" resultType="User">...</select>

注意:如果用了 Maven,要确保资源过滤没问题,否则 xml 文件里引用不到别名。

3. 动态SQL搞不定怎么办?

比如搜索用户时,经常会有多个条件动态组合的情况。这时候就要用到 <if><choose><trim> 标签。

举个例子:

<select id="searchUsers" resultType="User">
    SELECT * FROM user
    <where>
        <if test="username != null and username != ''">
            AND username LIKE CONCAT('%', #{username}, '%')
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
    </where>
</select>

上面这段 SQL 在 usernamestatus 为空时会自动忽略相应的条件,非常适合构建灵活查询接口。


性能优化和工程实践

作为一个注重性能的后端工程师,我也在实践中做了不少优化。

数据库设计建议

  • 表结构尽量范式合理,减少冗余字段。
  • 关键字段加索引,特别是常用于查询的字段。
  • 尽量避免 N+1 查询,可以通过 <collection> 或 join 方式一次性加载关联数据。

例如,我们要查用户及其订单列表:

<resultMap id="userWithOrdersResultMap" type="com.example.model.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="amount" column="amount"/>
    </collection>
</resultMap>

<select id="getUserWithOrders" resultMap="userWithOrdersResultMap">
    SELECT u.id, u.username, o.id as order_id, o.amount
    FROM user u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.id = #{userId}
</select>

这样就能在一个 SQL 中拿到主表和子表的数据,提升了性能。

接口设计层面的思考

  • 分页一定要支持,可以用 PageHelper 插件实现。
  • 返回结果类型要抽象,不建议直接暴露数据库实体类,应该使用 DTO 包装数据。
  • 缓存机制结合使用,MyBatis 本身支持二级缓存,但推荐还是用 Redis 更稳定可控。

效果评估与收益回顾

项目上线三个月后,我们对比了使用 MyBatis 前后的几个关键指标:

指标 JDBC时代 MyBatis时代
DAO类代码行数 平均300行/类 平均80行/类
SQL变更效率 修改Java代码 + 重新部署 只需修改XML
单元测试覆盖率 不足50% 提升至75%
新人学习成本 高(需理解JDBC细节) 低(只需理解MyBatis语法)

不仅开发效率显著提升,系统的可维护性和可扩展性也大大提高。后来在加入新模块时,直接沿用这套 MyBatis 结构,节省了大量的重复开发时间。


经验总结:给新手的一些建议

数据库设计模型-1

如果你刚接触 MyBatis,这里是我的几点小建议:

  1. 先学会 XML 编写,这是最基础也是最有灵活性的方式。
  2. 不要一开始就追求自动化 ORM,像 MyBatis Plus 可以后面慢慢引入。
  3. 多关注事务控制和连接池配置,尤其是生产环境中。
  4. 用好日志插件(如log4j、slf4j),方便跟踪 SQL 执行情况。
  5. 结合Spring Boot使用更加顺畅,现在几乎已经是标配了。

还有一个小感悟:其实工具和技术都是辅助手段,真正的核心永远是清晰的设计思维和良好的代码组织能力。MyBatis 只是个好用的锤子,但别忘了你是在建造一座房子,而不是单纯敲钉子。


后记

这篇文章算是我对 MyBatis 学习和实践经验的一个阶段性总结。如果你也在经历从手动 SQL 到使用 MyBatis 的转变,希望我的经历能给你带来一些启发。

最后送大家一句话:“好的代码不是写出来的,而是不断演进打磨出来的。”

技术的路上没有捷径,只有不断地动手、试错、复盘。愿你在编码的世界里找到属于自己的节奏与快乐 🚀

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝