spring-moon-query
v0.0.6
Published
Query framework for Spring Moon - MyBatis-Plus style fluent API + MyBatis XML mappers on Node.js.
Maintainers
Readme
spring-moon-query
一个为 spring-moon 设计的 查询框架(Query Framework),目标是提供:
- MyBatis-Plus 风格的 Fluent Query API
- MyBatis 风格的 XML Mapper 查询
- Wrapper 与 XML 共享同一套 SqlSource 抽象 + Executor 执行引擎
- 通过 适配器 接入
pg/mysql2/ drizzle 等不同底层
⚠️ 这是框架内核,而不是业务 ORM:
- 不做实体生命周期管理
- 不做复杂关系映射
- 只专注在 SQL 构建 / 参数绑定 / 执行 三件事
总体设计
SqlSource:统一 SQL 抽象
export interface SqlSource<P = any> {
getSql(params?: P): { sql: string; params: any[] }
}所有查询入口(Fluent Query、XML Mapper)最终都会实现 SqlSource,由 QueryExecutor 统一执行。
两种查询入口
1. Fluent Query(MyBatis-Plus 风格)
推荐:实体用装饰器声明表/列映射(类似 MyBatis-Plus 的 @TableName / @TableField):
import { TableName, TableField } from 'spring-moon-query'
@TableName('users')
class UserEntity {
id!: number
name!: string
status!: number
@TableField('created_at')
createdAt!: Date
}
class UserMapper extends BaseMapper<UserEntity> {
constructor(executor: QueryExecutor) {
super(executor, UserEntity) // 传实体类即可,元数据从装饰器读取
}
}也可用元数据对象(兼容旧写法):
const userMeta: EntityMetadata<User> = {
entity: User,
tableName: 'user',
columns: [
{ property: 'id', column: 'id' },
{ property: 'createdAt', column: 'created_at' },
],
}
class UserMapper extends BaseMapper<User> {
constructor(executor: QueryExecutor) {
super(executor, userMeta)
}
}
const mapper = new UserMapper(executor)
// 全量查询(推荐)
const users = await mapper
.lambdaQuery()
.eq(u => u.status, 1)
.like(u => u.name, '%moon%')
.orderByDesc(u => u.createdAt)
.list()
// 单条查询
const oneUser = await mapper.lambdaQuery().eq(u => u.id, 1).one()
// 分页查询
const page = await mapper
.lambdaQuery()
.eq(u => u.status, 1)
.orderByDesc(u => u.createdAt)
.page(1, 10)
// 或者使用 Mapper 级语法糖:
const all = await mapper.list()
const page2 = await mapper.page(2, 20)@TableName('表名')/@TableField('列名')将表、列声明在实体上;未写@TableField的属性列名默认用属性名。lambdaQuery()返回LambdaQueryWrapper<T>,实现SqlSource。- 支持
eq / like / orderByAsc / orderByDesc等基础条件。 BaseMapper<T>构造可传实体类(装饰器元数据)或EntityMetadata对象。BaseMapper<T>提供lambdaQuery()入口和list() / one() / page()语法糖。
注:lambda 解析目前采用 函数字符串解析 的 best-effort 方式, 对简单的
u => u.status这种形式友好,复杂场景可以直接使用'status' as keyof User。
2. XML Mapper(MyBatis 风格)
XML:
<mapper namespace="UserMapper">
<select id="selectUserWithRole" resultType="UserDTO">
SELECT u.id, u.name, r.name AS roleName
FROM user u
LEFT JOIN role r ON u.role_id = r.id
WHERE u.status = #{status}
</select>
</mapper>注册 & 调用:
import { MappedStatementRegistry, registerMapperXml, SqlSession } from 'spring-moon-query'
const registry = new MappedStatementRegistry()
registerMapperXml(registry, xmlString)
const session = new SqlSession(executor, registry)
const dto = await session.selectOne<UserDTO>('UserMapper.selectUserWithRole', {
status: 1,
})- XML 中的
#{status}会被转换为?,并绑定参数数组[1]。 ${}暂未实现(且被视为危险,后续会有显式 API)。
统一执行模型
export interface QueryExecutor {
query<T = any>(sql: string, params?: any[]): Promise<T[]>
queryOne<T = any>(sql: string, params?: any[]): Promise<T | null>
queryBySource<T = any>(source: SqlSource, params?: any): Promise<T[]>
queryOneBySource<T = any>(source: SqlSource, params?: any): Promise<T | null>
}SimpleQueryExecutor依赖一个抽象的DatabaseClient:
export interface DatabaseClient {
query<T = any>(sql: string, params: any[]): Promise<{ rows: T[] }>
}你可以很容易地用 pg / mysql2 / drizzle 去适配这个接口。
MyBatis 风格 SQL 日志
SimpleQueryExecutor 默认会打印查询语句、参数和返回结果,格式参考 MyBatis:
==> Preparing: SELECT * FROM user WHERE status = $1
==> Parameters: 1
<== Total: 3单条查询时输出 Row:
==> Preparing: SELECT * FROM user WHERE id = $1
==> Parameters: 1
<== Row: {"id":1,"name":"John","status":1}- 关闭日志:设置环境变量
SM_QUERY_DEBUG=false或SM_QUERY_DEBUG=0 - 代码控制:
new SimpleQueryExecutor({ client: dbClient, enabled: false })
目录结构
spring-moon-query
├── src
│ ├── core
│ │ ├── repository.ts // BaseMapper<T>
│ │ ├── query-wrapper.ts // LambdaQueryWrapper<T>
│ │ ├── sql-source.ts // SqlSource 抽象
│ │ ├── condition.ts // 条件 & SQL 生成
│ │ └── metadata.ts // Entity / Column 元数据
│ │
│ ├── xml
│ │ ├── xml-parser.ts // XML → MappedStatement
│ │ ├── mapped-statement.ts // XML 语句定义
│ │ └── sql-session.ts // selectOne / selectList
│ │
│ ├── executor
│ │ ├── executor.ts // Executor 抽象
│ │ ├── simple-executor.ts // 基于 DatabaseClient 的简单实现
│ │ └── sql-logger.ts // MyBatis 风格 SQL 日志
│ │
│ ├── adapters
│ │ └── drizzle/ // 预留适配器目录
│ │
│ └── index.ts当前实现状态
✅ 第一阶段(已完成)
SqlSource抽象- XML Mapper:
- 解析
<mapper namespace> + <select|insert|update|delete>为MappedStatement - 支持
#{param}占位符 →?+ 参数数组 SqlSession.selectOne / selectList
- 解析
- Fluent Query:
LambdaQueryWrapper<T>支持eq / like / orderByAsc / orderByDescBaseMapper<T>.lambdaQuery().list(...) / one(...)
- 统一执行模型:
SimpleQueryExecutor基于抽象DatabaseClient
📝 下一步(TODO)
- JOIN / 多表查询的 Fluent API 与 XML 支持
- XML 动态标签(
<if> / <foreach>等) - 多数据源 / 事务集成
- 针对 drizzle / pg / mysql2 的正式适配器实现
使用建议
- 在
spring-moon应用中,你可以:- 用
spring-moon-core管理 DI / Controller / Service - 用
spring-moon-query管理 Mapper / SQL / XML Mapper
- 用
- 这两个模块之间通过 Mapper + Executor + DatabaseClient 解耦, 便于未来替换底层执行器或增加新的 Query 能力,而不需要推翻现有设计。
