【RuoYi-Eggjs】:把 MyBatis 带到 Node.js

🧐 为什么要折腾这个?

写 Node.js 后端时,你是不是也经常对着代码里那一坨坨拼凑的 SQL 字符串发愁?

  1. 代码乱:SQL 语句夹杂在 JS 逻辑里,想改个查询条件得在一堆引号和加号里找半天。
  2. 动态查询想撞墙:用户一会儿要按名字查,一会儿要按时间查。用 JS 手写 if (name) sql += ... 简直是噩梦,既容易出错又丑陋。
  3. DBA 想打人:由于 SQL 散落在代码各处,做 SQL 审查和性能优化非常困难。

如果你怀念 Java 生态中 MyBatis 那种 "XML 统一管理 SQL" 和 "强大的动态标签",那么 ruoyi-eggjs-mybatis 就能完美解决你的痛点,把这种优雅的开发体验带到 Egg.js 项目中。


🚀 核心亮点:不仅仅是把 SQL 挪个位置

这个插件不仅仅是把 SQL 搬到了 XML 里,更重要的是它带来了一套完整的 SQL 编程逻辑:

  • 告别字符串拼接:使用 <where><if> 等标签,让 SQL 像代码一样具备逻辑性。
  • 更安全:默认使用预编译(#{}),自动转义参数,从根源上防止 SQL 注入。
  • 开发超爽:开发环境下修改 XML 无需重启,即改即生效,调试效率拉满。

🛠️ 3分钟快速上手

1. 安装与开启

先装包,然后在 Egg.js 里把插件开起来。


$ npm i ruoyi-eggjs-mybatis --save


// config/plugin.js exports.mybatis = { enable: true, package: "ruoyi-eggjs-mybatis", };

2. 告诉插件 XML 在哪

在配置文件里指定 Mapper 文件的存放路径。


// config/config.default.js const path = require('path'); config.mybatis = { mapperPath: path.join(appInfo.baseDir, 'mapper'), // XML 文件放这就行 defaultPageSize: 10, // 默认分页大小 };

3. 你的第一个 XML

假设我们要查用户,在 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 &gt;= #{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>

场景三:批量删除(IN 查询)

不用手动循环拼 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'
    • 能有效防止 SQL 注入,95% 的情况都该用它。
  • ${param}(谨慎使用)

    • 裸奔。直接把字符串拼接到 SQL 里。
    • 适用场景:只有在表名、字段名(如 ORDER BY ${column})动态变化时才用。
    • 警告:一定要在代码里对传入的参数做白名单校验,否则就是给黑客留后门!

🏗️ 完整链路:Service 怎么写?

为了保持代码整洁,建议遵循 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); } }


⚖️ 选型建议:用这个还是用 ORM?

  • 如果你喜欢:完全掌控 SQL 细节,处理复杂的报表查询,或者团队里有 DBA 协助优化 SQL —— 选 ruoyi-eggjs-mybatis
  • 如果你喜欢:通过操作对象来操作数据库,不想写一句 SQL,且业务逻辑相对简单的 CRUD —— 选 Sequelize 或 TypeORM

希望这个插件能帮你把 Node.js 的数据层代码打理得井井有条!如果觉得好用,别忘了去 GitHub 上点个 Star 哦。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值