安装 Knex.js
Knex 是一个可以运行于NodeJS环境以及浏览器环境下的 SQL 查询构建工具(但受 WebSQL 的 限制,如删除表、读取表结构等操作是无法捃的),但强烈建议不要随意在浏览器中执行SQL查询,因为这会导致严重的安全漏洞,还是建议您在服务器端执行,在浏览器端执行查询,主要还是用于学习。
Node.js
Node.js 是 Knex 的首先运行环境,你需要在你的 Node.js 项目中安装 knex
库,同时还需要安装适当的数据库操作库:
- PostgreSQL,CockroachDB 以及 Amazon Redshift 数据库使用
pg
库 - PostgreSQl 的原生 C++ 库
libpq
则使用pg-native
库绑定 - MySQL 以及 MariaDB 使用
mysql
库 - SQLite3 使用
sqlite3
或者better-sqlite3
库 - MSSQL 使用
tedious
库
$ npm install knex --save
# Then add one of the following (adding a --save) flag:
$ npm install pg
$ npm install pg-native
$ npm install sqlite3
$ npm install better-sqlite3
$ npm install mysql
$ npm install mysql2
$ npm install oracledb
$ npm install tedious
浏览器
您可以使用 browserify
或者 webpack
这样的 JavaScript 构建工具,将 Knex
构建至您的浏览器项目中。
配置选项
knex
模块本身是一个用于构建 Knex
实例的函数,您需要将配置信息通过一个对象传递给该函数,配置信息是一个对象,该对象的 client
属性是必须的,它指明了新创建的 Knex
实例将使用哪一个数据库。
const knex = require('knex')({
client: 'mysql',
connection: {
host: '127.0.0.1',
port: 3306,
user: 'your_database_user',
password: 'your_database_password',
database: 'myapp_test',
},
});
connection
选项将直接被传递给数据库驱动,它可以是一个对象、一个连接字符串或者一个返回一个对象的函数。
PostgreSQL
对于PostgreSQL
数据库, Knex 允许你通过添加一个名为searchPath
参数给每一个数据库连接。
const pg = require('knex')({
client: 'pg',
connection: process.env.PG_CONNECTION_STRING,
searchPath: ['knex', 'public'],
});
当使用 PostgreSQL 驱动时,实例化 Knex 的配置对象 connection: {}
可以通过各种标签指定各种详细的参数,比如是否启用 SSL、一个连接字符串,如下例:
PostgreSQL
如果存在 connectionString
属性,则会优先使用该属性,否则,连接将使用多个独立的字段组合而成的配置(host
、port
等),最后,还可以使用 ssl
属性来指定是否启用 SSL
连接。
const pg = require('knex')({
client: 'pg',
connection: {
connectionString: config.DATABASE_URL,
host: config['DB_HOST'],
port: config['DB_PORT'],
user: config['DB_USER'],
database: config['DB_NAME'],
password: config['DB_PASSWORD'],
ssl: config['DB_SSL'] ? { rejectUnauthorized: false } : false,
},
});
下面则演示了如何使用 SQLite3 数据库
SQLite3 或 Better-SQLite3
当你使用 SQLite3
或者 Better-SQLite3
适配器的时候,filename
属性是必须项:
const knex = require('knex')({
client: 'sqlite3', // 或者 'better-sqlite3'
connection: {
filename: './mydb.sqlite',
}
});
你也可以通过指定 filename
为 :memory:
,来启用一个连接一个临时保存于内存中的数据库连接实例:
const knex = require('knex')({
client: 'sqlite3', // 或者 'better-sqlite3'
connection: {
filename: ':memory:',
}
});
SQLite3
当你使用 SQLite3 适配器时,你可以通过添加 flags
属性设置连接的标签:
const knex = require('knex')({
client: 'sqlite3',
connection: {
filename: 'file:memDb1?mode=memory&cache=shared',
flags: ['OPEN_URI', 'OPEN_SHAREDCACHE']
}
});
Better-SQLite3
当你使用 Better-SQLite3 适配器时,可以通过 options.nativeBindding
属性来绑定原生 C++ 适配器的地址:
const knex = require('knex')({
client: 'better-sqlite3',
connection: {
filename: ':memory:',
options: {
nativeBinding: '/path/to/better_sqlite3.node',
},
},
});
通过将 options.readonly
设置为 true
来开启一个只读连接:
const knex = require('knex')({
client: 'better-sqlite3',
connection: {
filename: '/path/to/db.sqlite3',
options: {
readonly: true,
},
},
});
MSSQL
使用 MSSQL 数据库时,您可以通过指定 mapBindding
函数来定义自己的逻辑来定义 knex
查询参数到 tedious
类型的绑定:
import { TYPES } from 'tedious';
const knex = require('knex')({
client: 'mssql',
connection: {
options: {
mapBinding: (value) => {
// bind all strings to varchar instead of nvarchar
if (typeof value === 'string') {
return {
type: TYPES.VarChar,
value,
};
}
// allow devs to pass tedious type at query time
if (value != null && value.type) {
return {
type: value.type,
value: value.value,
};
}
// undefined is returned; falling back to default mapping function
},
},
},
});
有用的信息
当您使用 PostgreSQL 适配器连接非标准数据库时,可以在 knex 配置中添加数据库版本。
const knex = require('knex')({ client: 'pg', version: '7.2', connection: { host: '127.0.0.1', port: 5432, user: 'your_database_user', password: 'your_database_password', database: 'myapp_test', }, });
const knex = require('knex')({ client: 'mysql', version: '5.7', connection: { host: '127.0.0.1', port: 3306, user: 'your_database_user', password: 'your_database_password', database: 'myapp_test', }, });
我们也可以使用一个函数动态的给 connection
属性赋值,该函数只需要返回一个配置对象,或者一个返回配置对象的 Promise
即可:
const knex = require('knex')({
client: 'sqlite3',
connection: () => ({
filename: process.env.SQLITE_FILENAME,
}),
});
Knex 在默认情况下,会将函数返回的结果缓存起来,后面的所有连接都将使用该缓存,您可以通过指定一个 expirationChecker
函数用于判断缓存是否已过期,Knex 会在每一次创建新连接之前,首先调用 expirationChecker
函数,若其返回 true
,则会清除缓存,并重新获取一次新的数据。
const knex = require('knex')({
client: 'postgres',
connection: async () => {
const { token, tokenExpiration } = await someCallToGetTheToken();
return {
host: 'your_host',
port: 5432,
user: 'your_database_user',
password: token,
database: 'myapp_test',
expirationChecker: () => {
return tokenExpiration <= Date.now();
},
};
},
});
你也可以指定一个 Unix Socket 地址,此时 port
与 host
会被直接忽略。
const knex = require('knex')({
client: 'mysql',
connection: {
socketPath: '/path/to/socket.sock',
user: 'your_database_user',
password: 'your_database_password',
database: 'myapp_test',
},
});
通过在配置信息中指定 userParams
参数,可以向 Knex 新创建的实例中添加随意的参数,这些参数可以通过实例对象 knex.userParams
访问:
const knex = require('knex')({
client: 'mysql',
connection: {
host: '127.0.0.1',
port: 3306,
user: 'your_database_user',
password: 'your_database_password',
database: 'myapp_test',
},
userParams: {
userParam1: '451',
},
});
在您的应用程序的运行过程中,Knex 实例只应该被初始化一次,该实例将创建一个新的数据库连接池,在您的应用中,永远调用新创建的 Knex 实例即可。
withUserParams
通过在 Knex 实例上面调用 withUserParams
方法,可以创建一个该实例的复本,使用同样的数据库连接:
const knex = require('knex')({
// Params
});
const knexWithParams = knex.withUserParams({
customUserParam: 'table1',
});
const customUserParam = knexWithParams.userParams.customUserParam;
debug
在初始化配置对象中,添加 debug: true
属性,则会启用 debugging
功能。
asyncStackTraces
通过添加 asyncStackTraces: true
,可以启用实例对所有查询的栈跟踪功能。
pool
Knex 是基于 tarn.js 创建连接池的,该库有一个默认的配置 min: 2, max: 10
,若您想修改该配置,可以通过在初始化配置对象中添加 pool
属性来改变它:
const knex = require('knex')({
client: 'mysql',
connection: {
host: '127.0.0.1',
port: 3306,
user: 'your_database_user',
password: 'your_database_password',
database: 'myapp_test',
},
pool: { min: 0, max: 7 },
});
afterCreate
当一个新的数据库连接被成功创建后, afterCreate
回调函数会被调用。
const knex = require('knex')({
client: 'pg',
connection: {
/*...*/
},
pool: {
afterCreate: function (conn, done) {
// in this example we use pg driver's connection API
conn.query('SET timezone="UTC";', function (err) {
if (err) {
// first query failed,
// return error and don't try to make next query
done(err, conn);
} else {
// do the second query...
conn.query('SELECT set_limit(0.01);', function (err) {
// if err is not falsy,
// connection is discarded from pool
// if connection aquire was triggered by a
// query the error is passed to query promise
done(err, conn);
});
}
});
},
},
});
acquireConnectionTimeout
默认情况下,当 Knex 连接数据库超过 60000ms 未成功时,会抛出连接超时异常,可以通过指定 acquireConnectionTimeout
值来修改默认超时时间。
const knex = require('knex')({
client: 'pg',
connection: {
/*...*/
},
pool: {
/*...*/
},
acquireConnectionTimeout: 10000,
});
fetchAsString
在使用 Oracle 数据库时,对于 DATE
、NUMBER
以及CLOB
类型的数据,可以选择以字符串返回数据:
const knex = require('knex')({
client: 'oracledb',
connection: {
/*...*/
},
fetchAsString: ['number', 'clob'],
});
postProcessResponse
查询结果返回给用户前,会调用该钩子函数,这是一个非常有用的钩子,最常见的用处莫过于将查询结果中的属性由 snake_case
转为 camelCase
。
const knex = require('knex')({
client: 'mysql',
// overly simplified snake_case -> camelCase converter
postProcessResponse: (result, queryContext) => {
// TODO: add special case for raw results
// (depends on dialect)
if (Array.isArray(result)) {
return result.map((row) => convertToCamel(row));
} else {
return convertToCamel(result);
}
},
});
wrapIdentifier
Knex 支持将标识符名称自动转换为每种SQL方言的带引号的版本,比如在 PostgreSQL 中, 'Table.columnName as foo'
会被自动转换为 "Table"."columnName" as "foo"
。
通过 wrapIdentifier
可以重写这种行为,比如,您可以通过该钩子函数来统一将 camelCase
修改为 snake_case
格式。
wrapIdentifier(value, dialectImpl, context): string
函数会将所有标识符遍历为单个值 value
。
const knex = require('knex')({
client: 'mysql',
// overly simplified camelCase -> snake_case converter
wrapIdentifier: (value, origImpl, queryContext) =>
origImpl(convertToSnakeCase(value)),
});
log
打开日志。
const knex = require('knex')({
log: {
warn(message) {},
error(message) {},
deprecate(message) {},
debug(message) {},
},
});
Typescript
import { Knex } from 'knex';
declare module 'knex/types/tables' {
interface User {
id: number;
name: string;
created_at: string;
updated_at: string;
}
interface Tables {
// This is same as specifying `knex<User>('users')`
users: User;
// For more advanced types, you can specify separate type
// for base model, "insert" type and "update" type.
// But first: notice that if you choose to use this,
// the basic typing showed above can be ignored.
// So, this is like specifying
// knex
// .insert<{ name: string }>({ name: 'name' })
// .into<{ name: string, id: number }>('users')
users_composite: Knex.CompositeTableType<
// This interface will be used for return type and
// `where`, `having` etc where full type is required
User,
// Specifying "insert" type will also make sure
// data matches interface in full. Meaning
// if interface is `{ a: string, b: string }`,
// `insert({ a: '' })` will complain about missing fields.
//
// For example, this will require only "name" field when inserting
// and make created_at and updated_at optional.
// And "id" can't be provided at all.
// Defaults to "base" type.
Pick<User, 'name'> & Partial<Pick<User, 'created_at' | 'updated_at'>>,
// This interface is used for "update()" calls.
// As opposed to regular specifying interface only once,
// when specifying separate update interface, user will be
// required to match it exactly. So it's recommended to
// provide partial interfaces for "update". Unless you want to always
// require some field (e.g., `Partial<User> & { updated_at: string }`
// will allow updating any field for User but require updated_at to be
// always provided as well.
//
// For example, this wil allow updating all fields except "id".
// "id" will still be usable for `where` clauses so
// knex('users_composite')
// .update({ name: 'name2' })
// .where('id', 10)`
// will still work.
// Defaults to Partial "insert" type
Partial<Omit<User, 'id'>>
>;
}
}
// The trailing `.js` is required by the TypeScript compiler in certain configs:
declare module 'knex/types/tables.js' {
// <----- Different module path!!!
interface Tables {
// ...
}
}
评论已关闭