MyBatis基础教程:Java持久层框架入门
上周五晚上十一点,杭州下着小雨,我瘫在工位上盯着屏幕上第37次报错的SQL语句,脑子里只剩下一个念头:“要不转行去卖煎饼吧?”
别误会,我不是被产品经理又改需求气的(虽然他确实刚把“用户登录”从“三天内完成”改成了“今晚上线”),而是被自己写的JDBC代码折磨疯了。每个DAO方法里都是try-catch-finally三件套,ResultSet遍历写得比八股文还规整,结果一个字段名拼错,线上直接500。
说来惭愧,作为一个在阿里和网易之间反复横跳、自称“全栈但主要写后端”的老油条,我一直对MyBatis这种ORM框架嗤之以鼻。以前总觉得:“手写SQL多爽,控制力强,性能好,还能装X。” 直到去年双11期间,我们服务因为DAO层膨胀到2000+行,一个简单的商品查询接口拖慢了整个链路,CTO在晨会上点名:“再不用ORM,就用你的人头祭天。”
那一刻,我终于理解了什么叫“真香”。
为什么是MyBatis?而不是Hibernate、Spring Data JPA,甚至Python的Django ORM?
说到持久层框架,很多人第一反应是对比。毕竟咱们程序员选型,不吵一架都不算完整。
| 框架 | 语言 | 抽象程度 | 灵活性 | 学习曲线 | 杭州大厂使用率 |
|---|---|---|---|---|---|
| Hibernate | Java | 高(全自动) | 低(HQL限制多) | 陡峭 | 中(偏传统金融) |
| Spring Data JPA | Java | 中高 | 中 | 中等 | 高(尤其Spring Boot项目) |
| MyBatis | Java | 中(半自动) | 高(直接写SQL) | 平缓 | 极高(阿里系最爱) |
| Django ORM | Python | 高 | 低 | 平缓 | 低(非主流) |
| Sequelize / TypeORM | JavaScript | 中 | 中 | 中 | 极低(前端主导) |
你看,Java生态里,MyBatis几乎成了杭州互联网公司的标配——尤其是阿里系。为啥?因为我们既要“敏捷开发”,又要“精细调优”。产品经理今天要加个模糊搜索,明天要联表查用户行为日志,后天还得支持分库分表。这时候,Hibernate那种“你信我,我帮你生成SQL”的哲学就显得有点天真了。
而MyBatis呢?它说:“兄弟,SQL你写,映射我来干。” 这不就是成年人的爱情吗?——给自由,也给兜底。
被逼上梁山:我的第一个MyBatis项目
事情起因很简单:公司新接了个ToB项目,要求两周内搭出核心数据模型。数据库设计完,我看着那张包含user_info、order_record、payment_log三张主表的ER图,心想:“完了,又要写80个DAO方法。”
但这次我学乖了。打开IDEA,新建Maven项目,pom.xml里加上:
<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>
然后建了个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/myapp?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
接着,写个实体类User.java:
public class User {
private Long id;
private String username;
private String email;
// getter/setter 略
}
重点来了——UserMapper.xml,这才是MyBatis的灵魂:
<?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="findById" parameterType="long" resultType="com.example.model.User">
SELECT id, username, email FROM user_info WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.example.model.User">
INSERT INTO user_info (username, email) VALUES (#{username}, #{email})
</insert>
</mapper>
最后,在Java代码里调用:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.findById(1L);
System.out.println(user.getUsername()); // 终于不用手动setXXX了!
}
跑起来那一刻,我差点哭出来——再也不用手动resultSet.getString("username")了!
踩坑实录:那些让我想砸键盘的瞬间
当然,真香之前总有阵痛。
坑1:XML里的&要转义
第一次写带条件的SQL:
SELECT * FROM user WHERE status = 1 AND create_time > '2023-01-01'
结果报错:The entity name must immediately follow the '&' in the entity reference.
后来才知道,XML里&要写成&。现在我看到&就PTSD。
坑2:驼峰命名 vs 下划线命名
数据库字段是create_time,Java属性是createTime,结果查出来是null。
解决方案:在config里加一行:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
坑3:事务没生效
测试插入两条记录,中间抛异常,结果第一条还是入库了。
原来MyBatis默认不开启事务,得手动:
SqlSession session = sqlSessionFactory.openSession(false); // 关闭自动提交
try {
mapper.insertUser(user1);
mapper.insertUser(user2);
session.commit(); // 手动提交
} catch (Exception e) {
session.rollback(); // 回滚
}
这些坑,每一个都让我深夜对着屏幕骂一句:“早知道用Python了!” —— 但冷静下来想想,Python的Django ORM虽然爽,可一旦要写复杂报表、多表关联、分页优化,照样得原生SQL伺候。语言不是银弹,场景才是。
为什么Java + MyBatis 在“代码人生”中更稳?
说实话,我试过用Node.js写后端,也用Flask搭过小工具。但当你面对百万级QPS、需要精确控制连接池、还要对接Kafka和Flink的时候,Java的生态厚度和稳定性,真的不是闹着玩的。
MyBatis在这其中扮演的角色,就像一个“懂你的老伙计”:
- 它不替你做决定(不像Hibernate自作聪明生成N+1查询)
- 它允许你写最原始的SQL(性能调优时救命)
- 它和Spring无缝集成(
@MapperScan一行搞定)
更重要的是,在杭州这片卷王之地,会MyBatis几乎等于拿到了面试通行证。我上周帮朋友内推,对方问的第一句就是:“MyBatis的#{}和${}区别能说说吗?”(答案:前者预编译防SQL注入,后者直接拼接——别用后者!)
写在最后:从抵触到拥抱,是成长的开始
曾经我以为,手写SQL是“技术硬核”的象征。
现在我明白,真正的硬核,是在合适的场景用合适的工具,把事情高效、稳定、可维护地做完。
MyBatis不是银弹,但它足够灵活、足够透明、足够接地气。它不会让你写出炫酷的函数式代码,也不会让你在GitHub上收获999 stars,但它能让你在凌晨三点改完Bug后,安心回家睡觉——而不是担心明天线上炸了。
所以,如果你还在手写JDBC,或者对ORM框架充满偏见,不妨试试MyBatis。
说不定某天深夜,你也会像我一样,盯着成功返回的JSON数据,喃喃一句:
“这玩意儿……真香。”
(完)
P.S. 产品经理刚发消息说,下周要加Redis缓存。算了,先睡吧,代码人生,且行且珍惜。

评论 0