serverpod-database
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseServerpod Database
Serverpod 数据库
Serverpod generates ORM code for models with in . PostgreSQL is the default production database; SQLite is also supported for projects/tests that configure the database on with .
table.spy.yamlconfig/<runMode>.yamldatabase.filePath: <path>Serverpod 会为中带有标识的模型生成ORM代码。PostgreSQL是默认的生产环境数据库;若在中配置,SQLite也可用于项目或测试环境。
.spy.yamltableconfig/<runMode>.yamldatabase.filePath: <path>CRUD
CRUD
spy
class: Company
table: company
fields:
name: String
foundedDate: DateTimeSee Serverpod Models for more on defining models and relations.
dart
var company = Company(name: 'Serverpod Inc.', foundedDate: DateTime.now());
company = await Company.db.insertRow(session, company);
var stored = await Company.db.findById(session, company.id);
company = company.copyWith(name: 'New Name');
await Company.db.updateRow(session, company);
await Company.db.deleteRow(session, company);spy
class: Company
table: company
fields:
name: String
foundedDate: DateTime如需了解更多模型及关系定义的内容,请查看Serverpod Models。
dart
var company = Company(name: 'Serverpod Inc.', foundedDate: DateTime.now());
company = await Company.db.insertRow(session, company);
var stored = await Company.db.findById(session, company.id);
company = company.copyWith(name: 'New Name');
await Company.db.updateRow(session, company);
await Company.db.deleteRow(session, company);Filters
筛选器
Fluent filter API via callback with table descriptor :
wheretdart
var activeCompanies = await Company.db.find(session,
where: (t) => t.name.ilike('a%') & (t.foundedDate > DateTime(2020)));- Equality: ,
t.column.equals(value)t.column.notEquals(value) - Comparison (int/double/Duration/DateTime): ,
>,>=,<<= - Range: ,
t.column.between(a, b)notBetween - Set: ,
t.column.inSet(set)notInSet - String: (case-sensitive),
t.column.like('A%')(case-insensitive);ilike= any chars,%= one char_ - Combine: (and),
&(or),|(not); use parentheses for precedence~ - Related (one-to-one):
t.address.street.like('%road%') - Related (one-to-many): ,
t.orders.count() > 3,t.orders.count((o) => o.itemType.equals('book')) > 3,t.orders.none(),t.orders.any(),t.orders.any((o) => ...)t.orders.every((o) => ...) - Vector: distance operators (,
distanceCosine) for similarity search (PostgreSQL only).distanceL2
通过回调结合表描述符实现流畅的筛选API:
wheretdart
var activeCompanies = await Company.db.find(session,
where: (t) => t.name.ilike('a%') & (t.foundedDate > DateTime(2020)));- 相等判断:、
t.column.equals(value)t.column.notEquals(value) - 比较操作(整数/浮点数/时长/日期时间):、
>、>=、<<= - 范围判断:、
t.column.between(a, b)notBetween - 集合判断:、
t.column.inSet(set)notInSet - 字符串匹配:(区分大小写)、
t.column.like('A%')(不区分大小写);ilike匹配任意字符,%匹配单个字符_ - 条件组合:(且)、
&(或)、|(非);使用括号调整优先级~ - 关联查询(一对一):
t.address.street.like('%road%') - 关联查询(一对多):、
t.orders.count() > 3、t.orders.count((o) => o.itemType.equals('book')) > 3、t.orders.none()、t.orders.any()、t.orders.any((o) => ...)t.orders.every((o) => ...) - 向量操作:用于相似度搜索的距离运算符(、
distanceCosine),仅支持PostgreSQL。distanceL2
Sorting and pagination
排序与分页
- Sort: (ascending default);
orderBy: (t) => t.columnfor descendingorderBy: (t) => t.column.desc() - Multiple:
orderByList: (t) => [t.name.desc(), t.id.asc()] - Sort on relation: ; on count:
orderBy: (t) => t.ceo.nameorderBy: (t) => t.employees.count() - Pagination: +
limitfor offset-based; cursor-based:offsetwithwhere: (t) => t.id > lastIdandorderBy: (t) => t.idlimit
- 排序:(默认升序);使用
orderBy: (t) => t.column实现降序orderBy: (t) => t.column.desc() - 多字段排序:
orderByList: (t) => [t.name.desc(), t.id.asc()] - 基于关联字段排序:;基于数量排序:
orderBy: (t) => t.ceo.nameorderBy: (t) => t.employees.count() - 分页:基于偏移量的分页使用+
limit;基于游标分页:结合offset、where: (t) => t.id > lastId和orderBy: (t) => t.idlimit
Relations (include, attach, detach)
关联关系(包含、附加、分离)
Fetch related objects with :
includedart
var employee = await Employee.db.findById(session, id,
include: Employee.include(address: Address.include()));
var company = await Company.db.findById(session, id,
include: Company.include(
employees: Employee.includeList(
where: (t) => t.name.ilike('a%'),
orderBy: (t) => t.name,
limit: 10,
includes: Employee.include(address: Address.include()),
),
));Models with object relations also have dedicated methods for attach/detach:
dart
await Company.db.attachRow.employees(session, company, employee);
await Company.db.attach.employees(session, company, [e1, e2]);
await Company.db.detachRow.employees(session, employee);Objects being attached/detached must have set (typically fetched previously from the database).
id使用获取关联对象:
includedart
var employee = await Employee.db.findById(session, id,
include: Employee.include(address: Address.include()));
var company = await Company.db.findById(session, id,
include: Company.include(
employees: Employee.includeList(
where: (t) => t.name.ilike('a%'),
orderBy: (t) => t.name,
limit: 10,
includes: Employee.include(address: Address.include()),
),
));带有对象关联的模型还提供专用的附加/分离方法:
dart
await Company.db.attachRow.employees(session, company, employee);
await Company.db.attach.employees(session, company, [e1, e2]);
await Company.db.detachRow.employees(session, employee);被附加/分离的对象必须设置(通常是之前从数据库中获取的)。
idTransactions
事务
dart
await session.db.transaction((tx) async {
await Company.db.insertRow(session, company, transaction: tx);
await OtherModel.db.updateRow(session, other, transaction: tx);
});Use for all DB calls inside the transaction.
txdart
await session.db.transaction((tx) async {
await Company.db.insertRow(session, company, transaction: tx);
await OtherModel.db.updateRow(session, other, transaction: tx);
});事务内的所有数据库操作都需使用。
txRow locking
行锁
Requires a transaction. Pass and to //:
lockModetransactionfindfindFirstRowfindById- (exclusive),
LockMode.forUpdate,forNoKeyUpdate,forShareforKeyShare - (default),
LockBehavior.wait(throw),noWait(skip, good for job queues)skipLocked - Lock without reading:
Company.db.lockRows(session, where: ..., lockMode: ..., transaction: tx)
On SQLite, trying to lock rows will be a no-op, since it only supports one write transaction at a time.
需要在事务中使用。在//中传入和:
findfindFirstRowfindByIdlockModetransaction- (排他锁)、
LockMode.forUpdate、forNoKeyUpdate、forShareforKeyShare - (默认,等待)、
LockBehavior.wait(抛出异常)、noWait(跳过行,适用于任务队列)skipLocked - 仅锁行不读取:
Company.db.lockRows(session, where: ..., lockMode: ..., transaction: tx)
在SQLite中,尝试锁行会无操作,因为它一次仅支持一个写入事务。
Runtime parameters
运行时参数
Set Postgres params globally: at Serverpod init. Per-transaction: . Use for search path, vector index options, or custom . Cannot set at session level due to connection pooling.
runtimeParametersBuilder: (params) => [params.searchPaths(['my_schema', 'public'])]await tx.setRuntimeParameters(...)MapRuntimeParameters全局设置Postgres参数:在Serverpod初始化时使用。事务级设置:。可用于设置搜索路径、向量索引选项或自定义。由于连接池的原因,无法在会话级别设置。
runtimeParametersBuilder: (params) => [params.searchPaths(['my_schema', 'public'])]await tx.setRuntimeParameters(...)MapRuntimeParametersRaw SQL
原生SQL
For running raw SQL queries, use one of the following methods:
dart
late DatabaseResult result;
result = await session.db.unsafeQuery(query, parameters: parameters);
result = await session.db.unsafeSimpleQuery(query);
late int rowsAffected;
rowsAffected = await session.db.unsafeExecute(query, parameters: parameters);
rowsAffected = await session.db.unsafeSimpleExecute(query);Prefer ORM for standard CRUD, and parameterize user input instead of string-concatenating SQL.
如需运行原生SQL查询,可使用以下方法之一:
dart
late DatabaseResult result;
result = await session.db.unsafeQuery(query, parameters: parameters);
result = await session.db.unsafeSimpleQuery(query);
late int rowsAffected;
rowsAffected = await session.db.unsafeExecute(query, parameters: parameters);
rowsAffected = await session.db.unsafeSimpleExecute(query);标准CRUD操作优先使用ORM,处理用户输入时请使用参数化查询,而非字符串拼接SQL。
Client-side database
客户端数据库
When at least one model has or , the generated class will have a method that returns a for the SQLite database file. On Flutter, open the database doing:
database: clientdatabase: allClientcreateSessionClientDatabaseSessiondart
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:my_project_client/my_project_client.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Set the client URL to the server URL
final client = Client(clientUrl);
// Resolve the database path
final path = await resolveDatabasePath('app.db');
// Store the session in your state manager to later use on database operations.
final session = await client.createSession(path, isDebugMode: kDebugMode);
}
Future<String> resolveDatabasePath(String fileName) async {
if (kIsWeb) return fileName;
final dir = await getApplicationSupportDirectory();
return p.join(dir.path, fileName);
}Note that the package will have to be added as dependency on the client package.
serverpod_database当至少一个模型设置或时,生成的类会包含方法,返回用于SQLite数据库文件的。在Flutter中,按以下方式打开数据库:
database: clientdatabase: allClientcreateSessionClientDatabaseSessiondart
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:my_project_client/my_project_client.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Set the client URL to the server URL
final client = Client(clientUrl);
// Resolve the database path
final path = await resolveDatabasePath('app.db');
// Store the session in your state manager to later use on database operations.
final session = await client.createSession(path, isDebugMode: kDebugMode);
}
Future<String> resolveDatabasePath(String fileName) async {
if (kIsWeb) return fileName;
final dir = await getApplicationSupportDirectory();
return p.join(dir.path, fileName);
}注意,需在客户端包中添加作为依赖。
serverpod_database