更新项:

1. 实体的pick和omit增加对toPickData方法的支持
2. database和PostgresClient增加同一的类型IDatabase
This commit is contained in:
2024-11-11 10:17:11 +08:00
parent 803ca64276
commit 72e4025f89
6 changed files with 115 additions and 111 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@yizhi/postgres", "name": "@yizhi/postgres",
"version": "1.0.4", "version": "1.0.6",
"main": "dist/index.js", "main": "dist/index.js",
"types": "typing/index.d.ts", "types": "typing/index.d.ts",
"scripts": {}, "scripts": {},

View File

@ -4,41 +4,66 @@ import { DeleteBuilder, IDeleteBuilder, IInsertBuilder, InsertBuilder, ISelectBu
import { Class } from "./types"; import { Class } from "./types";
import { formatSQL } from "./util"; import { formatSQL } from "./util";
interface IPostgresClient { /**
* 数据库基本操作
*/
export interface IDatabase {
/** /**
* 执行sql语句 * 执行sql语句
* @param sql sql语句 * @param sql sql语句
* @param args sql参数 * @param args sql参数
*/ */
query<R>(sql: string, args?: any[]): Promise<QueryResult<R[]>>; query<R extends QueryResultRow = any>(sql: string, args?: any[] | Record<string, any>): Promise<QueryResult<R>>
/** 开启事务 */
trans(): Promise<void>;
/** /**
* 查询实体 * 查询实体
* @param Entity 实体 * @param Entity 实体
* @param alias 别名 * @param alias 别名
*/ */
select<E extends BasicEntity>(Entity: Class<E>, alias?: string): ISelectBuilder<E>; select<E extends BasicEntity>(Entity: Class<E>, alias?: string): ISelectBuilder<E>
/** /**
* 进行数据库插入 * 进行数据库插入
* @param Entity 实体 * @param Entity 实体
*/ */
insert<E extends BasicEntity>(Entity: Class<E>): IInsertBuilder<E>; insert<E extends BasicEntity>(Entity: Class<E>): IInsertBuilder<E>
/** /**
* 进行实体更新 * 进行实体更新
* @param Entity 实体 * @param Entity 实体
*/ */
update<E extends BasicEntity>(Entity: Class<E>): IUpdateBuilder<E>; update<E extends BasicEntity>(Entity: Class<E>): IUpdateBuilder<E>
/** /**
* 进行实体删除 * 进行实体删除
* @param Entity 实体 * @param Entity 实体
*/ */
del<E extends BasicEntity>(Entity: Class<E>): IDeleteBuilder<E>; delete<E extends BasicEntity>(Entity: Class<E>): IDeleteBuilder<E>
}
interface IPostgresClient extends IDatabase {
/** 开启事务 */
trans(): Promise<void>;
}
interface IPostgresDatabase extends IDatabase {
/**
* 配置数据库
* @param loader 配置加载器
*/
config(loader: () => IPostgresConfig): void;
/**
* 连接数据库
* @param callback 回调
*/
connect<R>(callback: (client: IPostgresClient) => R | Promise<R>): Promise<R>
/**
* 执行事务操作
* @param callback 回调
*/
transaction<R>(callback: (client: IPostgresClient) => R | Promise<R>): Promise<R>
/** 关闭数据库 */
close(): void;
} }
interface IPostgresConfig { interface IPostgresConfig {
@ -58,7 +83,7 @@ class PostgresClient implements IPostgresClient {
this.#client = client; this.#client = client;
} }
public query<R>(sql: string, args?: any[] | Record<string, any>): Promise<QueryResult<R[]>> { return this.#client.query(args ? formatSQL(sql, args) : sql); } public query<R extends QueryResultRow = any>(sql: string, args?: any[] | Record<string, any>): Promise<QueryResult<R>> { return this.#client.query(args ? formatSQL(sql, args) : sql); }
public async trans() { public async trans() {
@ -69,23 +94,16 @@ class PostgresClient implements IPostgresClient {
public get transOn() { return this.#transOn; } public get transOn() { return this.#transOn; }
public release() { this.#client.release(); } public release() { this.#client.release(); }
public select<E extends BasicEntity>(Entity: Class<E>, alias?: string): ISelectBuilder<E> { return new SelectBuilder(this.query.bind(this), Entity, alias); } public select<E extends BasicEntity>(Entity: Class<E>, alias?: string): ISelectBuilder<E> { return new SelectBuilder(this.query.bind(this), Entity, alias); }
public insert<E extends BasicEntity>(Entity: Class<E>): IInsertBuilder<E> { return new InsertBuilder(this.query.bind(this), Entity); } public insert<E extends BasicEntity>(Entity: Class<E>): IInsertBuilder<E> { return new InsertBuilder(this.query.bind(this), Entity); }
public update<E extends BasicEntity>(Entity: Class<E>): IUpdateBuilder<E> { return new UpdateBuilder(this.query.bind(this), Entity); } public update<E extends BasicEntity>(Entity: Class<E>): IUpdateBuilder<E> { return new UpdateBuilder(this.query.bind(this), Entity); }
public delete<E extends BasicEntity>(Entity: Class<E>): IDeleteBuilder<E> { return new DeleteBuilder(this.query.bind(this), Entity); }
public del<E extends BasicEntity>(Entity: Class<E>): IDeleteBuilder<E> { return new DeleteBuilder(this.query.bind(this), Entity); }
}; };
/** 数据库操作相关 */
export namespace database {
let pool: Pool | null = null; let pool: Pool | null = null;
let confLoader!: () => IPostgresConfig let confLoader!: () => IPostgresConfig
function getPool() {
function getPool() {
if (!pool) { if (!pool) {
const conf = confLoader(); const conf = confLoader();
pool = new Pool({ pool = new Pool({
@ -99,56 +117,18 @@ export namespace database {
}); });
} }
return pool; return pool;
} }
/**
* 配置数据库 export const database: IPostgresDatabase = {
* @param loader 配置加载器 config(loader: () => IPostgresConfig) {
*/
export function config(loader: () => IPostgresConfig) {
confLoader = loader; confLoader = loader;
} },
/** async connect(callback) {
* 直接执行sql语句
* @param sql sql语句
* @param args sql参数
*/
export function query<R extends QueryResultRow>(sql: string, args?: any[] | Record<string, any>) { return getPool().query<R>(args ? formatSQL(sql, args) : sql); }
/**
* 查询实体
* @param Entity 实体
* @param alias 别名
*/
export function select<E extends BasicEntity>(Entity: Class<E>, alias?: string): ISelectBuilder<E> { return new SelectBuilder(database.query, Entity, alias); }
/**
* 进行数据库插入
* @param Entity 实体
*/
export function insert<E extends BasicEntity>(Entity: Class<E>): IInsertBuilder<E> { return new InsertBuilder(database.query, Entity); }
/**
* 进行实体更新
* @param Entity 实体
*/
export function update<E extends BasicEntity>(Entity: Class<E>): IUpdateBuilder<E> { return new UpdateBuilder(database.query, Entity); }
/**
* 进行实体删除
* @param Entity 实体
*/
export function del<E extends BasicEntity>(Entity: Class<E>): IDeleteBuilder<E> { return new DeleteBuilder(database.query, Entity); }
/**
* 连接数据库
* @param callback 回调
*/
export async function connect<R>(callback: (client: PostgresClient) => R | Promise<R>): Promise<R> {
const conn = await getPool().connect(); const conn = await getPool().connect();
const client = new PostgresClient(conn); const client = new PostgresClient(conn);
let ret: R; let ret: any;
try { try {
ret = await callback(client); ret = await callback(client);
if (client.transOn) await client.query("commit"); if (client.transOn) await client.query("commit");
@ -162,21 +142,22 @@ export namespace database {
} }
return ret; return ret;
} },
/** async transaction(callback) {
* 执行事务操作 return this.connect(async (client) => {
* @param callback 回调
*/
export function transaction<R>(callback: (client: PostgresClient) => R | Promise<R>): Promise<R> {
return connect(async (client) => {
await client.trans(); await client.trans();
return callback(client); return callback(client);
}); });
}; },
/** 关闭数据库 */ close() {
export function close() {
if (pool) pool.end().catch((err) => console.error(err)); if (pool) pool.end().catch((err) => console.error(err));
} },
}
query(sql, args) { return getPool().query(args ? formatSQL(sql, args) : sql); },
select(Entity, alias) { return new SelectBuilder(this.query.bind(this), Entity, alias); },
insert(Entity) { return new InsertBuilder(this.query.bind(this), Entity); },
update(Entity) { return new UpdateBuilder(this.query.bind(this), Entity); },
delete(Entity) { return new DeleteBuilder(this.query.bind(this), Entity); },
};

View File

@ -2,7 +2,7 @@ import moment from "moment";
import { DatabaseError } from "./error"; import { DatabaseError } from "./error";
import { TSVector } from "./type"; import { TSVector } from "./type";
import { escapeID, escapeValue } from "./util"; import { escapeID, escapeValue } from "./util";
import type { ArrayType, ArrayTypeSelf, BasicFieldName, Class, Decorators, IDLikeFieldName, Values } from "./types"; import type { Class, Decorators, IDLikeFieldName } from "./types";
export interface IEntityFieldConfig { export interface IEntityFieldConfig {
/** 字段名 */ /** 字段名 */
@ -68,7 +68,9 @@ export class BasicEntity {
public pick<K extends BasicFieldName<this>>(...keys: K[]): Pick<this, K> { public pick<K extends BasicFieldName<this>>(...keys: K[]): Pick<this, K> {
const result: any = {}; const result: any = {};
for (const k of keys) { for (const k of keys) {
result[k] = this[k]; let val: any = this[k];
if (typeof val?.toPickData === "function") val = val.toPickData();
result[k] = val;
} }
return result; return result;
} }
@ -82,7 +84,9 @@ export class BasicEntity {
const config = getEntityConfigWithoutCheck(this.constructor)!; const config = getEntityConfigWithoutCheck(this.constructor)!;
for (const col of config.fields) { for (const col of config.fields) {
if (keys.includes(col.prop as K)) continue; if (keys.includes(col.prop as K)) continue;
result[col.prop] = this[col.prop as K]; let val: any = this[col.prop as K];
if (typeof val?.toPickData === "function") val = val.toPickData();
result[col.prop as K] = val;
} }
return result; return result;
} }

View File

@ -1,3 +1,5 @@
import "./types";
export { export {
Table, Table,
Field, IntColumn, FloatColumn, StringColumn, BooleanColumn, DateColumn, DateTimeColumn, JsonColumn, Field, IntColumn, FloatColumn, StringColumn, BooleanColumn, DateColumn, DateTimeColumn, JsonColumn,

View File

@ -1,10 +1,10 @@
import { Pool, PoolClient, QueryResult, QueryResultRow } from "pg"; import { QueryResult, QueryResultRow } from "pg";
import { BasicEntity, IEntityFieldConfig, IEntityJoinConfig, getEntityConfig, getField, getJoin, getTableName } from "./entity"; import { BasicEntity, IEntityFieldConfig, IEntityJoinConfig, getEntityConfig, getField, getJoin, getTableName } from "./entity";
import type { JoinaFieldName, Class, ArrayTypeSelf, Values } from "./types"; import type { JoinaFieldName, Class, ArrayTypeSelf, Values, StringArgs, SQLStringArgs } from "./types";
import { escapeID, escapeValue, formatSQL } from "./util"; import { escapeID, escapeValue, formatSQL } from "./util";
//编译器原因,不加此段代码会出错,具体原因未知,请保持定义和entity.ts重的同名定义一致 //编译器原因,不加此段代码会出错,具体原因未知,请保持定义和types.ts重的同名定义一致
/** 取基础类型的字段名 */ /** 取基础类型的字段名 */
export type BasicFieldName<T extends BasicEntity> = Values<{ [P in keyof T]: export type BasicFieldName<T extends BasicEntity> = Values<{ [P in keyof T]:
//忽略never //忽略never
@ -25,7 +25,6 @@ interface IQueryFunc<R extends QueryResultRow = any> {
type DataOrCustom<T> = T | (() => string); type DataOrCustom<T> = T | (() => string);
type StringArgs<Str extends string, P extends string, S extends string> = Str extends `${string}${P}${infer R}${S}${infer T}` ? (R | StringArgs<T, P, S>) : never;
type BasicWhere<V> = never type BasicWhere<V> = never
@ -64,7 +63,7 @@ interface IWhereFn<E extends BasicEntity> {
*/ */
where(option: ConditionOption<E>): this where(option: ConditionOption<E>): this
where(sql: string, args?: any[]): this where(sql: string, args?: any[]): this
where<S extends string>(sql: S, args: { [P in StringArgs<S, "{:", "}">]: any }): this where<S extends string>(sql: S, args: { [P in SQLStringArgs<S>]: any }): this
/** /**
* 添加搜索条件 * 添加搜索条件

View File

@ -14,9 +14,10 @@ export namespace Decorators {
export type ParamDecorator<T> = (target: T, propertyKey: string, parameterIndex: number) => any; export type ParamDecorator<T> = (target: T, propertyKey: string, parameterIndex: number) => any;
} }
declare global {
/** 取基础类型的字段名 */ /** 取基础类型的字段名 */
export type BasicFieldName<T extends BasicEntity> = Values<{ [P in keyof T]: type BasicFieldName<T extends BasicEntity> = Values<{ [P in keyof T]:
//忽略never //忽略never
Exclude<T[P], null | undefined> extends never ? never : ( Exclude<T[P], null | undefined> extends never ? never : (
//忽略函数 //忽略函数
@ -27,7 +28,8 @@ export type BasicFieldName<T extends BasicEntity> = Values<{ [P in keyof T]:
) )
) )
) )
}>; }>;
}
/** 可以用作ID的字段名称 */ /** 可以用作ID的字段名称 */
@ -60,3 +62,19 @@ export type OneJoinFieldName<T extends BasicEntity> = Values<{ [P in keyof T]:
/** 可以Join的字段名 */ /** 可以Join的字段名 */
export type JoinaFieldName<T extends BasicEntity> = OneJoinFieldName<T> | ManyJoinFieldName<T>; export type JoinaFieldName<T extends BasicEntity> = OneJoinFieldName<T> | ManyJoinFieldName<T>;
/**
* SQL字符串参数
*
* @param Str SQL字符串
* @param P 前分割
* @param S 后分割
*/
export type StringArgs<Str extends string, P extends string, S extends string> = Str extends `${string}${P}${infer R}${S}${infer T}` ? (R | StringArgs<T, P, S>) : never;
/**
* SQL字符串参数
*
* @param Str SQL字符串
*/
export type SQLStringArgs<Str extends string> = StringArgs<Str, "{:", "}">;