写在前面
说实话,坐教室最后一排真的听不清老师在讲啥,再加上寒假玩了一个月,开学回来发现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确实省了很多事。
我的收获:
理解了三大核心对象的生命周期
学会了基本的CRUD操作
知道了常见的坑和解决方法
下一步计划:
学习动态SQL(if、foreach这些标签)
研究resultMap的高级用法
试试和Spring Boot集成
最后的感悟:其实技术学习就是这样,看十遍不如动手做一遍。虽然坐在后排听不清,但自己动手调试的过程反而让我理解得更深刻。希望我的这些踩坑经历能帮到同样在自学的同学!
环境信息:MyBatis 3.5 + MySQL 8.0 + Java 17
最后更新:2026年3月12日
默认评论
Halo系统提供的评论