MyBatis入门踩坑记:一个寒假后遗症患者的自救指南

SailTrack
2026-03-13
点 赞
0
热 度
4
评 论
0
  1. 首页
  2. 学习
  3. MyBatis入门踩坑记:一个寒假后遗症患者的自救指南

写在前面
说实话,坐教室最后一排真的听不清老师在讲啥,再加上寒假玩了一个月,开学回来发现JavaWeb的知识点都快忘光了。昨天晚上和今天刚好没课,我硬着头皮把第一章和第二章的PPT翻出来看,结果在MyBatis这里就卡了半天。这篇笔记就是我边看边试、边报错边改的真实记录,希望能帮到和我一样"寒假后遗症"的同学。

一、为什么我要学MyBatis?——被JDBC折磨的痛

刚开始看PPT的时候,我还在想:“JDBC不是挺好的吗?为啥要搞个MyBatis?”

然后我就想起了上学期期末设计时用的JDBC有多折磨...

被这三个问题折磨到想哭:

1. SQL写死在代码里真的麻烦

// 每次改个字段都要重新编译,我昨天就因为这个浪费了半小时

String sql = "SELECT id, name, age FROM employee WHERE id = ?";

改个表结构或者加个字段,整个Java文件都要重新编译,调试起来特别痛苦。

2. 连接管理太容易出错了

// 我昨天就忘了关连接,数据库直接连不上了

Connection conn = null;

try {

    conn = DriverManager.getConnection(...);

    // ... 执行SQL

} finally {

    if (conn != null) conn.close(); // 这行经常忘记写!

}

特别是嵌套调用的时候,经常搞不清楚哪个连接该关,哪个不该关。

3. 结果集解析写到手抽筋

// 字段一多,这代码看得我眼睛都花了

二、三大核心对象——我卡壳的地方

这部分我看了PPT好几遍都没搞明白,后来自己动手写代码才理解。让我用大白话解释一下:

SqlSessionFactoryBuilder(一次性工具人)

  • 就是个"建筑工人",盖完房子就走人了

  • 只在程序启动的时候用一次,创建完SqlSessionFactory就可以扔了

  • 我的踩坑经历:一开始我以为这个要一直留着,结果内存占用特别高

SqlSessionFactory(餐厅老板)

  • 这个要全局只有一个,做成单例

  • 为什么?因为创建它要读配置文件、初始化连接池,特别耗资源

  • 我的理解:就像开餐厅,你不可能每来一个客人就重新装修一次餐厅吧?

SqlSession(服务员)

  • 每次操作数据库都要叫一个新的服务员

  • 用完必须让他下班(close),不然会占用数据库连接

  • 血泪教训:我昨天测试的时候没关SqlSession,跑了几次测试后数据库连接池就满了,整个应用都连不上数据库了!

完整的代码流程(我调试了5次才跑通):

