【RuoYi-Eggjs】:多数据库与 MyBatis 特性详解
在企业级应用开发中,数据库的选择往往需要根据项目规模、性能需求、部署环境等因素灵活调整。# 【RuoYi-Eggjs】:多数据库与 MyBatis 特性详解
在企业级应用开发中,数据库的选择往往需要根据项目规模、性能需求、部署环境等因素灵活调整。RuoYi-Eggjs 项目通过精心设计的数据库抽象层,实现了 一行配置切换数据库 的能力,同时引入了 Java 开发者熟悉的 MyBatis XML 风格来编写 SQL,让业务逻辑与数据访问完美分离。
GitHub:
https://github.com/undsky/RuoYi-Eggjs
核心特性
🔌 多数据库支持
项目原生支持三种主流数据库:
| 数据库 | 插件 | 适用场景 |
|---|---|---|
| MySQL | ruoyi-eggjs-mysql | 生产环境首选,功能完善 |
| PostgreSQL | ruoyi-eggjs-pgsql | 复杂查询、地理数据 |
| SQLite | ruoyi-eggjs-sqlite | 开发测试、轻量部署 |
🗄️ MyBatis XML 风格
- 业务逻辑与 SQL 完全分离
- 支持动态 SQL 标签(if、where、set、foreach 等)
- 参数化查询,自动防 SQL 注入
- SQL 片段复用,提高可维护性
数据库映射配置
一行配置,轻松切换
在 config/config.default.js 中,只需修改 driver 字段即可切换数据库:
javascript
// 数据库映射配置
config.database = {
master: {
driver: "mysql", // 切换为 "pgsql" 或 "sqlite" 即可
instance: "ruoyi", // 数据库实例名称
},
slave: {
driver: "mysql", // 从库配置(读操作)
instance: "ruoyi",
},
readWriteSplit: false, // 是否启用读写分离
};
切换数据库只需三步:
- 修改
driver为目标数据库类型 - 在
config.local.js中配置对应的数据库连接 - 重启应用
读写分离支持
对于高并发场景,可以启用读写分离:
javascript
config.database = {
master: {
driver: "mysql",
instance: "ruoyi_master", // 主库(写操作)
},
slave: {
driver: "mysql",
instance: "ruoyi_slave", // 从库(读操作)
},
readWriteSplit: true, // 启用读写分离
};
数据库连接配置
MySQL 配置
javascript
// config/config.local.js
config.mysql = {
camelCase: true, // 自动驼峰转换:user_name -> userName
clients: {
ruoyi: {
host: "127.0.0.1",
user: "root",
password: "your_password",
database: "ruoyi",
},
},
};
PostgreSQL 配置
javascript
config.pgsql = {
camelCase: true,
clients: {
ruoyi: {
host: "127.0.0.1",
user: "ruoyi",
password: "your_password",
database: "ruoyi",
},
},
};
SQLite 配置
javascript
config.sqlite = {
camelCase: true,
clients: {
ruoyi: {
database: "./ruoyi.db", // 数据库文件路径
},
},
};
MyBatis XML 映射
目录结构
项目采用分数据库类型的目录结构,便于管理不同数据库的 SQL 差异:
bash
mapper/
├── mysql/ # MySQL 专用 SQL
│ └── ruoyi/
│ ├── SysUserMapper.xml
│ ├── SysRoleMapper.xml
│ └── ...
├── pgsql/ # PostgreSQL 专用 SQL
│ └── ruoyi/
│ └── ...
└── sqlite/ # SQLite 专用 SQL
└── ruoyi/
└── ...
XML 映射文件示例
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">
<!-- SQL 片段复用 -->
<sql id="selectUserVo">
select u.user_id, u.user_name, u.nick_name, u.email,
u.phonenumber, u.status, u.create_time
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
</sql>
<!-- 动态条件查询 -->
<selects id="selectUserList" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.del_flag = '0'
<if test="userName != null and userName != ''">
AND u.user_name like concat('%', #{userName}, '%')
</if>
<if test="status != null and status != ''">
AND u.status = #{status}
</if>
<if test="params.beginTime != null and params.beginTime != ''">
AND u.create_time >= #{params.beginTime}
</if>
</selects>
<!-- 动态更新 -->
<update id="updateUser">
update sys_user
<set>
<if test="nickName != null and nickName != ''">
nick_name = #{nickName},
</if>
<if test="email != null">email = #{email},</if>
<if test="status != null and status != ''">
status = #{status},
</if>
update_time = sysdate()
</set>
where user_id = #{userId}
</update>
<!-- 批量删除 -->
<delete id="deleteUserByIds">
update sys_user set del_flag = '2' where user_id in
<foreach collection="array" item="userId"
open="(" separator="," close=")">
#{userId}
</foreach>
</delete>
</mapper>
支持的动态 SQL 标签
| 标签 | 说明 | 示例 |
|---|---|---|
<if> |
条件判断 | <if test="userName">...</if> |
<where> |
智能 WHERE 子句 | 自动处理首个 AND/OR |
<set> |
智能 SET 子句 | 自动处理末尾逗号 |
<foreach> |
循环遍历 | IN 查询、批量操作 |
<choose> |
多条件选择 | 类似 switch-case |
<sql> |
SQL 片段定义 | 提取公共 SQL |
<include> |
引用 SQL 片段 | 复用公共 SQL |
自动代码生成
Service 层自动生成
项目配套的 CLI 工具可以根据 XML Mapper 自动生成 Service 层代码:
bash
# 启动代码生成器(自动监听模式)
npm run mapper
# 或手动执行
psy mapper
生成效果:
bash
mapper/mysql/ruoyi/SysUserMapper.xml
↓ 自动生成
app/service/db/mysql/ruoyi/SysUserMapper.js
生成的 Service 代码
javascript
const Service = require('egg').Service;
class SysUserMapperService extends Service {
mapper(sqlid, values, params) {
return this.app.mapper(
'mapper/mysql/ruoyi/SysUserMapper.xml',
sqlid, values, params
);
}
db() {
return this.app.mysql.get('ruoyi');
}
// 查询用户列表
async selectUserList(values, params) {
return await this.db().selects(
this.mapper('selectUserList', values, params)
);
}
// 更新用户
async updateUser(values, params) {
return await this.db().update(
this.mapper('updateUser', values, params)
);
}
// 批量删除
async deleteUserByIds(values, params) {
return await this.db().del(
this.mapper('deleteUserByIds', values, params)
);
}
}
module.exports = SysUserMapperService;
实际使用
Controller 调用示例
javascript
// app/controller/system/user.js
class UserController extends Controller {
async list() {
const { ctx } = this;
const params = ctx.request.body;
// 调用自动生成的 Service
const users = await ctx.service.db.mysql.ruoyi
.sysUserMapper.selectUserList(
ctx.helper.page(params), // 自动分页
params
);
ctx.body = { code: 200, data: users };
}
async update() {
const { ctx } = this;
const user = ctx.request.body;
await ctx.service.db.mysql.ruoyi
.sysUserMapper.updateUser([user.userId], user);
ctx.body = { code: 200, msg: '更新成功' };
}
}
分页查询
内置分页辅助方法,自动计算 LIMIT 参数:
javascript
// 请求参数
{
"currentPage": 2,
"pageSize": 20,
"userName": "张三"
}
// 使用 ctx.helper.page() 自动注入分页参数
const sql = app.mapper(
'namespace',
'selectUserList',
ctx.helper.page(params), // 自动计算 [offset, limit]
params
);
参数占位符
#{} - 预编译参数(推荐)
自动转义,防止 SQL 注入:
xml
<select id="selectUser">
SELECT * FROM sys_user WHERE user_name = #{userName}
</select>
${} - 直接替换
用于表名、字段名等不可参数化的部分(注意安全性):
xml
<select id="selectByColumn">
SELECT * FROM sys_user ORDER BY ${orderColumn} ${orderType}
</select>
开发工作流
推荐流程
bash
# 1. 启动开发环境(自动生成 Mapper + 调试)
npm run dev
# 2. 编写/修改 XML Mapper 文件
# 3. CLI 自动检测变化并重新生成 Service
# 4. 在 Controller 中调用 Service
package.json 脚本
json
{
"scripts": {
"dev": "npm-run-all -p mapper debug",
"mapper": "psy mapper",
"debug": "egg-bin debug",
"start": "egg-scripts start",
"stop": "egg-scripts stop"
}
}
数据库切换实战
场景:从 MySQL 切换到 SQLite
步骤 1:修改数据库映射配置
javascript
// config/config.default.js
config.database = {
master: {
driver: "sqlite", // 改为 sqlite
instance: "ruoyi",
},
slave: {
driver: "sqlite",
instance: "ruoyi",
},
readWriteSplit: false,
};
步骤 2:配置 SQLite 连接
javascript
// config/config.local.js
config.sqlite = {
camelCase: true,
clients: {
ruoyi: {
database: "./ruoyi.db",
},
},
};
步骤 3:导入数据
bash
sqlite3 ruoyi.db < sql/sqlite/ry_20250522.sql
步骤 4:重启应用
bash
npm run dev
总结
RuoYi-Eggjs 通过以下设计实现了灵活的多数据库支持:
- 统一的数据库映射配置 - 一行配置切换数据库类型
- MyBatis XML 风格 - 业务逻辑与 SQL 分离,支持动态 SQL
- 自动代码生成 - 根据 XML 自动生成 Service 层代码
- 分数据库目录结构 - 便于管理不同数据库的 SQL 差异
这种设计让开发者可以:
- 开发阶段使用 SQLite 快速迭代
- 测试阶段切换到 MySQL/PostgreSQL 验证兼容性
- 生产环境根据需求选择最合适的数据库
真正实现了 一套代码,多库运行 的目标。
相关链接
- ruoyi-eggjs
- ruoyi-eggjs-mybatis - MyBatis XML SQL 映射插件
- ruoyi-eggjs-mysql - MySQL 数据库插件
- ruoyi-eggjs-pgsql - PostgreSQL 数据库插件
- ruoyi-eggjs-sqlite - SQLite 数据库插件
- ruoyi-eggjs-cli - 代码生成工具 。
核心特性
🔌 多数据库支持
项目原生支持三种主流数据库:
| 数据库 | 插件 | 适用场景 |
|---|---|---|
| MySQL | ruoyi-eggjs-mysql | 生产环境首选,功能完善 |
| PostgreSQL | ruoyi-eggjs-pgsql | 复杂查询、地理数据 |
| SQLite | ruoyi-eggjs-sqlite | 开发测试、轻量部署 |
🗄️ MyBatis XML 风格
- 业务逻辑与 SQL 完全分离
- 支持动态 SQL 标签(if、where、set、foreach 等)
- 参数化查询,自动防 SQL 注入
- SQL 片段复用,提高可维护性
数据库映射配置
一行配置,轻松切换
在 config/config.default.js 中,只需修改 driver 字段即可切换数据库:
javascript
// 数据库映射配置
config.database = {
master: {
driver: "mysql", // 切换为 "pgsql" 或 "sqlite" 即可
instance: "ruoyi", // 数据库实例名称
},
slave: {
driver: "mysql", // 从库配置(读操作)
instance: "ruoyi",
},
readWriteSplit: false, // 是否启用读写分离
};
切换数据库只需三步:
- 修改
driver为目标数据库类型 - 在
config.local.js中配置对应的数据库连接 - 重启应用
读写分离支持
对于高并发场景,可以启用读写分离:
javascript
config.database = {
master: {
driver: "mysql",
instance: "ruoyi_master", // 主库(写操作)
},
slave: {
driver: "mysql",
instance: "ruoyi_slave", // 从库(读操作)
},
readWriteSplit: true, // 启用读写分离
};
数据库连接配置
MySQL 配置
javascript
// config/config.local.js
config.mysql = {
camelCase: true, // 自动驼峰转换:user_name -> userName
clients: {
ruoyi: {
host: "127.0.0.1",
user: "root",
password: "your_password",
database: "ruoyi",
},
},
};
PostgreSQL 配置
javascript
config.pgsql = {
camelCase: true,
clients: {
ruoyi: {
host: "127.0.0.1",
user: "ruoyi",
password: "your_password",
database: "ruoyi",
},
},
};
SQLite 配置
javascript
config.sqlite = {
camelCase: true,
clients: {
ruoyi: {
database: "./ruoyi.db", // 数据库文件路径
},
},
};
MyBatis XML 映射
目录结构
项目采用分数据库类型的目录结构,便于管理不同数据库的 SQL 差异:
bash
mapper/
├── mysql/ # MySQL 专用 SQL
│ └── ruoyi/
│ ├── SysUserMapper.xml
│ ├── SysRoleMapper.xml
│ └── ...
├── pgsql/ # PostgreSQL 专用 SQL
│ └── ruoyi/
│ └── ...
└── sqlite/ # SQLite 专用 SQL
└── ruoyi/
└── ...
XML 映射文件示例
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">
<!-- SQL 片段复用 -->
<sql id="selectUserVo">
select u.user_id, u.user_name, u.nick_name, u.email,
u.phonenumber, u.status, u.create_time
from sys_user u
left join sys_dept d on u.dept_id = d.dept_id
</sql>
<!-- 动态条件查询 -->
<selects id="selectUserList" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.del_flag = '0'
<if test="userName != null and userName != ''">
AND u.user_name like concat('%', #{userName}, '%')
</if>
<if test="status != null and status != ''">
AND u.status = #{status}
</if>
<if test="params.beginTime != null and params.beginTime != ''">
AND u.create_time >= #{params.beginTime}
</if>
</selects>
<!-- 动态更新 -->
<update id="updateUser">
update sys_user
<set>
<if test="nickName != null and nickName != ''">
nick_name = #{nickName},
</if>
<if test="email != null">email = #{email},</if>
<if test="status != null and status != ''">
status = #{status},
</if>
update_time = sysdate()
</set>
where user_id = #{userId}
</update>
<!-- 批量删除 -->
<delete id="deleteUserByIds">
update sys_user set del_flag = '2' where user_id in
<foreach collection="array" item="userId"
open="(" separator="," close=")">
#{userId}
</foreach>
</delete>
</mapper>
支持的动态 SQL 标签
| 标签 | 说明 | 示例 |
|---|---|---|
<if> |
条件判断 | <if test="userName">...</if> |
<where> |
智能 WHERE 子句 | 自动处理首个 AND/OR |
<set> |
智能 SET 子句 | 自动处理末尾逗号 |
<foreach> |
循环遍历 | IN 查询、批量操作 |
<choose> |
多条件选择 | 类似 switch-case |
<sql> |
SQL 片段定义 | 提取公共 SQL |
<include> |
引用 SQL 片段 | 复用公共 SQL |
自动代码生成
Service 层自动生成
项目配套的 CLI 工具可以根据 XML Mapper 自动生成 Service 层代码:
bash
# 启动代码生成器(自动监听模式)
npm run mapper
# 或手动执行
psy mapper
生成效果:
bash
mapper/mysql/ruoyi/SysUserMapper.xml
↓ 自动生成
app/service/db/mysql/ruoyi/SysUserMapper.js
生成的 Service 代码
javascript
const Service = require('egg').Service;
class SysUserMapperService extends Service {
mapper(sqlid, values, params) {
return this.app.mapper(
'mapper/mysql/ruoyi/SysUserMapper.xml',
sqlid, values, params
);
}
db() {
return this.app.mysql.get('ruoyi');
}
// 查询用户列表
async selectUserList(values, params) {
return await this.db().selects(
this.mapper('selectUserList', values, params)
);
}
// 更新用户
async updateUser(values, params) {
return await this.db().update(
this.mapper('updateUser', values, params)
);
}
// 批量删除
async deleteUserByIds(values, params) {
return await this.db().del(
this.mapper('deleteUserByIds', values, params)
);
}
}
module.exports = SysUserMapperService;
实际使用
Controller 调用示例
javascript
// app/controller/system/user.js
class UserController extends Controller {
async list() {
const { ctx } = this;
const params = ctx.request.body;
// 调用自动生成的 Service
const users = await ctx.service.db.mysql.ruoyi
.sysUserMapper.selectUserList(
ctx.helper.page(params), // 自动分页
params
);
ctx.body = { code: 200, data: users };
}
async update() {
const { ctx } = this;
const user = ctx.request.body;
await ctx.service.db.mysql.ruoyi
.sysUserMapper.updateUser([user.userId], user);
ctx.body = { code: 200, msg: '更新成功' };
}
}
分页查询
内置分页辅助方法,自动计算 LIMIT 参数:
javascript
// 请求参数
{
"currentPage": 2,
"pageSize": 20,
"userName": "张三"
}
// 使用 ctx.helper.page() 自动注入分页参数
const sql = app.mapper(
'namespace',
'selectUserList',
ctx.helper.page(params), // 自动计算 [offset, limit]
params
);
参数占位符
#{} - 预编译参数(推荐)
自动转义,防止 SQL 注入:
xml
<select id="selectUser">
SELECT * FROM sys_user WHERE user_name = #{userName}
</select>
${} - 直接替换
用于表名、字段名等不可参数化的部分(注意安全性):
xml
<select id="selectByColumn">
SELECT * FROM sys_user ORDER BY ${orderColumn} ${orderType}
</select>
开发工作流
推荐流程
bash
# 1. 启动开发环境(自动生成 Mapper + 调试)
npm run dev
# 2. 编写/修改 XML Mapper 文件
# 3. CLI 自动检测变化并重新生成 Service
# 4. 在 Controller 中调用 Service
package.json 脚本
json
{
"scripts": {
"dev": "npm-run-all -p mapper debug",
"mapper": "psy mapper",
"debug": "egg-bin debug",
"start": "egg-scripts start",
"stop": "egg-scripts stop"
}
}
数据库切换实战
场景:从 MySQL 切换到 SQLite
步骤 1:修改数据库映射配置
javascript
// config/config.default.js
config.database = {
master: {
driver: "sqlite", // 改为 sqlite
instance: "ruoyi",
},
slave: {
driver: "sqlite",
instance: "ruoyi",
},
readWriteSplit: false,
};
步骤 2:配置 SQLite 连接
javascript
// config/config.local.js
config.sqlite = {
camelCase: true,
clients: {
ruoyi: {
database: "./ruoyi.db",
},
},
};
步骤 3:导入数据
bash
sqlite3 ruoyi.db < sql/sqlite/ry_20250522.sql
步骤 4:重启应用
bash
npm run dev
总结
RuoYi-Eggjs 通过以下设计实现了灵活的多数据库支持:
- 统一的数据库映射配置 - 一行配置切换数据库类型
- MyBatis XML 风格 - 业务逻辑与 SQL 分离,支持动态 SQL
- 自动代码生成 - 根据 XML 自动生成 Service 层代码
- 分数据库目录结构 - 便于管理不同数据库的 SQL 差异
这种设计让开发者可以:
- 开发阶段使用 SQLite 快速迭代
- 测试阶段切换到 MySQL/PostgreSQL 验证兼容性
- 生产环境根据需求选择最合适的数据库
真正实现了 一套代码,多库运行 的目标。
相关链接
- ruoyi-eggjs
- ruoyi-eggjs-mybatis - MyBatis XML SQL 映射插件
- ruoyi-eggjs-mysql - MySQL 数据库插件
- ruoyi-eggjs-pgsql - PostgreSQL 数据库插件
- ruoyi-eggjs-sqlite - SQLite 数据库插件
- ruoyi-eggjs-cli - 代码生成工具