MyBatis基础教程:Java持久层框架入门
从“手写JDBC”到MyBatis初体验:一个后端工程师的成长日记

2019年刚进公司那会儿,我被分配到了一个中小型电商平台的重构项目。说白了就是一个典型的Java后端项目,后台管理系统+订单系统+用户中心这些模块。当时我们整个团队都在用传统的JDBC方式操作数据库,每天的工作就是跟PreparedStatement和ResultSet打交道。
说实话那段日子真是挺痛苦的。写个简单的查询都要十几行代码,还要小心翼翼地处理各种资源释放的问题。更头疼的是,一旦表字段发生变动,几乎得把所有相关的DAO类都改一遍。有一次因为字段名拼错导致生产数据异常,排查了整整一天才定位到问题,真的是身心俱疲。
第一次直面持久层框架的需求
记得有次做商品管理模块重构的时候,产品经理突然提出要增加一个“多条件筛选搜索”的功能。本来以为是个简单需求,结果发现我们的基础架构根本没法支撑灵活查询。每个查询都是硬编码在DAO方法里的,新增一个筛选参数就得新增一个方法,代码膨胀得厉害不说,维护起来也特别费劲。
我当时就在想:“如果能把SQL和业务逻辑解耦就好了”。这时候老大推荐我去研究下MyBatis这个框架。他跟我说:“你试试看,说不定能解决你现在碰到的这些问题。”
探索之旅:从懵懂到渐入佳境
刚开始接触MyBatis的时候真是一脸懵。虽然之前听说过ORM框架,但实际用起来才发现完全不是那么回事。配置文件怎么写?映射关系怎么定义?还有那些奇怪的XML标签是干什么的?
我记得第一次试着把商品查询迁移到MyBatis时,光是写那个Mapper XML就折腾了好几个小时。一会儿是参数绑定出错,一会儿又是结果映射不正确。最离谱的是某个实体类字段名和表字段名不同,导致查出来的数据总是null,差点没把我心态搞崩。
不过真正上手之后才发现这东西确实好用。最直观的感受就是DAO层代码量大幅减少。以前几十行代码的查询现在只需要一个接口方法和一段XML配置就能搞定。而且通过注解的方式也能实现映射,这对于习惯了注解风格的我来说简直太友好了。
真正让我上头的几个特性
用了段时间之后,我发现有几个MyBatis的功能特别实用:
首先是动态SQL。这个真的救了我的命。特别是<if>、<choose>这些标签,在实现多条件查询时简直就是神器。以前那种根据条件拼接字符串的恶心做法终于可以退休了。
<select id="selectProduct" parameterType="map" resultType="Product">
SELECT * FROM product WHERE 1=1
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="categoryId != null">
AND category_id = #{categoryId}
</if>
</select>
其次是结果集自动映射。只要命名规范做得好,基本上不需要手动去set属性值了。对于嵌套对象的情况,还可以用<association>来处理关联查询,再也不用自己手动组装对象树了。
还有就是延迟加载功能。刚开始没注意这个细节,后来发现有些关联查询的数据其实并不是每次都需要。开启延迟加载后,确实感觉系统性能有所提升。
那些踩过的坑和学到的经验
当然使用过程中也不是一帆风顺的,遇到过不少坑:
第一个教训深刻的坑是在做批量插入时。刚开始直接循环insert语句,结果效率奇差。后来才知道可以用<foreach>标签配合INSERT INTO ... VALUES的语法来优化:
<insert id="batchInsert">
INSERT INTO user (username, email)
VALUES
<foreach collection="users" item="user" separator=",">
(#{user.username}, #{user.email})
</foreach>
</insert>
第二个印象深刻的教训来自N+1查询问题。当时有个接口需要查用户列表,并且显示每个用户的最近一条订单信息。初期实现时直接在循环里调用查询订单的方法,结果数据量一大就卡得不行。
后来学会了使用JOIN查询一次性获取数据,然后配合resultMap的手动映射来组装数据结构。虽然实现起来复杂点,但性能提升了好几个数量级。
第三个需要注意的地方是事务管理。刚开始没太在意,结果在做库存扣减这类操作时遇到了并发问题。后来才明白要在Service层统一控制事务边界,必要时加上适当的锁机制。
生产环境的一些运维经验
上线之后也积累了一些运维经验:
慢查询监控很重要。建议把MyBatis生成的实际SQL打印出来,结合数据库的慢查询日志进行分析。很多时候看似正常的查询,实际上执行计划可能并不理想。
缓存策略要考虑周全。MyBatis本身自带二级缓存,但在分布式环境下要注意数据一致性问题。我们最后选择了Redis来做应用级缓存,MyBatis只保留了一级缓存。
连接池配置要合理。初期设置的连接池大小偏小,高峰期经常出现等待现象。建议根据实际的QPS和平均响应时间来调整maxPoolSize等参数。
分库分表要提前规划。我们在系统运行一年后开始面临单表百万级别的数据压力。幸好前期设计时预留了sharding key的概念,后续拆分起来相对轻松。
给新手的一些建议
如果你也是刚接触MyBatis的新手,我有几点建议想分享:
别一开始就追求全自动。虽然现在很多框架(比如Spring Data JPA)可以做到自动建表、自动生成CRUD方法,但强烈建议先手动写一段时间Mapper,这样能更深入理解底层原理。
重视命名规范。不管是表字段还是Java属性,最好都遵循统一的命名规范。这样可以最大限度利用MyBatis的自动映射能力,减少配置工作量。
善用工具。像MyBatis-Generator这样的代码生成工具确实能提高效率,但记住它只是辅助工具。生成的代码一定要仔细检查,尤其是关联查询部分。
关注性能。虽然MyBatis封装了JDBC,但不代表可以随意写低效的SQL。定期做执行计划分析,及时优化慢查询。
做好错误处理。特别是在生产环境中,建议对MyBatis抛出的异常做统一处理,避免将敏感信息暴露给前端。
这些年的成长感悟
回过头来看看这几年,从当初那个连ResultHandler都不会用的小白到现在能够独立设计数据访问层架构,真的是感慨万千。
我觉得技术学习最重要的就是要动手实践。不管你看多少教程、读多少源码,都不如亲自写一个完整的CRUD功能来得实在。每解决一个问题,你的技能树就会点亮一片新的区域。
还有一个重要的体会就是选择合适的技术比追求新技术更重要。MyBatis虽然不是最新的框架,但它成熟稳定、生态完善,在绝大多数项目中都能很好地胜任。比起追风口,我更愿意在熟悉的工具上下功夫,挖掘它的最大价值。
说到未来,我也在关注一些新的趋势,比如Native SQL vs ORM之争、DSL查询构建器的发展等。但我坚信,无论技术如何演进,扎实的基础功底永远是最宝贵的财富。
如果你正在为选择哪个持久层框架而纠结,或者对MyBatis有一些疑问,欢迎随时找我交流。我很乐意分享自己的经验和想法,毕竟技术这条路,本来就应该是互相学习、共同进步的。

评论 0