npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spring-moon-query

v0.0.6

Published

Query framework for Spring Moon - MyBatis-Plus style fluent API + MyBatis XML mappers on Node.js.

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=falseSM_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 / orderByDesc
    • BaseMapper<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 能力,而不需要推翻现有设计。