请写一篇关于【MyBatis基础教程:Java持久层框架入门】的技术文章
凌晨一点半,我在回龙观出租屋里敲着代码,突然想回家了
上周五晚上,准确地说是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