src/connection/Connection.ts
//一个连接对应一个数据库,根据数据库类型,可以创建连接池
export class Connection {
//连接名称,默认为default
readonly name: string;
//连接选项
readonly options: ConnectionOptions;
//是否已经连接
readonly isConnected = false;
//数据库驱动,对应了不同数据库,执行数据库操作的最基本对象QueryRunner就是使用Driver创建的
readonly driver: Driver;
//实体管理器,一个连接对应一个实体管理器,实体关联器是Repository用来执行数据库操作的内部对象
//实体管理器内部还是使用Connection创建QueryRunner、QueryBuilder等来执行操作
//所以本质上还是使用Driver
readonly manager: EntityManager;
//命名策略,当用户未指定时,用来确定表名、列名等与实体类的关系
readonly namingStrategy: NamingStrategyInterface;
//日志记录器
readonly logger: Logger;
//迁移对象数组,每个迁移类有一个up一个down方法
//迁移的up方法会在connect方法中执行
readonly migrations: MigrationInterface[] = [];
//实体订阅对象数组,每个实体订阅类上面有一些与数据库触发器类似的方法
readonly subscribers: EntitySubscriberInterface[] = [];
//连接对应数据库中,所有注册实体的元数据
readonly entityMetadatas: EntityMetadata[] = [];
//查询结果缓存,这里为接口,实现中可以缓存到当前数据库,也可以缓存到Redis
readonly queryResultCache?: QueryResultCache;
//使用连接选项初始化
constructor(options: ConnectionOptions) {
//默认连接名称为default
this.name = options.name || "default";
this.options = options;
//日志记录器
this.logger = new LoggerFactory().create(this.options.logger, this.options.logging);
//由驱动工厂创建驱动,根据连接类型不同,驱动实现类也不同
this.driver = new DriverFactory().create(this);
//创建实体管理器,根据数据库类型不同,也会创建不同类型实体管理器,如Mongodb、sqljs等
this.manager = this.createEntityManager();
//创建命名策略
this.namingStrategy = options.namingStrategy || new DefaultNamingStrategy();
//根据选项设置查询结果缓存对象,由工厂根据选项创建不同的实现类对象
this.queryResultCache = options.cache ? new QueryResultCacheFactory(this).create() : undefined;
}
//获取mongodb实体管理器
get mongoManager(): MongoEntityManager {
//如果实体管理器类型不是Mongodb的,抛出错误
if (!(this.manager instanceof MongoEntityManager))
throw new Error(`MongoEntityManager is only available for MongoDB databases.`);
return this.manager as MongoEntityManager;
}
//获取sqljs实体管理器
get sqljsManager(): SqljsEntityManager {
if (!(this.manager instanceof SqljsEntityManager))
throw new Error(`SqljsEntityManager is only available for Sqljs databases.`);
return this.manager as SqljsEntityManager;
}
//执行连接
//这一步连接了驱动、构建了元数据、根据选项对数据库进行删除、同步操作、运行迁移
async connect(): Promise {
//如果已经连接,抛出异常
if (this.isConnected)
throw new CannotConnectAlreadyConnectedError(this.name);
//使用驱动连接
await this.driver.connect();
//如果由查询结果缓存,连接它
if (this.queryResultCache)
await this.queryResultCache.connect();
//设置连接状态
Object.assign(this, { isConnected: true });
try {
//构建元数据
this.buildMetadatas();
//调用连接后方法
await this.driver.afterConnect();
//如果选项设置了dropSchema,则删除对应数据库表
if (this.options.dropSchema)
await this.dropDatabase();
//如果由同步选项,则将当前实体信息与数据库表同步
if (this.options.synchronize)
await this.synchronize();
//运行迁移
if (this.options.migrationsRun)
await this.runMigrations();
} catch (error) {
await this.close();
throw error;
}
return this;
}
//关闭连接
async close(): Promise {
if (!this.isConnected)
throw new CannotExecuteNotConnectedError(this.name);
//使用驱动断开连接
await this.driver.disconnect();
//断开查询缓存
if (this.queryResultCache)
await this.queryResultCache.disconnect();
//设置连接状态为false
Object.assign(this, { isConnected: false });
}
//实体与数据库表结构同步,参数为是否先删除数据库表,connect中调用只是同步、不删除
async synchronize(dropBeforeSync: boolean = false): Promise {
if (!this.isConnected)
throw new CannotExecuteNotConnectedError(this.name);
//先删除数据库
if (dropBeforeSync)
await this.dropDatabase();
//使用驱动获取schema构建器
const schemaBuilder = this.driver.createSchemaBuilder();
//构建表结构,这一步查询当前连接中实体类所对应表的列类型,与实体对比,进行修改
await schemaBuilder.build();
}
//删除数据库表
async dropDatabase(): Promise {
//获取查询运行器
const queryRunner = await this.createQueryRunner("master");
//过滤出所有元数据中的schema
const schemas = this.entityMetadatas
.filter(metadata => metadata.schema)
.map(metadata => metadata.schema!);
//如果是SqlServer、Mysql驱动
if (this.driver instanceof SqlServerDriver || this.driver instanceof MysqlDriver) {
//获取数据库名数组,第一个元素为主数据库名,即默认情况下数据库名
const databases: string[] = this.driver.database ? [this.driver.database] : [];
//遍历实体元数据,获取实体中出现的所有数据库名
this.entityMetadatas.forEach(metadata => {
//如果实体元数据中指定数据库不是连接中指定的数据库名,加入数组中
if (metadata.database && databases.indexOf(metadata.database) === -1)
databases.push(metadata.database);
});
//使用查询运行器删除数据库中的表
await PromiseUtils.runInSequence(databases, database => queryRunner.clearDatabase(schemas, database));
} else {
await queryRunner.clearDatabase(schemas);
}
await queryRunner.release();
}
//允许迁移
async runMigrations(): Promise {
if (!this.isConnected)
throw new CannotExecuteNotConnectedError(this.name);
//获取迁移执行器
const migrationExecutor = new MigrationExecutor(this);
//执行迁移,其实就是执行所有迁移对象的up方法
await migrationExecutor.executePendingMigrations();
}
//移除最后一次迁移,即执行最后一个迁移对象的down方法
async undoLastMigration(): Promise {
if (!this.isConnected)
throw new CannotExecuteNotConnectedError(this.name);
const migrationExecutor = new MigrationExecutor(this);
await migrationExecutor.undoLastMigration();
}
//是否包含指定类,schema名的元数据
hasMetadata(target: Function|string): boolean {
return !!this.findMetadata(target);
}
//获取指定实体类或者schema的元数据
getMetadata(target: Function|string): EntityMetadata {
const metadata = this.findMetadata(target);
if (!metadata)
throw new EntityMetadataNotFound(target);
return metadata;
}
//获取指定实体类的仓库
getRepository(target: ObjectType|string): Repository {
//由实体管理器创建仓库
return this.manager.getRepository(target);
}
//获取指定实体类的树状仓库
getTreeRepository(target: ObjectType|string): TreeRepository {
return this.manager.getTreeRepository(target);
}
//获取mongodb仓库,只有驱动为相应类型才可以获取
getMongoRepository(target: ObjectType|string): MongoRepository {
if (!(this.driver instanceof MongoDriver))
throw new Error(`You can use getMongoRepository only for MongoDB connections.`);
return this.manager.getRepository(target) as any;
}
//获取定制仓库
getCustomRepository(customRepository: ObjectType): T {
return this.manager.getCustomRepository(customRepository);
}
//在一个事务中运行指定参数方法,方法使用实体管理器进行数据库操作,这些操作都是在一个事务当中
//好像不需要显式开启事务、提交、回滚,加入抛出错误,会自动回滚
async transaction(runInTransaction: (entityManger: EntityManager) => Promise): Promise {
return this.manager.transaction(runInTransaction);
}
//执行原始sql查询,返回原始查询结果
async query(query: string, parameters?: any[], queryRunner?: QueryRunner): Promise {
//mongodb没有原始sql查询
if (this instanceof MongoEntityManager)
throw new Error(`Queries aren't supported by MongoDB.`);
//查询运行器已释放,抛出错误
if (queryRunner && queryRunner.isReleased)
throw new QueryRunnerProviderAlreadyReleasedError();
//未指定查询运行器时,使用master查询运行器,即从主数据库获取的查询运行器
const usedQueryRunner = queryRunner || this.createQueryRunner("master");
try {
//使用查询运行器允许原始sql查询
return await usedQueryRunner.query(query, parameters); // await is needed here because we are using finally
} finally {
if (!queryRunner)
//最后释放查询运行器
await usedQueryRunner.release();
}
}
//获取指定实体类的查询构建器,并对类设置别名,查询构建器也是使用查询运行器创建
createQueryBuilder(entityClass: ObjectType|Function|string, alias: string, queryRunner?: QueryRunner): SelectQueryBuilder;
createQueryBuilder(queryRunner?: QueryRunner): SelectQueryBuilder;
createQueryBuilder(entityOrRunner?: ObjectType|Function|string|QueryRunner, alias?: string, queryRunner?: QueryRunner): SelectQueryBuilder {
//Mongodb没有查询构建器
if (this instanceof MongoEntityManager)
throw new Error(`Query Builder is not supported by MongoDB.`);
//如果设置别名
if (alias) {
//获取指定实体类元数据
const metadata = this.getMetadata(entityOrRunner as Function|string);
//返回select查询构建器,并设置别名
return new SelectQueryBuilder(this, queryRunner)
//别名使用select设置
.select(alias)
.from(metadata.target, alias);
}
//未设置别名
else {
return new SelectQueryBuilder(this, entityOrRunner as QueryRunner|undefined);
}
}
//创建查询运行器,在一个单独的数据库连接中进行查询
//mode代表了连接的目的,在开启主从复制模式时,如果执行写操作为master,执行读操作为slave
createQueryRunner(mode: "master"|"slave" = "master"): QueryRunner {
//使用驱动创建查询构建器
const queryRunner = this.driver.createQueryRunner(mode);
//生成实体管理器
const manager = this.createEntityManager(queryRunner);
//实体管理器为查询运行器的属性,他俩互相有对方的引用
Object.assign(queryRunner, { manager: manager });
return queryRunner;
}
//获取指定实体的many-to-many关系的连接表元数据
//第一个参数为实体类,第二个参数为many-to-many属性
getManyToManyMetadata(entityTarget: Function|string, relationPropertyPath: string) {
//获取实体元数据上指定属性的关系元数据
const relationMetadata = this.getMetadata(entityTarget).findRelationWithPropertyPath(relationPropertyPath);
if (!relationMetadata)
throw new Error(`Relation "${relationPropertyPath}" was not found in ${entityTarget} entity.`);
//关系元数据不是many-to-many类型
if (!relationMetadata.isManyToMany)
throw new Error(`Relation "${entityTarget}#${relationPropertyPath}" does not have a many-to-many relationship.` +
`You can use this method only on many-to-many relations.`);
//返回关系元数据上的连接实体元数据
return relationMetadata.junctionEntityMetadata;
}
//创建实体管理器
createEntityManager(queryRunner?: QueryRunner): EntityManager {
//这里根据不同连接类型创建不同类型实体管理器,如Mongodb、sqljs
return new EntityManagerFactory().create(this, queryRunner);
}
//查找指定实体类或者表名的元数据
protected findMetadata(target: Function|string): EntityMetadata|undefined {
return this.entityMetadatas.find(metadata => {
//如果类构造函数相等返回它
if (metadata.target === target)
return true;
//字符串参数
if (typeof target === "string") {
//名称包含.
if (target.indexOf(".") !== -1) {
//根据表路径查找
return metadata.tablePath === target;
}
//不包含.
else {
//根据表名查找
return metadata.name === target || metadata.tableName === target;
}
}
return false;
});
}
//构建连接中所有注册实体的元数据
protected buildMetadatas(): void {
//连接元数据构建器
const connectionMetadataBuilder = new ConnectionMetadataBuilder(this);
//实体元数据验证器
const entityMetadataValidator = new EntityMetadataValidator();
//根据选项创建订阅者
const subscribers = connectionMetadataBuilder.buildSubscribers(this.options.subscribers || []);
//设置订阅者
Object.assign(this, { subscribers: subscribers });
//根据选项中实体构建实体元数据
const entityMetadatas = connectionMetadataBuilder.buildEntityMetadatas(this.options.entities || [], this.options.entitySchemas || []);
Object.assign(this, { entityMetadatas: entityMetadatas });
//根据选项构建迁移对象
const migrations = connectionMetadataBuilder.buildMigrations(this.options.migrations || []);
Object.assign(this, { migrations: migrations });
//验证元数据是否符合数据库驱动
entityMetadataValidator.validateMany(this.entityMetadatas, this.driver);
}
}
1.连接中包含了数据库驱动,不同数据库类型有不同数据库驱动
2.运行查询的三个类
QueryRuner:查询运行器,对应了一个真实的数据库连接,即一个连接池中的连接,如mysql包连接池中的连接
EntityManager:使用查询运行器创建
QueryBuilder:也是使用查询运行器创建
还有一个query方法来执行原始sql查询,也是使用查询运行器查询