请写一篇关于【MyBatis基础教程:Java持久层框架入门】的技术文章

赵玉
2026-03-12 06:27
阅读 1065

凌晨一点半,我在回龙观出租屋里敲着代码,突然想回家了

上周五晚上,准确地说是2024年3月15日,凌晨1点28分,我瘫在回龙观那张吱呀作响的二手电脑椅上,盯着屏幕里一堆报错的MyBatis配置文件,脑子里却在盘算:要不要回老家?

窗外偶尔传来几声远处京藏高速上的卡车轰鸣,屋里只有机械键盘的咔嗒声和空调外机的嗡嗡声。老婆已经睡了——其实是我们俩轮流“值夜班”,她白天带娃,我晚上改bug。房贷每月9860元,房租3500(对,我们还没资格住自己的房),孩子奶粉一个月2000多,再加上各种杂七杂八……我这月薪22k看起来光鲜,实则月光都算奢侈。

就在这时,IDEA又弹出一个红叉:Invalid bound statement (not found): com.example.mapper.UserMapper.selectUserById

我叹了口气,灌了口早已凉透的速溶咖啡,心想:也许回老家,真的没那么差?


一切还得从那个被Spring Data JPA坑惨的项目说起

时间倒回去年十月,公司接了个新项目,老板拍脑袋说要用“最新技术栈”,于是后端选型直接上了Spring Boot + Spring Data JPA。我当时心里就咯噔一下——虽然JPA听起来高大上,但咱们这种中小厂,数据库表动不动就加字段、改关联,JPA那套“对象先行”的思路,简直是在给自己挖坑。

果不其然,上线前三天,产品经理跑来说:“用户表要加个‘是否VIP’字段,顺便把订单关联改成一对多。”我看着Entity类里那一堆@OneToMany@JoinColumn,差点当场表演一个原地爆炸。

更惨的是,为了查个复杂报表,我硬是写了三层嵌套的JPQL,结果性能差到DBA找上门来:“兄弟,你这SQL扫全表了,再这么搞,MySQL主库要挂!”

那天晚上,我在地铁13号线上站了整整两小时(早高峰+晚高峰双重暴击),脑子里只有一个念头:得换持久层框架了。


MyBatis:那个“写SQL自由”的老伙计

其实我对MyBatis并不陌生。刚入行那会儿(2017年,月薪才8k),公司用的就是它。那时候觉得写XML好麻烦啊,动不动就要配resultMap,还不如直接JDBC来得痛快。

但人到中年,经历多了才明白:自由是有代价的,而MyBatis恰恰给了你“可控的自由”。

于是我开始偷偷在本地搭了个Demo项目,想重新拾起这个“老朋友”。打开IDEA,新建Maven工程,引入依赖:

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

看到这里你可能会笑:这不就是最基础的依赖吗?但对我这个每天被K8s、Docker、微服务搞得头大的北漂程序员来说,能回到“一个jar包搞定”的时代,竟有种返璞归真的感动。


第一步:别被XML吓到,它其实很温柔

很多人一听MyBatis就要写XML,立马退缩。但其实,XML只是MyBatis的一种映射方式,你甚至可以用注解(虽然我不推荐复杂场景用)。

我建了个简单的User类:

public class User {
    private Long id;
    private String name;
    private Integer age;
    // getter/setter 省略
}

然后写Mapper接口:

public interface UserMapper {
    User selectUserById(Long id);
}

重点来了——对应的XML文件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 id, name, age FROM user WHERE id = #{id}
    </select>
</mapper>

注意看namespace必须和接口全路径一致,id必须和方法名一致。就这么简单,MyBatis就能把SQL结果自动映射成Java对象。

我当时在出租屋的小桌上敲完这段代码,运行测试,控制台输出了正确的User对象——那一刻,我居然有点小激动。不是因为技术多牛,而是因为“掌控感”回来了。


第二步:处理复杂查询?resultMap来救场

当然,现实中的SQL哪有这么简单。比如我们有个需求:查用户及其订单列表。

这时候就得用resultMap了:

<resultMap id="UserWithOrdersResultMap" type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="amount" column="amount"/>
    </collection>
</resultMap>

<select id="selectUserWithOrders" resultMap="UserWithOrdersResultMap">
    SELECT 
        u.id as user_id, 
        u.name as user_name,
        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>

这里的关键是列别名(如user_id)和resultMap里的column对应。很多初学者在这里翻车,其实只要记住:SQL返回的列名,必须和resultMap里的column匹配。

