typeorm之MysqlDriver

xiaoxiao2021-02-28  37

src/driver/mysql/MysqlDriver.ts //Mysql数据库驱动类 export class MysqlDriver implements Driver { //连接 connection: Connection; //mysql底层库的导出 mysql: any; //连接池,非复制模式 pool: any; //连接池集群,在复制模式下使用 poolCluster: any; //连接选项 options: MysqlConnectionOptions; //数据库名称,在复制模式下为主数据库名称 database?: string; //是否为复制模式 isReplicated: boolean = false; //是否支持树表 treeSupport = true; //支持的列数据类型 supportedDataTypes: ColumnType[] = [ //整型 "int","tinyint","smallint","mediumint","bigint", //浮点数、小数 "float","double","decimal", //日期 "date","datetime","timestamp","time","year", //字符串 "char","varchar", //二进制、大文本 "blob","text","tinyblob","tinytext","mediumblob","mediumtext","longblob","longtext", //枚举 "enum", //json "json" ]; //被mysql驱动支持的带有长度属性的列数据类型 withLengthColumnTypes: ColumnType[] = ["int","tinyint","smallint","mediumint","bigint","char","varchar","blob","text"]; //映射列类型,即使用以下键作为列名时,会自动推断出列数据类型 mappedDataTypes: MappedColumnTypes = { createDate: "datetime", createDatePrecision: 6, createDateDefault: "CURRENT_TIMESTAMP(6)", updateDate: "datetime", updateDatePrecision: 6, updateDateDefault: "CURRENT_TIMESTAMP(6)", version: "int", treeLevel: "int", migrationName: "varchar", migrationTimestamp: "bigint", cacheId: "int", cacheIdentifier: "varchar", cacheTime: "bigint", cacheDuration: "int", cacheQuery: "text", cacheResult: "text", }; //数据类型默认长度,其中整数类型长度为显示长度 dataTypeDefaults: DataTypeDefaults = { varchar: { length: 255 }, int: { length: 11 }, tinyint: { length: 4 }, smallint: { length: 5 }, mediumint: { length: 9 }, bigint: { length: 20 }, year: { length: 4 } }; //使用连接构造驱动 constructor(connection: Connection) { this.connection = connection; this.options = connection.options as MysqlConnectionOptions; this.isReplicated = this.options.replication ? true : false; //加载mysql库 this.loadDependencies(); } //连接方法 async connect(): Promise<void> { //复制模式 if (this.options.replication) { //使用mysql包创建池集群 this.poolCluster = this.mysql.createPoolCluster(this.options.replication); //遍历从服务器选项 this.options.replication.slaves.forEach((slave, index) => { //添加从服务器、选项键值对 this.poolCluster.add("SLAVE" + index, this.createConnectionOptions(this.options, slave)); }); //添加主服务器、选项键值对 this.poolCluster.add("MASTER", this.createConnectionOptions(this.options, this.options.replication.master)); //数据库名为主服务器数据库名 this.database = this.options.replication.master.database; } //非复制模式创建池就好了 else { //创建连接池 this.pool = await this.createPool(this.createConnectionOptions(this.options, this.options)); //设置数据库名 this.database = this.options.database; } } //连接之后神码也不做 afterConnect(): Promise<void> { return Promise.resolve(); } //断开连接 async disconnect(): Promise<void> { //即不存在池集群也不存在池,抛出异常 if (!this.poolCluster && !this.pool) return Promise.reject(new ConnectionIsNotSetError("mysql")); //如果存在池集群 if (this.poolCluster) { return new Promise<void>((ok, fail) => { //调用end方法 this.poolCluster.end((err: any) => err ? fail(err) : ok()); this.poolCluster = undefined; }); } //池也是调用end方法 if (this.pool) { return new Promise<void>((ok, fail) => { this.pool.end((err: any) => { if (err) return fail(err); this.pool = undefined; ok(); }); }); } } //schema构建器,用来构建、同步schema createSchemaBuilder() { return new RdbmsSchemaBuilder(this.connection); } //创建查询运行器,mode用来决定当开启复制模式时,如何获取底层数据库连接 createQueryRunner(mode: "master"|"slave" = "master") { return new MysqlQueryRunner(this, mode); } //使用键值对替换sql中参数,主要用来替换sql中使用:xxx占位符的参数 //将:xxx替换为?,返回对应的值数组 escapeQueryWithParameters(sql: string, parameters: ObjectLiteral): [string, any[]] { if (!parameters || !Object.keys(parameters).length) return [sql, []]; //已转义数组 const escapedParameters: any[] = []; //遍历键,转义,拼接,获取正则表达式 const keys = Object.keys(parameters).map(parameter => "(:" + parameter + "\\b)").join("|"); //匹配、替换,key为匹配字符串 sql = sql.replace(new RegExp(keys, "g"), (key: string) => { //获取对应值,去掉: const value = parameters[key.substr(1)]; if (value instanceof Function) { //如果是函数替换为返回值 return value(); } else { //添加到已转义数组 escapedParameters.push(parameters[key.substr(1)]); //普通情况替换为? return "?"; } }); //返回替换为?之后的sql与值数组 return [sql, escapedParameters]; } //转义列名,就是加上单引号 escape(columnName: string): string { return "`" + columnName + "`"; } //预处理要持久化的值,即插入、更新之前将js类型值转化为数据库值 preparePersistentValue(value: any, columnMetadata: ColumnMetadata): any { if (columnMetadata.transformer) //to方法 value = columnMetadata.transformer.to(value); if (value === null || value === undefined) return value; //Boolean则返回1、0 if (columnMetadata.type === Boolean) { return value === true ? 1 : 0; } else if (columnMetadata.type === "date") { //date也是字符串 return DateUtils.mixedDateToDateString(value); } else if (columnMetadata.type === "time") { //time也是字符串 return DateUtils.mixedDateToTimeString(value); } else if (columnMetadata.type === "json") { //json返回json字符串 return JSON.stringify(value); } else if (columnMetadata.type === "datetime" || columnMetadata.type === Date) { //返回Date对象 return DateUtils.mixedDateToDate(value, true); } else if (columnMetadata.isGenerated && columnMetadata.generationStrategy === "uuid" && !value) { //uuid,注意只有当未指定value时才会调用 return RandomGenerator.uuid4(); } else if (columnMetadata.type === "simple-array") { //转化数组为字符串 return DateUtils.simpleArrayToString(value); } return value; } //转化数据库查询值为js值 //即规范相应类型,只能输出指定类型值 //转化js值为要存储的值 prepareHydratedValue(value: any, columnMetadata: ColumnMetadata): any { //列包含transformer if (columnMetadata.transformer) //调用transformer的from方法 value = columnMetadata.transformer.from(value); if (value === null || value === undefined) return value; if (columnMetadata.type === Boolean) { //boolean,返回true、false return value ? true : false; } else if (columnMetadata.type === "datetime" || columnMetadata.type === Date) { //日期时间或者Date,返回一个Date对象 return DateUtils.normalizeHydratedDate(value); } else if (columnMetadata.type === "date") { //只有日期,返回一个字符串 return DateUtils.mixedDateToDateString(value); } else if (columnMetadata.type === "json") { //json类型则返回解析对象 return typeof value === "string" ? JSON.parse(value) : value; } else if (columnMetadata.type === "time") { //time类型也是返回字符串 return DateUtils.mixedTimeToString(value); } else if (columnMetadata.type === "simple-array") { //将字符串转化为数组 return DateUtils.stringToSimpleArray(value); } return value; } //规范化列类型,从给定元数据,只有类型,没有长度、精度、标度等,也不知道这些参数干嘛用 normalizeType(column: { type: ColumnType, length?: number | string, precision?: number, scale?: number }): string { //可以从TypeScript类型推断数据库类型 //Number、integer为int if (column.type === Number || column.type === "integer") { return "int"; } //String为varchar else if (column.type === String) { return "varchar"; } //Date为datetime else if (column.type === Date) { return "datetime"; } //Buffer为blob else if ((column.type as any) === Buffer) { return "blob"; } //Boolean为tinyint else if (column.type === Boolean) { return "tinyint"; } //uuid也是varchar else if (column.type === "uuid") { return "varchar"; } //简单数组,为text,simple-array为typeorm的类型 else if (column.type === "simple-array") { return "text"; } else { return column.type as string || ""; } } //规范化列默认值 normalizeDefault(column: ColumnMetadata): string { //number类型,转化为字符串 if (typeof column.default === "number") { return "" + column.default; } //boolean类型,转化为字符串1、2 else if (typeof column.default === "boolean") { return column.default === true ? "1" : "0"; } //函数类型,获取返回值 else if (typeof column.default === "function") { return column.default(); } else if (typeof column.default === "string") { return `'${column.default}'`; } else { return column.default; } } //规范化isUnique,其实就是获取这个列是否为unique normalizeIsUnique(column: ColumnMetadata): boolean { //列本身isUnique return column.isUnique || //或者在列的实体元数据的索引中查找unique且列长度为1且列元数据相等的索引存在 !!column.entityMetadata.indices.find(index => index.isUnique && index.columns.length === 1 && index.columns[0] === column); } //计算列长度 getColumnLength(column: ColumnMetadata): string { if (column.length) return column.length; //规范化列类型 const normalizedType = this.normalizeType(column) as string; if (this.dataTypeDefaults && this.dataTypeDefaults[normalizedType] && this.dataTypeDefaults[normalizedType].length) //返回默认长度 return this.dataTypeDefaults[normalizedType].length!.toString(); return ""; } //创建TableColumn的完整类型 createFullType(column: TableColumn): string { let type = column.type; if (column.length) { //长度 type += "(" + column.length + ")"; } else if (column.precision && column.scale) { //精度、标度 type += "(" + column.precision + "," + column.scale + ")"; } else if (column.precision) { type += "(" + column.precision + ")"; } else if (column.scale) { type += "(" + column.scale + ")"; } else if (this.dataTypeDefaults && this.dataTypeDefaults[column.type] && this.dataTypeDefaults[column.type].length) { //默认长度 type += "(" + this.dataTypeDefaults[column.type].length!.toString() + ")"; } //如果是数组,加上array,mysql支持array????? if (column.isArray) type += " array"; return type; } //获取主服务器连接 obtainMasterConnection(): Promise<any> { return new Promise<any>((ok, fail) => { if (this.poolCluster) { this.poolCluster.getConnection("MASTER", (err: any, dbConnection: any) => { err ? fail(err) : ok(dbConnection); }); } else if (this.pool) { this.pool.getConnection((err: any, dbConnection: any) => { err ? fail(err) : ok(dbConnection); }); } else { fail(new Error(`Connection is not established with mysql database`)); } }); } //获取一个从服务器底层连接 obtainSlaveConnection(): Promise<any> { //池集群不存在,即非复制模式,直接获取主服务器底层连接 if (!this.poolCluster) return this.obtainMasterConnection(); return new Promise<any>((ok, fail) => { //使用连接池获取连接,注意名称为SLAVE*,那么获取从服务器连接的策略在哪里实现 this.poolCluster.getConnection("SLAVE*", (err: any, dbConnection: any) => { err ? fail(err) : ok(dbConnection); }); }); } //加载驱动依赖 protected loadDependencies(): void { try { //使用平台工具加载mysql包 this.mysql = PlatformTools.load("mysql"); } catch (e) { try { //备用包 this.mysql = PlatformTools.load("mysql2"); } catch (e) { throw new DriverPackageNotInstalledError("Mysql", "mysql"); } } } //创建连接选项,使用连接选项、认证选项,虽然连接选项本身包含了认证选项,但是这里将其内容分离作为两个参数使用 protected createConnectionOptions(options: MysqlConnectionOptions, credentials: MysqlConnectionCredentialsOptions): Promise<any> { //解析url,分解为类型、域名、端口号、用户名、密码、数据库名,与现有认证选项合并 credentials = Object.assign(credentials, DriverUtils.buildDriverOptions(credentials)); //合并各种选项 return Object.assign({}, { charset: options.charset, timezone: options.timezone, connectTimeout: options.connectTimeout, insecureAuth: options.insecureAuth, supportBigNumbers: options.supportBigNumbers, bigNumberStrings: options.bigNumberStrings, dateStrings: options.dateStrings, debug: options.debug, trace: options.trace, multipleStatements: options.multipleStatements, flags: options.flags }, { host: credentials.host, user: credentials.username, password: credentials.password, database: credentials.database, port: credentials.port, ssl: options.ssl }, options.extra || {}); } //创建连接池,使用指定选项 protected createPool(connectionOptions: any): Promise<any> { //使用底层类创建连接池 const pool = this.mysql.createPool(connectionOptions); return new Promise<void>((ok, fail) => { //使用池尝试获取连接 pool.getConnection((err: any, connection: any) => { //如果发生错误,关闭池 if (err) return pool.end(() => fail(err)); //如果成功释放连接 connection.release(); //返回池 ok(pool); }); }); } }

 

 

1.驱动实现类指定了相应数据库的数据类型

2.构造函数中就获取了底层mysql导出

3.根据主从复制是否开启,获取连接方式也不一样:池、池集群

4.创建查询运行器,要通过它,因为Driver、Query、Runner都是依赖于数据库的

5.数据库类型、值与js类型、值之间的转化,说明Driver、QueryRunner是有分工的.

Driver面向orm中类型、值的转化

QueryRunner直接面向数据库

 

转载请注明原文地址: https://www.6miu.com/read-2629258.html

最新回复(0)