Skip to content

Commit

Permalink
快速迭代第一版(未深入测试)
Browse files Browse the repository at this point in the history
  • Loading branch information
LuckyStarry committed Sep 17, 2020
1 parent 548db3a commit e307ad3
Show file tree
Hide file tree
Showing 11 changed files with 342 additions and 3 deletions.
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"devDependencies": {
"@types/chai": "^4.2.12",
"@types/mocha": "^8.0.3",
"@types/mysql": "^2.15.15",
"@types/node": "^14.10.2",
"chai": "^4.2.0",
"coveralls": "^3.1.0",
Expand Down
177 changes: 175 additions & 2 deletions src/mysql-client.ts
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' })
}
})
}
}
56 changes: 56 additions & 0 deletions src/mysql-connection.ts
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()
}
})
})
}
}
30 changes: 30 additions & 0 deletions src/mysql-exception.ts
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
}
}
12 changes: 12 additions & 0 deletions src/mysql-value-adapter.ts
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()
2 changes: 1 addition & 1 deletion test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { expect } from 'chai'
import MySqlClient from '../src/index'

describe('Index', function () {
describe('./src/index', function () {
it('存在 默认导出', function () {
expect(MySqlClient).not.null
expect(MySqlClient).not.undefined
Expand Down
10 changes: 10 additions & 0 deletions test/mysql-client.spec.ts
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
})
})
10 changes: 10 additions & 0 deletions test/mysql-connection.spec.ts
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
})
})
22 changes: 22 additions & 0 deletions test/mysql-exception.spec.ts
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()
})
})
16 changes: 16 additions & 0 deletions test/mysql-value-adapter.spec.ts
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
})
})

0 comments on commit e307ad3

Please sign in to comment.