pub struct QueryBuilder<M: Model> { /* private fields */ }Expand description
用于构造 SQL 查询的查询构造器
Implementations§
Source§impl<M: Model> QueryBuilder<M>
impl<M: Model> QueryBuilder<M>
pub fn new(dialect: Box<dyn Dialect>) -> Self
pub fn table(self, table: impl Into<String>) -> Self
Sourcepub fn without_soft_delete(self) -> Self
pub fn without_soft_delete(self) -> Self
P0-1:临时禁用软删除过滤,用于查询已删除的记录。
等价于 SeaORM 的 Entity::find().filter(Column::DeletedAt.is_not_null())
或 Laravel Eloquent 的 Model::withTrashed()。
§示例
use sz_orm_core::query::QueryBuilder;
use sz_orm_core::dialect::MySqlDialect;
// 查询包含已软删除的用户
let sql = QueryBuilder::<User>::new(Box::new(MySqlDialect))
.table("users")
.without_soft_delete()
.build_select();
// 不会自动追加 WHERE deleted_at IS NULLSourcepub fn is_soft_delete_disabled(&self) -> bool
pub fn is_soft_delete_disabled(&self) -> bool
P0-1:返回软删除过滤是否被禁用
Sourcepub fn with_tenant_id(self, tenant_id: i64) -> Self
pub fn with_tenant_id(self, tenant_id: i64) -> Self
P0-3:设置当前租户 ID,启用多租户自动过滤。
当 M::tenant_field() 返回 Some(field) 时,QueryBuilder 会在以下场景
自动追加 WHERE {field} = ?(参数化,值通过 params 绑定):
build_select/build_select_with_paramsbuild_count/build_exists/build_max/build_min/build_sum/build_avgbuild_update/build_update_with_params(防止跨租户更新)build_delete/build_delete_with_params(防止跨租户删除)
使用 without_tenant() 可临时禁用租户过滤(用于跨租户管理查询)。
§示例
use sz_orm_core::query::QueryBuilder;
use sz_orm_core::dialect::MySqlDialect;
let (sql, params) = QueryBuilder::<Order>::new(Box::new(MySqlDialect))
.table("orders")
.with_tenant_id(42)
.build_select_with_params();
// sql => "SELECT * FROM `orders` WHERE `tenant_id` = ?"
// params => [Value::I64(42)]Sourcepub fn without_tenant(self) -> Self
pub fn without_tenant(self) -> Self
P0-3:临时禁用租户过滤,用于跨租户管理查询。
等价于 Laravel Eloquent 的全局作用域禁用。
Sourcepub fn is_tenant_disabled(&self) -> bool
pub fn is_tenant_disabled(&self) -> bool
P0-3:返回租户过滤是否被禁用
Sourcepub fn select(self, columns: Vec<&str>) -> Self
pub fn select(self, columns: Vec<&str>) -> Self
设置 SELECT 列。
M-3 安全警告:本方法直接拼接 columns 到 SQL,不进行标识符校验或 quote。
调用方必须确保 columns 来自可信来源(硬编码或经 sql_safety::validate_identifier
校验)。若列名可能来自不可信输入,请使用 QueryBuilder::select_quoted。
本方法保留原行为以兼容复杂表达式(如 COUNT(*)、users.id AS uid)。
Sourcepub fn select_quoted(self, columns: Vec<&str>) -> Result<Self, DbError>
pub fn select_quoted(self, columns: Vec<&str>) -> Result<Self, DbError>
M-3 修复:安全的 SELECT 列设置,自动校验每个列名并 quote。
每个 column 必须通过 sql_safety::validate_identifier 校验
(仅允许 ASCII 字母数字 + 下划线,不以数字开头,长度 1-63)。
校验失败时返回 DbError::InvalidInput。
对于复杂表达式(如 COUNT(*)、users.id AS uid),请使用 QueryBuilder::select
并自行确保安全。
Sourcepub fn where_cond(self, condition: impl Into<String>) -> Self
👎Deprecated since 1.3.0: P0-2: 字符串拼接存在 SQL 注入风险,请使用 where_eq/where_ne/where_gt/where_lt/where_like 等参数化方法
pub fn where_cond(self, condition: impl Into<String>) -> Self
P0-2: 字符串拼接存在 SQL 注入风险,请使用 where_eq/where_ne/where_gt/where_lt/where_like 等参数化方法
添加原始字符串 WHERE 条件(AND 关系)。
⚠️ P0-2 安全警告(v1.3.0+):本方法直接拼接 condition 到 SQL,
存在 SQL 注入风险。仅在以下场景使用:
- 条件来自硬编码字符串(如
where_cond("age > 18")) - 条件含复杂表达式(如
where_cond("status = 'active' AND role = 'admin'"))
禁止将用户输入拼接到 condition 中。若值来自不可信来源,
必须使用参数化方法:where_eq / where_ne /
where_gt / where_lt / where_like。
§推荐迁移
// ❌ 危险:字符串拼接
builder.where_cond(format!("name = '{}'", user_input));
// ✅ 安全:参数化绑定
builder.where_eq("name", Value::String(user_input.to_string()));Sourcepub fn or_where(self, condition: impl Into<String>) -> Self
👎Deprecated since 1.3.0: P0-2: 字符串拼接存在 SQL 注入风险,请使用参数化方法
pub fn or_where(self, condition: impl Into<String>) -> Self
P0-2: 字符串拼接存在 SQL 注入风险,请使用参数化方法
添加原始字符串 WHERE 条件(OR 关系)。
⚠️ P0-2 安全警告:同 where_cond,存在注入风险。
Sourcepub fn where_ne(self, field: impl Into<String>, value: Value) -> Self
pub fn where_ne(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化不等条件 field != ?(AND 关系)。
Sourcepub fn where_gt(self, field: impl Into<String>, value: Value) -> Self
pub fn where_gt(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化大于条件 field > ?(AND 关系)。
Sourcepub fn where_ge(self, field: impl Into<String>, value: Value) -> Self
pub fn where_ge(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化大于等于条件 field >= ?(AND 关系)。
Sourcepub fn where_lt(self, field: impl Into<String>, value: Value) -> Self
pub fn where_lt(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化小于条件 field < ?(AND 关系)。
Sourcepub fn where_le(self, field: impl Into<String>, value: Value) -> Self
pub fn where_le(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化小于等于条件 field <= ?(AND 关系)。
Sourcepub fn where_like(self, field: impl Into<String>, pattern: Value) -> Self
pub fn where_like(self, field: impl Into<String>, pattern: Value) -> Self
Sourcepub fn or_where_eq(self, field: impl Into<String>, value: Value) -> Self
pub fn or_where_eq(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化 OR 等值条件 OR field = ?。
值通过 ? 占位符绑定,杜绝 SQL 注入。OR 条件会与相邻的 OR 条件组合成 (cond1 OR cond2) 形式。
Sourcepub fn or_where_ne(self, field: impl Into<String>, value: Value) -> Self
pub fn or_where_ne(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化 OR 不等条件 OR field != ?。
Sourcepub fn or_where_gt(self, field: impl Into<String>, value: Value) -> Self
pub fn or_where_gt(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化 OR 大于条件 OR field > ?。
Sourcepub fn or_where_ge(self, field: impl Into<String>, value: Value) -> Self
pub fn or_where_ge(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化 OR 大于等于条件 OR field >= ?。
Sourcepub fn or_where_lt(self, field: impl Into<String>, value: Value) -> Self
pub fn or_where_lt(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化 OR 小于条件 OR field < ?。
Sourcepub fn or_where_le(self, field: impl Into<String>, value: Value) -> Self
pub fn or_where_le(self, field: impl Into<String>, value: Value) -> Self
P0-2:参数化 OR 小于等于条件 OR field <= ?。
Sourcepub fn or_where_like(self, field: impl Into<String>, pattern: Value) -> Self
pub fn or_where_like(self, field: impl Into<String>, pattern: Value) -> Self
P0-2:参数化 OR LIKE 条件 OR field LIKE ?。
pub fn where_in(self, field: impl Into<String>, values: Vec<Value>) -> Self
pub fn where_not_in(self, field: impl Into<String>, values: Vec<Value>) -> Self
pub fn where_between( self, field: impl Into<String>, start: Value, end: Value, ) -> Self
pub fn where_not_between( self, field: impl Into<String>, start: Value, end: Value, ) -> Self
pub fn where_null(self, field: impl Into<String>) -> Self
pub fn where_not_null(self, field: impl Into<String>) -> Self
pub fn order_by(self, field: impl Into<String>) -> Self
pub fn order_desc(self, field: impl Into<String>) -> Self
pub fn group_by(self, field: impl Into<String>) -> Self
pub fn having(self, condition: impl Into<String>) -> Self
pub fn limit(self, limit: usize) -> Self
pub fn offset(self, offset: usize) -> Self
pub fn page(self, page: usize, page_size: usize) -> Self
Sourcepub fn keyset_after(
self,
field: impl Into<String>,
cursor_value: Value,
page_size: usize,
) -> Self
pub fn keyset_after( self, field: impl Into<String>, cursor_value: Value, page_size: usize, ) -> Self
P2-5:基于游标的 Keyset 分页 — 查询指定字段值之后的记录(下一页)
生成 WHERE {field} > ? ORDER BY {field} ASC LIMIT {page_size}。
适用于按主键或时间戳递增遍历大表的场景,性能不受数据插入/删除影响。
§参数
field:排序字段(通常是主键或索引列,如id、created_at)cursor_value:当前页最后一条记录的该字段值page_size:每页大小
§示例
use sz_orm_core::{QueryBuilder, DbType, dialect::get_dialect, Value};
let dialect = get_dialect(DbType::MySQL).unwrap();
let builder = QueryBuilder::<User>::new(dialect)
.keyset_after("id", Value::I64(100), 20);
let (sql, params) = builder.build_select_with_params();
// 方言会引用字段名(如 MySQL 的 `id`),去除引号后检查
let sql_clean = sql.replace('`', "").replace('"', "");
assert!(sql_clean.contains("id > ?"));
assert!(sql.to_uppercase().contains("ORDER BY"));
assert!(sql.to_uppercase().contains("ASC"));
assert!(sql.contains("LIMIT 20"));
assert_eq!(params, vec![Value::I64(100)]);Sourcepub fn keyset_before(
self,
field: impl Into<String>,
cursor_value: Value,
page_size: usize,
) -> Self
pub fn keyset_before( self, field: impl Into<String>, cursor_value: Value, page_size: usize, ) -> Self
P2-5:基于游标的 Keyset 分页 — 查询指定字段值之前的记录(上一页)
生成 WHERE {field} < ? ORDER BY {field} DESC LIMIT {page_size}。
适用于反向遍历场景。
§参数
field:排序字段cursor_value:当前页第一条记录的该字段值page_size:每页大小
§示例
use sz_orm_core::{QueryBuilder, DbType, dialect::get_dialect, Value};
let dialect = get_dialect(DbType::MySQL).unwrap();
let builder = QueryBuilder::<User>::new(dialect)
.keyset_before("id", Value::I64(100), 20);
let (sql, params) = builder.build_select_with_params();
// 方言会引用字段名(如 MySQL 的 `id`),去除引号后检查
let sql_clean = sql.replace('`', "").replace('"', "");
assert!(sql_clean.contains("id < ?"));
assert!(sql.to_uppercase().contains("ORDER BY"));
assert!(sql.to_uppercase().contains("DESC"));
assert!(sql.contains("LIMIT 20"));
assert_eq!(params, vec![Value::I64(100)]);pub fn join_inner( self, table: impl Into<String>, on_left: impl Into<String>, on_right: impl Into<String>, ) -> Self
pub fn join_left( self, table: impl Into<String>, on_left: impl Into<String>, on_right: impl Into<String>, ) -> Self
pub fn join_right( self, table: impl Into<String>, on_left: impl Into<String>, on_right: impl Into<String>, ) -> Self
Sourcepub fn build_select(&self) -> String
pub fn build_select(&self) -> String
构建 SELECT SQL 语句
L-5 修复:补充示例文档
根据 table、select_columns、where_conditions、joins、order_by、
group_by、having、limit、offset 等条件拼装最终 SQL。
若未通过 table() 指定表名,则使用 M::table_name()。
§示例
use sz_orm_core::query::QueryBuilder;
use sz_orm_core::dialect::MySqlDialect;
use sz_orm_core::model::Model;
#[derive(Default)]
struct User;
impl Model for User {
type PrimaryKey = i64;
fn table_name() -> &'static str { "users" }
fn pk(&self) -> Self::PrimaryKey { 0 }
fn set_pk(&mut self, _: Self::PrimaryKey) {}
}
let sql = QueryBuilder::<User>::new(Box::new(MySqlDialect))
.select(vec!["id", "name"])
.where_cond("age > 18")
.order_by("id DESC")
.limit(10)
.build_select();
// sql => "SELECT id, name FROM `users` WHERE age > 18 ORDER BY id DESC LIMIT 10"pub fn build_insert(&self, data: &HashMap<String, Value>) -> String
pub fn build_update(&self, data: &HashMap<String, Value>) -> String
Sourcepub fn build_delete(&self) -> String
pub fn build_delete(&self) -> String
构建 DELETE SQL 语句。
P0-1 软删除集成(v1.3.0+):当 M: Model 实现了 soft_delete_field()
返回 Some(field) 且未调用 without_soft_delete() 时,本方法自动生成
UPDATE {table} SET {field} = NOW() WHERE ... 而非 DELETE FROM ...。
这与 SeaORM 的 ActiveModelBehavior::after_delete + ActiveValue::Set
行为对齐:删除操作实际是软删除 UPDATE。
若需物理删除,请使用 build_force_delete。
Sourcepub fn build_force_delete(&self) -> String
pub fn build_force_delete(&self) -> String
构建物理 DELETE SQL 语句(绕过软删除)。
即使 Model 实现了 soft_delete_field(),也生成 DELETE FROM ...,
且不追加 WHERE deleted_at IS NULL 过滤(保留用户指定的 WHERE 条件)。
用于管理员强制清除场景。
§安全警告
物理删除不可恢复,请谨慎使用。
Sourcepub fn build_select_with_params(&self) -> (String, Vec<Value>)
pub fn build_select_with_params(&self) -> (String, Vec<Value>)
构建 SELECT SQL(参数绑定版本)。
WHERE 子句中的值使用 ? 占位符,值通过 params 返回。
适用于 Connection::query_with_params()。
Sourcepub fn build_insert_with_params(
&self,
data: &HashMap<String, Value>,
) -> (String, Vec<Value>)
pub fn build_insert_with_params( &self, data: &HashMap<String, Value>, ) -> (String, Vec<Value>)
构建 INSERT SQL(参数绑定版本)。
Sourcepub fn build_batch_insert_with_params(
&self,
rows: &[HashMap<String, Value>],
) -> (String, Vec<Value>)
pub fn build_batch_insert_with_params( &self, rows: &[HashMap<String, Value>], ) -> (String, Vec<Value>)
P2-6:构建批量 INSERT SQL(参数绑定版本)。
生成 INSERT INTO t (c1, c2) VALUES (?, ?), (?, ?), ... 形式的多行插入 SQL。
所有行的列必须一致(取第一行的列顺序);空行列表返回空 SQL。
L3 实现深度:使用参数化占位符 ?,所有值通过 params 绑定,杜绝 SQL 注入。
Sourcepub fn build_batch_upsert_with_params(
&self,
rows: &[HashMap<String, Value>],
conflict_columns: &[&str],
update_columns: &[&str],
) -> Result<(String, Vec<Value>), DbError>
pub fn build_batch_upsert_with_params( &self, rows: &[HashMap<String, Value>], conflict_columns: &[&str], update_columns: &[&str], ) -> Result<(String, Vec<Value>), DbError>
P2-6:构建批量 Upsert SQL(参数绑定版本)。
在 build_batch_insert_with_params 基础上追加冲突处理子句:
- MySQL:
ON DUPLICATE KEY UPDATE col=VALUES(col), ... - PostgreSQL/SQLite:
ON CONFLICT (conflict_cols) DO UPDATE SET col=EXCLUDED.col, ... - Oracle/SQL Server/ClickHouse/Db2: 返回
Err(DbError::InvalidInput)(不支持)
§参数
rows: 批量数据行(所有行的列必须一致)conflict_columns: 冲突检测列(主键/唯一键);MySQL 自动检测可传空update_columns: 冲突时更新的列;空切片表示更新所有非冲突列
§返回
Ok((sql, params)): 生成的 SQL 和参数列表Err(DbError::InvalidInput): 方言不支持 upsert 或 rows 为空
L3 实现深度:
- SQL 下推:冲突处理由数据库执行,非内存判断
- 参数化:所有值通过
?占位符绑定,不拼接用户值 - 实际执行:生成标准 INSERT…ON CONFLICT/ON DUPLICATE KEY SQL
Sourcepub fn build_update_with_params(
&self,
data: &HashMap<String, Value>,
) -> (String, Vec<Value>)
pub fn build_update_with_params( &self, data: &HashMap<String, Value>, ) -> (String, Vec<Value>)
构建 UPDATE SQL(参数绑定版本)。 参数顺序:SET 参数在前,WHERE 参数在后。
Sourcepub fn build_delete_with_params(&self) -> (String, Vec<Value>)
pub fn build_delete_with_params(&self) -> (String, Vec<Value>)
构建 DELETE SQL(参数绑定版本)。
P0-1 软删除集成(v1.3.0+):当 Model 启用软删除时,自动生成
UPDATE {table} SET {field} = NOW() WHERE ... 而非 DELETE FROM ...。
参数列表为空(NOW() 由数据库填充)。
Sourcepub fn build_force_delete_with_params(&self) -> (String, Vec<Value>)
pub fn build_force_delete_with_params(&self) -> (String, Vec<Value>)
构建物理 DELETE SQL(参数绑定版本,绕过软删除)。
即使 Model 启用软删除,也生成 DELETE FROM ...,且不追加软删除过滤。
pub fn build_count(&self) -> String
pub fn build_exists(&self) -> String
pub fn build_max(&self, field: &str) -> String
pub fn build_min(&self, field: &str) -> String
pub fn build_sum(&self, field: &str) -> String
pub fn build_avg(&self, field: &str) -> String
Sourcepub fn validate(&self) -> Result<(), Vec<SqlValidationError>>
pub fn validate(&self) -> Result<(), Vec<SqlValidationError>>
校验生成的 SELECT SQL 语句 检查 SQL 语法、JOIN 列名、表名合法性
Sourcepub fn validate_insert(
&self,
data: &HashMap<String, Value>,
) -> Result<(), Vec<SqlValidationError>>
pub fn validate_insert( &self, data: &HashMap<String, Value>, ) -> Result<(), Vec<SqlValidationError>>
校验生成的 INSERT SQL 语句 含空数据检测(EmptyInsertData 错误)
Sourcepub fn validate_update(
&self,
data: &HashMap<String, Value>,
) -> Result<(), Vec<SqlValidationError>>
pub fn validate_update( &self, data: &HashMap<String, Value>, ) -> Result<(), Vec<SqlValidationError>>
校验生成的 UPDATE SQL 语句 含空数据检测(EmptyUpdateData 错误)
Sourcepub fn validate_delete(&self) -> Result<(), Vec<SqlValidationError>>
pub fn validate_delete(&self) -> Result<(), Vec<SqlValidationError>>
校验生成的 DELETE SQL 语句
Trait Implementations§
Auto Trait Implementations§
impl<M> !RefUnwindSafe for QueryBuilder<M>
impl<M> !UnwindSafe for QueryBuilder<M>
impl<M> Freeze for QueryBuilder<M>
impl<M> Send for QueryBuilder<M>
impl<M> Sync for QueryBuilder<M>
impl<M> Unpin for QueryBuilder<M>where
M: Unpin,
impl<M> UnsafeUnpin for QueryBuilder<M>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more