189 lines
5.1 KiB
Plaintext
189 lines
5.1 KiB
Plaintext
/*
|
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
|
* 查询构建器:条件/排序/分页 → SQL,委托执行回调物化结果。
|
|
*
|
|
* 解耦设计:QueryBuilder 不直接依赖 DbContext/驱动。
|
|
* 构造时传入 执行回调 (sql, params) -> ArrayList<T>,由 DbContext 提供。
|
|
*/
|
|
|
|
package simcu::orm.query
|
|
|
|
import std.collection.*
|
|
import simcu::orm.model.*
|
|
import simcu::orm.sql.*
|
|
|
|
/**
|
|
* 分页结果。
|
|
*/
|
|
public class PagedResult<T> {
|
|
/// 当前页数据
|
|
public var items: ArrayList<T> = ArrayList()
|
|
/// 满足条件的总条数
|
|
public var total: Int64 = 0
|
|
/// 页码(从 1 开始)
|
|
public var page: Int64 = 1
|
|
/// 每页条数
|
|
public var pageSize: Int64 = 20
|
|
|
|
public init() {}
|
|
|
|
public init(items: ArrayList<T>, total: Int64, page: Int64, pageSize: Int64) {
|
|
this.items = items
|
|
this.total = total
|
|
this.page = page
|
|
this.pageSize = pageSize
|
|
}
|
|
|
|
/// 总页数
|
|
public func totalPages(): Int64 {
|
|
if (pageSize <= 0) {
|
|
return 0
|
|
}
|
|
(total + pageSize - 1) / pageSize
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 查询构建器(对齐 EF Core IQueryable 的常用子集)。
|
|
*/
|
|
public class QueryBuilder<T> {
|
|
private let _model: EntityModel
|
|
private let _dialect: ISqlDialect
|
|
private let _executor: (String, ArrayList<Any>) -> ArrayList<T>
|
|
private let _counter: (String, ArrayList<Any>) -> Int64
|
|
|
|
private let _whereParts = ArrayList<String>()
|
|
private let _params = ArrayList<Any>()
|
|
private let _orderByParts = ArrayList<String>()
|
|
private var _skip: Int64 = 0
|
|
private var _take: Int64 = 0
|
|
|
|
public init(model: EntityModel, dialect: ISqlDialect, executor: (String, ArrayList<Any>) -> ArrayList<T>,
|
|
counter: (String, ArrayList<Any>) -> Int64) {
|
|
this._model = model
|
|
this._dialect = dialect
|
|
this._executor = executor
|
|
this._counter = counter
|
|
}
|
|
|
|
/// filter("age > ?", [18]):原始片段,参数按 ? 顺序;片段中请直接写数据库列名
|
|
public func filter(condition: String, params: ArrayList<Any>): QueryBuilder<T> {
|
|
_whereParts.add("(${condition})")
|
|
for (p in params) {
|
|
_params.add(p)
|
|
}
|
|
this
|
|
}
|
|
|
|
/// filter("age", ">", 18):属性名/列名自动映射为列名
|
|
public func filter(property: String, op: String, value: Any): QueryBuilder<T> {
|
|
let col = _model.mapColumn(property)
|
|
_whereParts.add("(${_dialect.quoteName(col)} ${op} ?)")
|
|
_params.add(value)
|
|
this
|
|
}
|
|
|
|
public func orderBy(property: String): QueryBuilder<T> {
|
|
_orderByParts.add("${_dialect.quoteName(_model.mapColumn(property))} ASC")
|
|
this
|
|
}
|
|
|
|
public func orderByDesc(property: String): QueryBuilder<T> {
|
|
_orderByParts.add("${_dialect.quoteName(_model.mapColumn(property))} DESC")
|
|
this
|
|
}
|
|
|
|
public func skip(n: Int64): QueryBuilder<T> {
|
|
_skip = n
|
|
this
|
|
}
|
|
|
|
public func take(n: Int64): QueryBuilder<T> {
|
|
_take = n
|
|
this
|
|
}
|
|
|
|
/// 执行查询,返回实体列表
|
|
public func toList(): ArrayList<T> {
|
|
_executor(buildSelectSql(), copyParams())
|
|
}
|
|
|
|
/// 返回第一条(无结果返回 None)
|
|
public func first(): ?T {
|
|
let saved = _take
|
|
_take = 1
|
|
let list = toList()
|
|
_take = saved
|
|
if (list.size > 0) {
|
|
return Some(list[0])
|
|
}
|
|
None
|
|
}
|
|
|
|
/// 满足条件的总数
|
|
public func count(): Int64 {
|
|
let sql = _dialect.buildCount(_model.tableName, whereSql())
|
|
_counter(sql, copyParams())
|
|
}
|
|
|
|
/// 分页(page 从 1 开始)
|
|
public func page(page: Int64, pageSize: Int64): PagedResult<T> {
|
|
let total = count()
|
|
let savedSkip = _skip
|
|
let savedTake = _take
|
|
_skip = (page - 1) * pageSize
|
|
_take = pageSize
|
|
let items = toList()
|
|
_skip = savedSkip
|
|
_take = savedTake
|
|
PagedResult<T>(items, total, page, pageSize)
|
|
}
|
|
|
|
private func buildSelectSql(): String {
|
|
_dialect.buildSelect(_model.tableName, selectColumns(), whereSql(), orderBySql(), _skip, _take)
|
|
}
|
|
|
|
/// 物化列(与属性顺序一致,结果集按此索引读取)
|
|
private func selectColumns(): ArrayList<String> {
|
|
let cols = ArrayList<String>()
|
|
for (p in _model.properties) {
|
|
cols.add(p.columnName)
|
|
}
|
|
cols
|
|
}
|
|
|
|
private func whereSql(): String {
|
|
var sb = StringBuilder()
|
|
var first = true
|
|
for (w in _whereParts) {
|
|
if (!first) {
|
|
sb.append(" AND ")
|
|
}
|
|
sb.append(w)
|
|
first = false
|
|
}
|
|
sb.toString()
|
|
}
|
|
|
|
private func orderBySql(): String {
|
|
var sb = StringBuilder()
|
|
var first = true
|
|
for (o in _orderByParts) {
|
|
if (!first) {
|
|
sb.append(", ")
|
|
}
|
|
sb.append(o)
|
|
first = false
|
|
}
|
|
sb.toString()
|
|
}
|
|
|
|
private func copyParams(): ArrayList<Any> {
|
|
let copy = ArrayList<Any>()
|
|
for (p in _params) {
|
|
copy.add(p)
|
|
}
|
|
copy
|
|
}
|
|
}
|