我第一次写这个的时候也错了,把u.id as user_id写成了u.id,结果orders列表全是null。调试了半小时才发现问题——这种“低级错误”在高压环境下特别容易犯,尤其是在你刚开完一个扯皮的需求评审会之后。


第三步:动态SQL,告别字符串拼接地狱

说到动态查询,MyBatis的<if><foreach>简直是神器。

比如搜索用户,可能按名字、年龄、城市等多个条件组合:

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

注意<where>标签会自动去掉第一个AND,避免语法错误。这比你在Java里写StringBuilder拼SQL安全多了——至少不会被SQL注入搞到半夜被叫起来救火。

有一次我前同事就因为手拼SQL,被人传了个'; DROP TABLE user; --,虽然没真删库(还好有权限控制),但被CTO骂了整整一周。从此我们组立下规矩:禁止手拼SQL,违者请全组喝奶茶。


资源有限?别慌,这些免费工具帮你起飞

作为背着房贷的打工人,我深知“资源”二字有多重。公司不给买正版IDE插件,培训预算为零,甚至连技术大会门票都要自己掏腰包。

但好消息是:MyBatis生态里有很多免费且好用的资源。

  • MyBatis Generator:自动生成Mapper、Model、XML,省下80% CRUD代码。
  • MyBatis-Plus:如果你觉得原生MyBatis还是太啰嗦,这个增强工具能让你用Lambda表达式写查询,还自带分页插件。
  • 官方文档https://mybatis.org/mybatis-3/zh/index.html —— 写得清晰又详细,关键是免费

另外,最近我还发现一个叫DeepSeek的国产大模型(不是广告,纯个人体验),它对Java和MyBatis的理解相当到位。有次我问它:“如何优化MyBatis的一对多查询N+1问题?”它不仅给出了<collection>的fetchType="eager"方案,还提醒我注意内存溢出风险。

虽然AI不能替代思考,但在你深夜加班、脑子转不动的时候,有个“懂技术的聊天机器人”确实能救命。


回老家?也许不必非此即彼

写到这里,天已经快亮了。窗外传来早班地铁的呼啸声,老婆翻了个身,嘟囔了一句:“还不睡?”

我保存了代码,关掉IDEA,走到阳台点了根烟(戒了三年,最近压力大又捡起来了)。

其实纠结回不回老家,本质上是在问:我还能不能在这座城市找到“技术的价值感”?

在北京,我每天挤地铁两小时,写业务代码像流水线工人;但在老家,可能月薪只有12k,却能准点下班陪孩子长大。

可MyBatis教会我的一件事是:框架的选择没有绝对好坏,只有适不适合当前场景。

也许人生也一样。我不必现在就做决定。我可以先把手头这个用MyBatis重构的项目做好,争取涨薪;也可以周末抽空研究分布式事务,提升不可替代性;甚至可以尝试接点外包,看看远程工作的可能性。

技术人的优势在于:我们的“生产资料”是一台电脑和一个脑子。只要持续学习,哪里都能扎根。


最后几句掏心窝子的话

如果你也是个正在挣扎的程序员,不管是北漂、沪漂还是广漂,我想说:

  • 别怕回头看老技术。MyBatis虽“老”,但稳定、灵活、社区成熟,依然是企业级开发的中坚力量。
  • 别被“新技术焦虑”绑架。Spring Cloud Alibaba、Service Mesh、云原生……这些东西很重要,但前提是你的CRUD先写稳了。
  • 善用免费资源。官方文档、GitHub开源项目、技术博客、甚至像DeepSeek这样的AI助手,都是你的杠杆。
  • 最重要的是:别让工作吃掉你的生活。我见过太多人为了“奋斗”熬垮身体,最后连看病的钱都要贷款。

回到开头那个报错——后来我发现是因为mybatis-config.xml里没注册Mapper XML路径。加上这一行就搞定了:

<mappers>
    <mapper resource="com/example/mapper/UserMapper.xml"/>
</mappers>

有时候,解决问题的关键,就藏在一个被忽略的配置里。

就像人生的选择,答案可能不在“留下”或“离开”的二元对立中,而在你是否愿意静下心,一行一行地检查自己的“配置文件”。


写完这篇文,已经是早上6点。孩子快醒了,我得去冲奶粉了。

至于回不回老家?下周和老婆认真聊聊吧。但不管在哪,我都会继续写Java,继续和MyBatis打交道——因为这是我的饭碗,也是我的热爱。

共勉。

评论 0

最热最新
暂无评论
赵玉Lv.1
0
影响力
0
文章
0
粉丝