学 Java 持久层的时候,我先写了一个纯 JDBC 的小项目。写完之后只有一个感受:太累了。
后来换了 MyBatis 重写一遍,同样的功能,代码量直接砍了一半多。这篇就记一下 MyBatis 到底比 JDBC 好在哪,都是我自己踩过的坑。
一、JDBC 那些重复代码,真的写到烦
先说说 JDBC 写起来有多啰嗦。一个简单的查询用户功能,你得干这些事:
- 加载数据库驱动
- 获取连接
- 创建 PreparedStatement
- 手动给占位符设参数
- 执行查询
- 遍历 ResultSet,一行一行取数据
- 把数据塞到 Java 对象里
- 关闭 ResultSet、Statement、Connection
- 中间任何一步出错了,还得处理异常
这里面有一半以上的代码跟业务逻辑没有任何关系。你只是想查一个用户的信息,结果写了几十行样板代码。而且每次查询都要写一遍,复制粘贴改改就完事。
我第一个 JDBC 项目,光是关闭资源的try-catch-finally就写了不知道多少遍。后来才知道,这种重复代码有个说法,叫"模板代码"。MyBatis 把这些事全包了,你只管写 SQL,连接怎么开、怎么关、结果集怎么遍历,它来处理。
二、SQL 写在 Java 代码里,看着就头疼
JDBC 还有一个让我难受的地方:SQL 是字符串,硬编码在 Java 代码里。
比如一个带条件的查询:
Stringsql="SELECT id, username, email FROM user WHERE username = ? AND status = ?";简单查询还行,一旦 SQL 复杂起来,十几个条件、好几个 JOIN,字符串拼起来又长又难看。而且你想改一下 SQL,得改 Java 代码,改完还得重新编译打包。
MyBatis 把 SQL 抽出来放到 XML 文件里(也可以用注解,但复杂 SQL 还是 XML 看着清楚)。Java 代码里只写一个接口方法,SQL 写在对应的 XML 文件里。两边各管各的,互不干扰。
这样做的好处是:改 SQL 不用动 Java 代码,DBA 想审查你的 SQL 也方便,直接看 XML 文件就行。我后来在团队项目里见过有人直接在 Java 里写几百行的 SQL 字符串,说实话,看的时候真的很崩溃。
三、动态 SQL,这个我最开始搞不明白
实际开发里,查询条件经常是不固定的。比如一个搜索页面,用户可以填用户名,也可以不填;可以选状态,也可以不选。SQL 里的 WHERE 条件要根据传进来的参数动态生成。
用 JDBC 的话,你得在 Java 代码里拼字符串。大概就是这样的逻辑:
Stringsql="SELECT * FROM user WHERE 1=1";if(username!=null){sql+=" AND username = ?";}if(status!=null){sql+=" AND status = ?";}WHERE 1=1这种写法,我以前以为是什么黑魔法,后来才明白就是为了拼字符串方便。但这种方式很容易出错,少一个空格、多一个 AND,SQL 就挂了,而且报的错还不好排查。
MyBatis 提供了一套 XML 标签来处理动态 SQL:
<if>:条件满足就拼上这段<choose>/<when>/<otherwise>:相当于 if-else<where>:自动处理 WHERE 后面多余的 AND 或 OR<foreach>:批量处理,比如 IN 查询
写出来大概是这样:
<selectid="findUsers"resultType="User">SELECT * FROM user<where><iftest="username != null">AND username = #{username}</if><iftest="status != null">AND status = #{status}</if></where></select>第一次看这段 XML 的时候觉得有点怪,用了两天就习惯了。比起在 Java 里拼字符串,这个写起来清楚太多了,而且不用担心WHERE 1=1那种问题,<where>标签会自动处理。
还有一个<sql>标签可以定义 SQL 片段,其他地方直接<include>引用,省得同一段 SQL 到处复制。
四、结果集映射,终于不用一行一行 set 了
JDBC 查完数据之后,你要自己遍历 ResultSet,一行一行取字段,然后手动 set 到 Java 对象里:
while(rs.next()){Useruser=newUser();user.setId(rs.getInt("id"));user.setUsername(rs.getString("username"));user.setEmail(rs.getString("email"));// ... 还有很多字段}字段少的时候还好,字段多了就是一场噩梦。而且一旦数据库表加了字段,Java 这边也得跟着改,很容易漏掉。
MyBatis 可以自动把查询结果映射到 Java 对象上。简单的情况,字段名和属性名一样,它直接就映射了,什么都不用配。复杂一点的情况,用<resultMap>标签定义一下映射关系就行。
<resultMapid="userResultMap"type="User"><idproperty="id"column="id"/><resultproperty="username"column="user_name"/><resultproperty="email"column="email"/></resultMap>数据库字段叫user_name,Java 属性叫username,在 resultMap 里对应一下就行了。
如果是一对多关系,比如一个用户有多条订单,MyBatis 也有对应的标签可以处理,不用自己写循环去组装。我第一次用的时候觉得这也太方便了,JDBC 里这种事要写一堆代码。
五、换数据库基本不用改代码
MyBatis 底层还是用 JDBC 连数据库的,所以 JDBC 支持的数据库它都支持。MySQL、Oracle、PostgreSQL,换个数据库驱动、改一下连接配置,基本就能跑。
这一点在我刚学的时候感受不深,因为一直用 MySQL。后来有个项目要从 MySQL 迁移到 PostgreSQL,除了个别 SQL 语法不一样(比如分页的写法),MyBatis 的配置和 Java 代码完全不用动。如果用的是 JDBC,改动量会大很多。
另外 MyBatis 跟 Spring 的集成也很顺滑。Spring 帮你管数据源、管事务,MyBatis 帮你管 SQL 和映射,各干各的活。实际项目里基本都是这么搭配的,配一次之后就不用再操心了。
最后
说了这么多,其实 MyBatis 解决的核心问题就一个:让你不用把时间浪费在 JDBC 那些重复的样板代码上。
连接管理它帮你做了,结果集映射它帮你做了,动态 SQL 它给你一套标签,SQL 和代码分离它给你 XML 文件。你剩下的事情就是两样:写 SQL,写业务逻辑。
如果你是新手,建议先老老实实用 JDBC 写一个小项目,把那些痛苦都体验一遍。然后再切到 MyBatis,你会知道它到底帮你省了多少事。没经历过 JDBC 的苦,就很难理解 MyBatis 的好。