// 第一次写的时候reader没关,第二次factory重复创建...
Reader reader = null;
SqlSession session = null;
try {
    reader = Resources.getResourceAsReader("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
    session = factory.openSession(true); // autoCommit=true
    
    Employee emp = session.selectOne("com.dray.pojo.EmployeeMapper.findById", 1);
    System.out.println(emp.getName());
    
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (session != null) session.close(); // 这行绝对不能忘!
    if (reader != null) {
        try { reader.close(); } catch (IOException e) {}
    }
}

三、配置文件那些坑

配置文件顺序不能乱!

PPT里说配置文件必须按特定顺序写,我一开始不信邪,把<mappers>放到<properties>前面,结果直接报错:

The content of element type "configuration" must match 
"(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?)".

好吧,MyBatis是真的严格…

连接池配置

<dataSource type="POOLED">

我之前一直用UNPOOLED,觉得简单。后来发现用POOLED后查询速度明显快了很多,特别是多次查询的时候。

小发现POOLED其实是MyBatis自己实现的简单连接池,生产环境一般会用Druid或者HikariCP,不过对我们学生来说够用了。

四、映射文件实战——namespace差点让我放弃

namespace到底是什么?

这个概念我看了三遍PPT都没懂,直到我犯了个错误:

// 我一开始这么写,结果报错找不到Mapper
Employee emp = session.selectOne("findById", 1);

后来才发现必须写完整的namespace:

// 正确写法
Employee emp = session.selectOne("com.dray.pojo.EmployeeMapper.findById", 1);

我的理解:namespace就像是包名,用来区分不同Mapper里的同名方法。比如UserMapper和EmployeeMapper都有findById方法,不加namespace就分不清调用哪个。

参数传递的坑

最开始我不知道#{}怎么用,写了这样的代码:

<!-- 错误示范 -->
<select id="findByName" parameterType="string">
    SELECT * FROM employee WHERE name = #{name}
</select>

Java代码:

// 这样调用会报错!
session.selectOne("findByName", "张三");

正确做法

<!-- 基本类型参数,名字随便写 -->
<select id="findByName" parameterType="string">
    SELECT * FROM employee WHERE name = #{anyNameYouWant}
</select>

或者传对象:

<!-- 对象参数,用属性名 -->
<select id="insert" parameterType="Employee">
    INSERT INTO employee(name, age) VALUES(#{name}, #{age})
</select>

五、项目结构——按照PPT搭的

我把项目结构严格按照PPT的要求搭建:

mybatis-study/
├── src/main/java/com/dray/pojo/
│   └── Employee.java
│
├── src/main/resources/
│   ├── db.properties
│   ├── mybatis-config.xml
│   └── mapper/
│       └── EmployeeMapper.xml
│
└── src/test/java/
    └── EmployeeTest.java

注意:实体类一定要有getter/setter方法!我一开始偷懒用Lombok的@Data注解,结果MyBatis反射找不到方法,折腾了好久才发现这个问题。

六、测试类的正确打开方式

用JUnit写测试真的比main方法方便多了:

public class EmployeeTest {
    private SqlSession sqlSession;
    
    @Before
    public void setUp() throws IOException {
        // 每个测试前都初始化
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        sqlSession = factory.openSession(true);
    }
    
    @Test
    public void testFindById() {
        // 终于成功了!
        Employee emp = sqlSession.selectOne(
            "com.dray.pojo.EmployeeMapper.findById", 1);
        System.out.println("找到员工: " + emp.getName());
        assertNotNull(emp);
    }
    
    @After
    public void tearDown() {
        // 自动清理,再也不怕忘记关连接了
        if (sqlSession != null) sqlSession.close();
    }
}

好处:不用每次都在main方法里复制粘贴初始化代码,而且@After保证了资源一定会被释放。

七、我遇到的真实问题和解决方法

问题1:MySQL 8.0驱动类名变了

# 我一开始用的是老的驱动类名,一直连不上
mysql.driver=com.mysql.jdbc.Driver  # 错误!

# 正确的应该是
mysql.driver=com.mysql.cj.jdbc.Driver

问题2:时区问题

# 不加时区参数会报错
mysql.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&serverTimezone=UTC

问题3:列名和属性名不一致

数据库里是user_name,Java里是userName,结果查出来都是null。

解决方法:要么在SQL里用别名,要么用resultMap(我觉得别名简单多了):

<select id="findAll" resultType="Employee">
    SELECT id, user_name as userName, user_age as userAge FROM employee
</select>

八、总结和下一步

经过这两天的折腾,我总算把MyBatis的基础搞明白了。虽然过程很痛苦,但比起JDBC确实省了很多事。

我的收获

  1. 理解了三大核心对象的生命周期

  2. 学会了基本的CRUD操作

  3. 知道了常见的坑和解决方法

下一步计划

  • 学习动态SQL(if、foreach这些标签)

  • 研究resultMap的高级用法

  • 试试和Spring Boot集成

最后的感悟:其实技术学习就是这样,看十遍不如动手做一遍。虽然坐在后排听不清,但自己动手调试的过程反而让我理解得更深刻。希望我的这些踩坑经历能帮到同样在自学的同学!


环境信息:MyBatis 3.5 + MySQL 8.0 + Java 17
最后更新:2026年3月12日


让我们忠于理想,让我们面对显示

SailTrack

entp 辩论家

站长

具有版权性

请您在转载、复制时注明本文 作者、链接及内容来源信息。 若涉及转载第三方内容,还需一同注明。

具有时效性

文章目录

欢迎来到SailTrack的站点,为您导航全站动态

25 文章数
9 分类数
2 评论数
11标签数