如果你是从 Java 转到 Node.js 的全栈开发者,或者你受够了在 JS 代码里用字符串拼接 SQL,那么这篇文章就是为你准备的。本文将带你上手 ruoyi-eggjs-mybatis[1],体验业务逻辑与 SQL 彻底分离的清爽。
写 Node.js 后端时,你是不是也经常对着代码里那一坨坨拼凑的 SQL 字符串发愁?
if (name) sql += ... 简直是噩梦,既容易出错又丑陋。如果你怀念 Java 生态中 MyBatis 那种 "XML 统一管理 SQL" 和 "强大的动态标签",那么 ruoyi-eggjs-mybatis 就能完美解决你的痛点,把这种优雅的开发体验带到 Egg.js 项目中。
这个插件不仅仅是把 SQL 搬到了 XML 里,更重要的是它带来了一套完整的 SQL 编程逻辑:
<where>、<if> 等标签,让 SQL 像代码一样具备逻辑性。#{}),自动转义参数,从根源上防止 SQL 注入。先装包,然后在 Egg.js 里把插件开起来。
$ npm i ruoyi-eggjs-mybatis --save// config/plugin.js
exports.mybatis = {
enable: true,
package: "ruoyi-eggjs-mybatis",
};在配置文件里指定 Mapper 文件的存放路径。
// config/config.default.js
const path = require('path');
config.mybatis = {
mapperPath: path.join(appInfo.baseDir, 'mapper'), // XML 文件放这就行
defaultPageSize: 10, // 默认分页大小
};假设我们要查用户,在 mapper/ruoyi/ 下新建 SysUserMapper.xml:
<?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="mapper/mysql/ruoyi/SysUserMapper.xml">
<select id="selectUserById">
SELECT * FROM sys_user WHERE user_id = ?
</select>
</mapper>就是这么简单,接下来看它在实战中有多好用。
这是 MyBatis 最强的地方。以前你需要写几十行 JS 代码来判断拼接 WHERE 子句,现在只需要:
<select id="selectUserList">
SELECT * FROM sys_user
<where>
<if test="userName">
AND user_name LIKE CONCAT('%', #{userName}, '%')
</if>
<if test="status">
AND status = #{status}
</if>
<if test="params.beginTime">
AND create_time >= #{params.beginTime}
</if>
</where>
ORDER BY create_time DESC
LIMIT ?, ?
</select>神奇之处:
<where> 标签很智能,它会自动去掉第一个多余的 AND。WHERE 关键字都不会生成。Service 层调用:
// 自动处理分页逻辑,无需手动算 offset
const sql = app.mapper(
'mapper/mysql/ruoyi/SysUserMapper.xml',
'selectUserList',
ctx.helper.page(params),
params
);
const users = await app.mysql.selects(sql);做 Update 操作时,用户只传了 email,没传 userName?用 <set> 标签,不用再担心逗号拼错的问题。
<update id="updateUser">
UPDATE sys_user
<set>
<if test="userName">user_name = #{userName},</if>
<if test="email">email = #{email},</if>
update_time = NOW()
</set>
WHERE user_id = ?
</update>不用手动循环拼 ID 了,用 <foreach> 优雅解决:
<delete id="deleteUserByIds">
DELETE FROM sys_user WHERE user_id IN
<foreach collection="array" item="userId" open="(" separator="," close=")">
#{userId}
</foreach>
</delete># 和 $ 的区别这是新手最容易踩的坑,请务必记好:
#{param}(强烈推荐):?。user_name = #{name} -> user_name = 'admin'。${param}(谨慎使用):ORDER BY ${column})动态变化时才用。为了保持代码整洁,建议遵循 Controller -> Service -> Mapper 的三层架构。
Service 层示例:
Service 层的职责变得非常纯粹——准备参数,调用 Mapper,获取结果。
// app/service/user.js
const { Service } = require('egg');
class UserService extends Service {
async selectUserById(userId) {
const { app } = this;
// 1. 定位 XML 文件
// 2. 指定 SQL ID
// 3. 传入参数
const sql = app.mapper(
'mapper/mysql/ruoyi/SysUserMapper.xml',
'selectUserById',
[userId]
);
return await app.mysql.select(sql);
}
}希望这个插件能帮你把 Node.js 的数据层代码打理得井井有条!如果觉得好用,别忘了去 GitHub 上点个 Star 哦。
[1] ruoyi-eggjs-mybatis: https://github.com/undsky/ruoyi-eggjs-mybatis
[2] RuoYi-Eggjs: https://github.com/undsky/ruoyi-eggjs
[3] RuoYi-Eggjs 文档: https://www.undsky.com/blog/?category=RuoYi-Eggjs#