-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
548db3a
commit e307ad3
Showing
11 changed files
with
342 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,175 @@ | ||
import mysql from 'mysql' | ||
export class MySqlClient {} | ||
import mysql, { PoolConfig } from 'mysql' | ||
import { MySqlConnection } from './mysql-connection' | ||
import { MySqlException } from './mysql-exception' | ||
import adapter, { MySqlValueAdapter } from './mysql-value-adapter' | ||
export class MySqlClient { | ||
private pool: mysql.Pool | ||
private adapter: MySqlValueAdapter = adapter | ||
public MySqlClient(config: PoolConfig | string) { | ||
if (!config) { | ||
config = process.env.MYSQL_CONNECTION | ||
} | ||
if (!config) { | ||
throw new Error('必须配置 MySQL 的连接字符串。') | ||
} | ||
this.pool = mysql.createPool(config) | ||
} | ||
|
||
public async transactAsync<T>(process: (connection: MySqlConnection) => Promise<T>): Promise<T> { | ||
return await this.executeAsync(async (connection) => { | ||
return new Promise(async (resolve, reject) => { | ||
if (connection) { | ||
await connection.beginTransaction() | ||
try { | ||
let result = await process(connection) | ||
await connection.commit() | ||
resolve(result) | ||
} catch (error) { | ||
await connection.rollback() | ||
if (error instanceof MySqlException) { | ||
reject(error) | ||
} else { | ||
reject(new MySqlException({ sql: '', process: 'transactAsync', inner: error })) | ||
} | ||
} | ||
} else { | ||
reject(new MySqlException({ sql: '', process: 'getConnection' })) | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
public async executeAsync<T>(process: (connection: MySqlConnection) => Promise<T>): Promise<T> { | ||
return new Promise<T>((resolve, reject) => { | ||
this.pool.getConnection(async (error, connection) => { | ||
if (error) { | ||
reject(new MySqlException({ sql: '', process: 'executeAsync', inner: error })) | ||
} else { | ||
if (connection) { | ||
try { | ||
resolve(await process(new MySqlConnection(connection))) | ||
} catch (e) { | ||
if (e instanceof MySqlException) { | ||
reject(e) | ||
} else { | ||
reject(new MySqlException({ sql: '', process: 'executeAsync', inner: e })) | ||
} | ||
} finally { | ||
connection.release() | ||
} | ||
} else { | ||
reject(new MySqlException({ sql: '', process: 'getConnection' })) | ||
} | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
public async allAsync<T>(sql: string, parameters?: any[]): Promise<T[]> { | ||
return await this.executeAsync(async (connection) => { | ||
try { | ||
let { results } = await connection.queryAsync(sql, parameters) | ||
if (results && results.length) { | ||
let list = [] | ||
for (let item of results) { | ||
for (let column in item) { | ||
if (column) { | ||
item[column] = this.adapter.transfer(item[column]) | ||
} | ||
} | ||
list.push(item) | ||
} | ||
return list | ||
} | ||
return null | ||
} catch (error) { | ||
throw new MySqlException({ sql, parameters, process: 'allAsync', inner: error }) | ||
} | ||
}) | ||
} | ||
|
||
public async queryAsync<T>(sql: string, parameters?: any[]): Promise<{ list: T[]; count: number }> { | ||
return await this.executeAsync(async (connection) => { | ||
try { | ||
let { results } = await connection.queryAsync(sql, parameters) | ||
if (results && results.length === 2) { | ||
let count = results[0][0]['__COUNT'] | ||
let list = [] | ||
for (let item of results[1]) { | ||
for (let column in item) { | ||
if (column) { | ||
item[column] = this.adapter.transfer(item[column]) | ||
} | ||
} | ||
list.push(item) | ||
} | ||
return { list, count } | ||
} | ||
return null | ||
} catch (error) { | ||
throw new MySqlException({ sql, parameters, process: 'queryAsync', inner: error }) | ||
} | ||
}) | ||
} | ||
|
||
public async getCountAsync(sql: string, parameters?: any[]): Promise<number> { | ||
return await this.executeAsync(async (connection) => { | ||
try { | ||
let { results, fields } = await connection.queryAsync(sql, parameters) | ||
if (results && fields && results.length && fields.length) { | ||
if (fields.length === 1 && results.length === 1) { | ||
let row = results[0] | ||
let name = fields[0].name | ||
return row[name] | ||
} | ||
} | ||
throw new MySqlException({ message: '获取 Count 失败, 检索结果的字段及行数不正确。', sql, parameters, process: 'getCountAsync' }) | ||
} catch (error) { | ||
if (error instanceof MySqlException) { | ||
throw error | ||
} | ||
throw new MySqlException({ sql, process: 'getCountAsync', parameters, inner: error }) | ||
} | ||
}) | ||
} | ||
|
||
public async queryFirstAsync<T>(sql: string, parameters?: any[]): Promise<T> { | ||
let results = await this.allAsync<T>(sql, parameters) | ||
if (results && results.length) { | ||
return results.shift() | ||
} else { | ||
throw new MySqlException({ message: '获取首行数据失败, 检索结果为空。', sql, parameters, process: 'queryFirstAsync' }) | ||
} | ||
} | ||
|
||
public async queryFirstOrDefaultAsync<T>(sql: string, parameters?: any[], defaultValue?: T): Promise<T> { | ||
let results = await this.allAsync<T>(sql, parameters) | ||
if (results && results.length) { | ||
return results.shift() | ||
} else { | ||
return defaultValue | ||
} | ||
} | ||
|
||
public async insertAsync(sql: string, parameters?: any[]): Promise<number> { | ||
return await this.executeAsync(async (connection) => { | ||
try { | ||
let { results } = await connection.queryAsync(sql, parameters) | ||
return results.insertId | ||
} catch (error) { | ||
throw new MySqlException({ message: '插入数据失败。', sql, parameters, process: 'insertAsync' }) | ||
} | ||
}) | ||
} | ||
|
||
public async executeNonQueryAsync(sql: string, parameters?: any[]): Promise<number> { | ||
return await this.executeAsync(async (connection) => { | ||
try { | ||
let { results } = await connection.queryAsync(sql, parameters) | ||
return results.affectedRows | ||
} catch (error) { | ||
throw new MySqlException({ message: '执行SQL失败。', sql, parameters, process: 'executeNonQueryAsync' }) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import * as mysql from 'mysql' | ||
|
||
export class MySqlConnection { | ||
private connection: mysql.Connection | ||
public constructor(connection: mysql.Connection) { | ||
this.connection = connection | ||
} | ||
|
||
public async queryAsync<T>(sql: string, parameters?: any[]): Promise<{ results?: any; fields?: mysql.FieldInfo[] }> { | ||
return new Promise((resolve, reject) => { | ||
this.connection.query(sql, parameters || [], (error, results, fields) => { | ||
if (error) { | ||
reject(error) | ||
} else { | ||
resolve({ results, fields }) | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
public async beginTransaction(): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.connection.beginTransaction(async (error) => { | ||
if (error) { | ||
reject(error) | ||
} else { | ||
resolve() | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
public async commit(): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.connection.commit(async (error) => { | ||
if (error) { | ||
reject(error) | ||
} else { | ||
resolve() | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
public async rollback(): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.connection.rollback(async (error) => { | ||
if (error) { | ||
reject(error) | ||
} else { | ||
resolve() | ||
} | ||
}) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
export class MySqlException extends Error { | ||
private process: string | ||
private sql: string | ||
private parameters: any | ||
private inner: Error | ||
public constructor(payload: { message?: string; sql: string; parameters?: any; process: string; inner?: Error }) { | ||
payload = Object.assign({}, payload, { message: 'SQL执行出现异常' }) | ||
super(payload.message || 'SQL执行出现异常') | ||
this.sql = payload.sql || '' | ||
this.parameters = payload.parameters || null | ||
this.process = payload.process || 'UNKNOWN' | ||
this.inner = payload.inner || null | ||
} | ||
|
||
public get Process(): string { | ||
return this.process | ||
} | ||
|
||
public get Sql(): string { | ||
return this.sql | ||
} | ||
|
||
public get Parameters(): any { | ||
return this.parameters | ||
} | ||
|
||
public get InnerException(): Error { | ||
return this.inner | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export class MySqlValueAdapter { | ||
public transfer(value: any): any { | ||
if (value) { | ||
if (typeof value.readInt8 === 'function') { | ||
return !!value.readInt8() | ||
} | ||
} | ||
return value | ||
} | ||
} | ||
|
||
export default new MySqlValueAdapter() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* tslint:disable */ | ||
import { expect } from 'chai' | ||
import { MySqlClient } from '../src/mysql-client' | ||
|
||
describe('./src/mysql-client', function () { | ||
it('存在 MySqlClient', function () { | ||
expect(MySqlClient).not.null | ||
expect(MySqlClient).not.undefined | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* tslint:disable */ | ||
import { expect } from 'chai' | ||
import { MySqlConnection } from '../src/mysql-connection' | ||
|
||
describe('./src/mysql-connection', function () { | ||
it('存在 MySqlConnection', function () { | ||
expect(MySqlConnection).not.null | ||
expect(MySqlConnection).not.undefined | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* tslint:disable */ | ||
import { expect } from 'chai' | ||
import { MySqlException } from '../src/mysql-exception' | ||
|
||
describe('./src/mysql-exception', function () { | ||
it('存在 MySqlException', function () { | ||
expect(MySqlException).not.null | ||
expect(MySqlException).not.undefined | ||
}) | ||
|
||
it('new MySqlException(null) 不报错', function () { | ||
expect(() => { | ||
new MySqlException(null) | ||
}).not.throw() | ||
}) | ||
|
||
it('new MySqlException() 不报错', function () { | ||
expect(() => { | ||
new MySqlException(undefined) | ||
}).not.throw() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* tslint:disable */ | ||
import { expect } from 'chai' | ||
import adapter, { MySqlValueAdapter } from '../src/mysql-value-adapter' | ||
|
||
describe('./src/mysql-value-adapter', function () { | ||
it('存在默认导出', function () { | ||
expect(adapter).not.null | ||
expect(adapter).not.undefined | ||
expect(adapter).instanceof(MySqlValueAdapter) | ||
}) | ||
|
||
it('存在 MySqlValueAdapter', function () { | ||
expect(MySqlValueAdapter).not.null | ||
expect(MySqlValueAdapter).not.undefined | ||
}) | ||
}) |