更新项:
1. 实体的pick和omit增加对toPickData方法的支持 2. database和PostgresClient增加同一的类型IDatabase
This commit is contained in:
@ -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": {},
|
||||||
|
161
src/database.ts
161
src/database.ts
@ -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,86 +94,41 @@ 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({
|
host: conf.host,
|
||||||
host: conf.host,
|
port: conf.port,
|
||||||
port: conf.port,
|
user: conf.user,
|
||||||
user: conf.user,
|
password: conf.password,
|
||||||
password: conf.password,
|
database: conf.database,
|
||||||
database: conf.database,
|
max: conf.max,
|
||||||
max: conf.max,
|
ssl: false,
|
||||||
ssl: false,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
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); },
|
||||||
|
};
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加搜索条件
|
* 添加搜索条件
|
||||||
|
40
src/types.ts
40
src/types.ts
@ -14,20 +14,22 @@ 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 : (
|
||||||
//忽略函数
|
//忽略函数
|
||||||
Exclude<T[P], null | undefined> extends Function ? never : (
|
Exclude<T[P], null | undefined> extends Function ? never : (
|
||||||
//忽略实体
|
//忽略实体
|
||||||
ArrayTypeSelf<Exclude<T[P], null | undefined>> extends BasicEntity ? never : (
|
ArrayTypeSelf<Exclude<T[P], null | undefined>> extends BasicEntity ? never : (
|
||||||
P
|
P
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
}>;
|
||||||
}>;
|
}
|
||||||
|
|
||||||
|
|
||||||
/** 可以用作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, "{:", "}">;
|
||||||
|
Reference in New Issue
Block a user