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直接面向数据库