在项目开发中,经常需要操作数据库(如:增删改查等功能),手工拼写 SQL 语句非常麻烦,同时还要注意 SQL 注入等安全问题。为此框架提供了模型功能,方便操作数据库。
框架默认没有提供模型的功能,需要加载对应的扩展才能支持,对应的模块为 think-model。修改扩展的配置文件 src/config/extend.js
(多模块项目为 src/common/config/extend.js
),添加如下的配置:
const model = require('think-model');
module.exports = [
model(think.app) // 让框架支持模型的功能
]
添加模型的扩展后,会添加方法 think.Model、think.model、ctx.model、controller.model、service.model。
模型由于要支持多种数据库,所以配置文件的格式为 Adapter 的方式,文件路径为 src/config/adapter.js
(多模块项目下为 src/common/config/adapter.js
)。
const mysql = require('think-model-mysql');
exports.model = {
type: 'mysql', // 默认使用的类型,调用时可以指定参数切换
common: { // 通用配置
logConnect: true, // 是否打印数据库连接信息
logSql: true, // 是否打印 SQL 语句
logger: msg => think.logger.info(msg) // 打印信息的 logger
},
mysql: { // mysql 配置
handle: mysql
},
mysql2: { // 另一个 mysql 的配置
handle: mysql
},
sqlite: { // sqlite 配置
},
postgresql: { // postgresql 配置
}
}
如果项目里要用到同一个类型的多个数据库配置,那么可以通过不同的 type 区分,如:mysql
,mysql2
,调用时可以指定参数切换。
const user1 = think.model('user'); // 使用默认的数据库配置,默认的 type 为 mysql,那么就是使用 mysql 的配置
const user2 = think.model('user', 'mysql2'); // 使用 mysql2 的配置
const user3 = think.model('user', 'sqlite'); // 使用 sqlite 的配置
const user4 = think.model('user', 'postgresql'); // 使用 postgresql 的配置
由于可以调用时指定使用哪个 type
,理论上可以支持无限多的类型配置,项目中可以根据需要进行配置。
Mysql 的 Adapter 为 think-model-mysql,底层基于 mysql 库实现,使用连接池的方式连接数据库,默认连接数为 1。
const mysql = require('think-model-mysql');
exports.model = {
type: 'mysql',
mysql: {
handle: mysql, // Adapter handle
user: 'root', // 用户名
password: '', // 密码
database: '', // 数据库
host: '127.0.0.1', // host
port: 3306, // 端口
connectionLimit: 1, // 连接池的连接个数,默认为 1
prefix: '', // 数据表前缀,如果一个数据库里有多个项目,那项目之间的数据表可以通过前缀来区分
acquireWaitTimeout: 0, // 等待连接的超时时间,避免获取不到连接一直卡在那里,开发环境下有用
reuseDB: false, // 是否复用数据库连接,事务的时候可能会用到
jsonFormat: false, // 是否针对 json 类型字段做数据的处理,包括存储的时候对数据进行 JSON.stringify 操作,读取的时候对数据进行 JSON.parse 操作
}
}
除了用 host 和 port 连接数据库外,也可以通过 socketPath
来连接,更多配置选项请见 https://github.com/mysqljs/mysql#connection-options
SQLite 的 Adapter 为 think-model-sqlite,底层基于 sqlite3 库实现,使用连接池的方式连接数据库,默认连接数为 1。
const sqlite = require('think-model-sqlite');
exports.model = {
type: 'sqlite',
sqlite: {
handle: sqlite, // Adapter handle
path: path.join(think.ROOT_PATH, 'runtime/sqlite'), // sqlite 保存的目录
database: '', // 数据库名
connectionLimit: 1, // 连接池的连接个数,默认为 1
prefix: '', // 数据表前缀,如果一个数据库里有多个项目,那项目之间的数据表可以通过前缀来区分
}
}
PostgreSQL 的 Adapter 为 think-model-postgresql,底层基于 pg 库实现,使用连接池的方式连接数据库,默认连接数为 1。
const postgresql = require('think-model-postgresql');
exports.model = {
type: 'postgresql',
postgresql: {
handle: postgresql, // Adapter handle
user: 'root', // 用户名
password: '', // 密码
database: '', // 数据库
host: '127.0.0.1', // host
port: 3211, // 端口
connectionLimit: 1, // 连接池的连接个数,默认为 1
prefix: '', // 数据表前缀,如果一个数据库里有多个项目,那项目之间的数据表可以通过前缀来区分
}
}
除了用 host 和 port 连接数据库外,也可以通过 connectionString
来连接,更多配置选项请见 https://node-postgres.com/features/connecting
模型文件放在 src/model/
目录下(多模块项目为 src/common/model
以及 src/[module]/model
),继承模型基类 think.Model
,文件格式为:
// src/model/user.js
module.exports = class extends think.Model {
getList() {
return this.field('name').select();
}
}
也可以在项目根目录下通过 thinkjs model modelName
快速创建模型文件。
如果项目比较复杂,希望对模型文件分目录管理,那么可以在模型目录下建立子目录,如: src/model/front/user.js
,src/model/admin/user.js
,这样在模型目录下建立 front
和 admin
目录,分别管理前台和后台的模型文件。
含有子目录的模型实例化需要带上子目录,如:think.model('front/user')
,具体见这里。
项目启动时,会扫描项目下的所有模型文件(目录为 src/model/
,多模块项目下目录为 src/common/model
以及各种 src/[module]/model
),扫描后会将所有的模型类存放在 think.app.models
对象上,实例化时会从这个对象上查找,如果找不到则实例化模型基类 think.Model
。
实例化模型类。
think.model('user'); // 获取模型的实例
think.model('user', 'sqlite'); // 获取模型的实例,修改数据库的类型
think.model('user', { // 获取模型的实例,修改类型并添加其他的参数
type: 'sqlite',
aaa: 'bbb'
});
think.model('user', {}, 'admin'); // 获取模型的实例,指定为 admin 模块(多模块项目下有效)
实例化模型类,获取配置后调用 think.model
方法,多模块项目下会获取当前模块下的配置。
const user = ctx.model('user');
实例化模型类,获取配置后调用 think.model
方法,多模块项目下会获取当前模块下的配置。
module.exports = class extends think.Controller {
async indexAction() {
const user = this.model('user'); // controller 里实例化模型
const data = await user.select();
return this.success(data);
}
}
实例化模型类,等同于 think.model
如果模型目录下含有子目录,那么实例化时需要带上对应的子目录,如:
const user1 = think.model('front/user'); // 实例化前台的 user 模型
const user2 = think.model('admin/user'); // 实例化后台的 user 模型
think.Model
基类提供了丰富的方法进行 CRUD 操作,下面来一一介绍。
模型提供了多种方法来查询数据,如:
同时模型支持通过下面的方法指定 SQL 语句中的特定条件,如:
模型提供了下列的方法来添加数据:
模型提供了下列的方法来更新数据:
模型提供了下列的方法来删除数据:
有时候模型包装的方法不能满足所有的情况,这时候需要手工指定 SQL 语句,可以通过下面的方法进行:
对于数据安全要求很高的业务(如:订单系统、银行系统)操作时需要使用事务,这样可以保证数据的原子性、一致性、隔离性和持久性,模型提供了操作事务的方法。
可以手工通过 model.startTrans、model.commit 和 model.rollback 方法操作事务。
每次操作事务时都手工执行 startTrans、commit 和 rollback 比较麻烦,模型提供了 model.transaction 方法快速操作事务。
可以通过 pk
属性设置数据表的主键,具体见 model.pk。
可以通过 schema
属性设置数据表结构,具体见 model.schema。
数据库中表经常会跟其他数据表有关联,数据操作时需要连同关联的表一起操作。如:一个博客文章会有分类、标签、评论,以及属于哪个用户,支持的类型有:一对一、一对一(属于)、一对多和多对多。
可以通过 model.relation 属性配置详细的关联关系。
一对一关联,表示当前表含有一个附属表。假设当前表的模型名为 user
,关联表的模型名为 info
,那么配置中字段 key
的默认值为 id
,字段 fKey
的默认值为 user_id
。
module.exports = class extends think.Model {
get relation() {
return {
info: think.Model.HAS_ONE
};
}
}
执行查询操作时,可以得到类似如下的数据:
[
{
id: 1,
name: '111',
info: { //关联表里的数据信息
user_id: 1,
desc: 'info'
}
}, ...]
一对一关联,属于某个关联表,和 HAS_ONE 是相反的关系。假设当前模型名为 info
,关联表的模型名为 user
,那么配置字段 key
的默认值为 user_id
,配置字段 fKey
的默认值为 id
。
module.exports = class extends think.Model {
get relation() {
return {
user: think.Model.BELONG_TO
}
}
}
执行查询操作时,可以得到类似下面的数据:
[
{
id: 1,
user_id: 1,
desc: 'info',
user: {
name: 'thinkjs'
}
}, ...
]
一对多的关系。假如当前模型名为 post
,关联表的模型名为 comment
,那么配置字段 key
默认值为 id
,配置字段 fKey
默认值为 post_id
。
module.exports = class extends think.Model {
get relation() {
return {
comment: {
type: think.Model.HAS_MANY
}
}
}
}
执行查询数据时,可以得到类似下面的数据:
[{
id: 1,
title: 'first post',
content: 'content',
comment: [{
id: 1,
post_id: 1,
name: 'welefen',
content: 'first comment'
}, ...]
}, ...]
如果关联表的数据需要分页查询,可以通过 model.setRelation 方法进行。
多对多关系。假设当前模型名为 post
,关联模型名为 cate
,那么需要一个对应的关联关系表。配置字段 rModel
默认值为 post_cate
,配置字段 rfKey
默认值为 cate_id
。
module.exports = class extends think.Model {
get relation() {
return {
cate: {
type: think.Model.MANY_TO_MANY,
rModel: 'post_cate',
rfKey: 'cate_id'
}
}
}
}
查询出来的数据结构为:
[{
id: 1,
title: 'first post',
cate: [{
id: 1,
name: 'cate1',
post_id: 1
}, ...]
}, ...]
有时候数据库需要用到分布式数据库,或者进行读写分离,这时候可以给配置里添加 parser
完成,如:
exports.model = {
type: 'mysql',
mysql: {
user: 'root',
password: '',
parser: sql => {
// 这里会把当前要执行的 SQL 传递进来
const sqlLower = sql.toLowerCase();
if (sqlLower.startWith('select ')) {
return {
host: '',
port: ''
}
} else {
return {
host: '',
port: ''
}
}
}
}
}
parser
里可以根据 sql 返回不同的配置,会将返回的配置和默认的配置进行合并。
假设项目有二个集群,每个集群有十台机器,每台机器开启了四个 worker,数据库配置的连接池里的连接数为五,那么总体的最大连接数为:2 * 10 * 4 * 5 = 400
模型使用的 debug 名称为 think-model
,可以通过 DEBUG=think-model npm start
启动服务然后查看调试信息。
设置表结构,默认从数据表中获取,也可以自己配置增加额外的配置项。
module.exports = class extends think.Model {
get schema() {
return {
id: { // 字段名称
type: 'int(11)',
...
}
}
}
}
支持的字段为:
type
{String} 字段的类型,包含长度属性required
{Boolean} 是否必填default
{mixed} 默认值,可以是个值,也可以是函数module.exports = class extends think.Model {
get schema() {
return {
type: { // 字段名称
type: 'varchar(10)',
default: 'small'
},
create_time: {
type: 'datetime',
default: () => think.datetime() // default 为一个函数
},
score: {
type: 'int',
default: data => { // data 为添加/更新时的数据
return data.grade * 1.5;
}
}
}
}
}
primary
{boolean} 是否为主键unique
{boolean} 字段是否唯一autoIncrement
{boolean} 自动是否 auto increment
readonly
{boolean} 字段是否只读,也就是只能创建时添加,不让更新该字段update
{boolean} 默认值是否在更新时也有效。如果设置了 readonly
,那么该字段无效。配置数据表的关联关系。
module.exports = class extends think.Model {
// 配置关联关系
get relation() {
return {
cate: { // 配置跟分类的关联关系
type: think.Model.MANY_TO_MANY,
...
},
comment: { // 配置跟评论的关联关系
}
}
}
}
每个关联关系支持的配置如下:
type
关联关系类型,默认为 think.Model.HAS_ONE
一对一:think.Model.HAS_ONE
一对一(属于):think.Model.BELONG_TO
一对多:think.Model.HAS_MANY
多对多:think.Model.MANY_TO_MANY
model
关联表的模型名,默认为配置的 key
实例化对应关联模型的时候使用,会通过 const relationModel = this.model(item.model) 去实例化关联模型
name
对应的数据字段名,默认为配置的 key,查询到数据后,保存字段的名称。
// 原始数据
const originData = {
id: 1,
email: ''
}
// 设置对应的数据字段名为 cate
// 那么最终生成的数据为
const targetData = {
id: 1,
email: '',
cate: {
}
}
key
当前模型的关联 key
一对一、一对多、多对多下默认值为当前模型的主键,如:id
一对一(属于)下默认值为关联表名称和 id 的组合,如:cate_id
fKey
关联表与之对应的 key
一对一、一对多、多对多下默认值为关联表名称和 id 的组合,如:cate_id
一对一(属于)下默认值为当前模型的主键,如:id
field
关联表查询时设置的 field,默认值为 *
。如果需要设置,必须包含 fKey
对应的值,支持函数。
// 设置 field 字段
get relation() {
return {
cate: {
field: 'id,name' // 只查询 id, name 字段
}
}
}
// 设置 field 为 function
get relation() {
return {
cate: {
// rModel 为关联模型的实例,model 为当前模型的实例
field: (rModel, model) => {
return 'id,name'
}
}
}
}
where
关联表查询时设置的 where 条件,支持函数
order
关联表查询时设置的 order,支持函数
limit
关联表查询时设置的 limit,支持函数
page
关联表查询时设置的 page,支持函数
rModel
多对多关系下,对应的关联关系模型名,默认值为二个模型名的组合,如:article_cate
多对多关联模型下,一般需要一个中间的关联表维护关联关系,如:article(文章)和 cate(分类)是多对多的关联关系,那么就需要一个文章-分类的中间关系表(article_cate),rModel 为配置的中间关联表的模型名称
rfKey
多对多关系下,关系表对应的 key
relation
是否关闭关联表的关联关系
// 如果关联表还配置了关联关系,那么查询时还会一并查询
// 有时候不希望查询关联表的关联数据,那么就可以通过 relation 属性关闭
get relation() {
return {
cate: {
relation: false // 关闭关联表的所有关联关系,可以避免关联死循环等各种问题
}
}
}
设置关联关系后,查询等操作都会自动查询关联表的数据。如果某些情况下不需要查询关联表的数据,可以通过 setRelation
方法临时关闭关联关系查询。
通过 setRelation(false)
关闭所有的关联关系查询。
module.exports = class extends think.Model {
getList(){
return this.setRelation(false).select();
}
}
通过 setRelation('comment')
只查询 comment
的关联数据,不查询其他的关联关系数据。
module.exports = class extends think.Model {
getList2(){
return this.setRelation('comment').select();
}
}
通过 setRelation('comment', false)
关闭 comment
的关联关系数据查询。
module.exports = class extends think.Model {
getList2(){
return this.setRelation('comment', false).select();
}
}
通过 setRelation(true)
重新启用所有的关联关系数据查询。
module.exports = class extends think.Model {
getList2(){
return this.setRelation(true).select();
}
}
虽然通过 relation 属性配置了关联关系,但有时候调用的时候希望动态修改某些值,如:设置分页,这时候也可以通过 setRelation 方法来完成。
module.exports = class extends think.Model {
getList2(page){
// 动态设置 comment 的分页
return this.setRelation('comment', {page}).select();
}
}
获取或者设置 db 的实例,db 为 Adapter handle(如:think-model-mysql) 的实例。事务操作时由于要复用一个连接需要使用该方法。
module.exports = class extends think.Model {
async getList() {
// 让 user 复用当前的 Apdater handle 实例,这样后续可以复用同一个数据库连接
const user = this.model('user').db(this.db());
}
}
实例化模型时传入的模型名
const user = think.model('user');
实例化时传入的模型名为 user
,那么 model.modelName
值为 user
。
实例化模型时传入的配置,模型实例化时会自动传递,不用手工赋值。
{
host: '127.0.0.1',
port: 3306,
...
}
获取数据表前缀,从配置里的 prefix
字段获取。如果要修改的话,可以通过下面的方式:
module.exports = class extends think.Model {
get tablePrefix() {
return 'think_';
}
}
获取数据表名,值为 tablePrefix + modelName
。如果要修改的话,可以通过下面的方式:
module.exports = class extends think.Model {
get tableName() {
return 'think_user';
}
}
获取数据表的主键,默认值为 id
。如果数据表的主键不是 id
,需要自己配置,如:
module.exports = class extends think.Model {
get pk() {
return 'user_id';
}
}
有时候不想写模型文件,而是在控制器里直接实例化,这时候又想改变主键的名称,那么可以通过设置 _pk
属性的方式,如:
module.exports = class extends think.Controller {
async indexAction() {
const user = this.model('user');
user._pk = 'user_id'; // 通过 _pk 属性设置 pk
const data = await user.select();
}
}
模型操作的一些选项,设置 where、limit、group 等操作时最终都会解析到 options 选项上,格式为:
{
where: {}, // 存放 where 条件的配置项
limit: {}, // 存放 limit 的配置项
group: {},
...
}
获取最近一次执行的 SQL 语句,默认值为空。
const user = think.model('user');
console.log(user.lastSql); // 打印最近一条的 sql 语句,如果没有则为空
name
{String} 要实例化的模型名return
{this} 模型实例实例化别的模型,支持子目录的模型实例化。
module.exports = class extends think.Model {
async getList() {
// 如果含有子目录,那么这里带上子目录,如: this.model('front/article')
const article = this.model('article');
const data = await article.select();
...
}
}
offset
{Number} SQL 语句里的 offsetlength
{Number} SQL 语句里的 lengthreturn
{this}设置 SQL 语句里的 limit
,会赋值到 this.options.limit
属性上,便于后续解析。
module.exports = class extends think.Model() {
async getList() {
// SQL: SELECT * FROM `test_d` LIMIT 10
const list1 = await this.limit(10).select();
// SQL: SELECT * FROM `test_d` LIMIT 10,20
const list2 = await this.limit(10, 20).select();
}
}
page
{Number} 设置当前页数pagesize
{Number} 每页条数,默认值为 this.config.pagesize
return
{this}设置查询分页,会解析为 limit 数据。
module.exports = class extends think.Model() {
async getList() {
// SQL: SELECT * FROM `test_d` LIMIT 0,10
const list1 = await this.page(1).select(); // 查询第一页,每页 10 条
// SQL: SELECT * FROM `test_d` LIMIT 20,20
const list2 = await this.page(2, 20).select(); // 查询第二页,每页 20 条
}
}
每页条数可以通过配置项 pageSize
更改,如:
// src/config/adapter.js
exports.model = {
type: 'mysql',
mysql: {
database: '',
...
pageSize: 20, // 设置默认每页为 20 条
}
}
where
{String | Object} 设置查询条件return
{this}设置 where 查询条件,会添加 this.options.where
属性,方便后续解析。可以通过属性 _logic
设置逻辑,默认为 AND
。可以通过属性 _complex
设置复合查询。
注意:where 条件中的值必须要在 Logic 里做数据校验,否则可能会有 SQL 注入漏洞。
module.exports = class extends think.Model {
where1(){
//SELECT * FROM `think_user`
return this.where().select();
}
where2(){
//SELECT * FROM `think_user` WHERE ( `id` = 10 )
return this.where({id: 10}).select();
}
where3(){
//SELECT * FROM `think_user` WHERE ( id = 10 OR id < 2 )
return this.where('id = 10 OR id < 2').select();
}
where4(){
//SELECT * FROM `think_user` WHERE ( `id` != 10 )
return this.where({id: ['!=', 10]}).select();
}
}
module.exports = class extends think.Model {
where1(){
//SELECT * FROM `think_user` where ( title IS NULL );
return this.where({title: null}).select();
}
where2(){
//SELECT * FROM `think_user` where ( title IS NOT NULL );
return this.where({title: ['!=', null]}).select();
}
}
ThinkJS 默认会对字段和值进行转义,防止安全漏洞。有时候一些特殊的情况不希望被转义,可以使用 EXP 的方式,如:
module.exports = class extends think.Model {
where1(){
//SELECT * FROM `think_user` WHERE ( (`name` ='name') )
return this.where({name: ['EXP', "=\"name\""]}).select();
}
}
module.exports = class extends think.Model {
where1(){
//SELECT * FROM `think_user` WHERE ( `title` NOT LIKE 'welefen' )
return this.where({title: ['NOTLIKE', 'welefen']}).select();
}
where2(){
//SELECT * FROM `think_user` WHERE ( `title` LIKE '%welefen%' )
return this.where({title: ['like', '%welefen%']}).select();
}
//like 多个值
where3(){
//SELECT * FROM `think_user` WHERE ( (`title` LIKE 'welefen' OR `title` LIKE 'suredy') )
return this.where({title: ['like', ['welefen', 'suredy']]}).select();
}
//多个字段或的关系 like 一个值
where4(){
//SELECT * FROM `think_user` WHERE ( (`title` LIKE '%welefen%') OR (`content` LIKE '%welefen%') )
return this.where({'title|content': ['like', '%welefen%']}).select();
}
//多个字段与的关系 Like 一个值
where5(){
//SELECT * FROM `think_user` WHERE ( (`title` LIKE '%welefen%') AND (`content` LIKE '%welefen%') )
return this.where({'title&content': ['like', '%welefen%']}).select();
}
}
module.exports = class extens think.Model {
where1(){
//SELECT * FROM `think_user` WHERE ( `id` IN ('10','20') )
return this.where({id: ['IN', '10,20']}).select();
}
where2(){
//SELECT * FROM `think_user` WHERE ( `id` IN (10,20) )
return this.where({id: ['IN', [10, 20]]}).select();
}
where3(){
//SELECT * FROM `think_user` WHERE ( `id` NOT IN (10,20) )
return this.where({id: ['NOTIN', [10, 20]]}).select();
}
}
module.exports = class extens think.Model {
where1(){
//SELECT * FROM `think_user` WHERE ( (`id` BETWEEN 1 AND 2) )
return this.where({id: ['BETWEEN', 1, 2]}).select();
}
where2(){
//SELECT * FROM `think_user` WHERE ( (`id` BETWEEN '1' AND '2') )
return this.where({id: ['between', '1,2']}).select();
}
}
module.exports = class extends think.Model {
where1(){
//SELECT * FROM `think_user` WHERE ( `id` = 10 ) AND ( `title` = 'www' )
return this.where({id: 10, title: "www"}).select();
}
//修改逻辑为 OR
where2(){
//SELECT * FROM `think_user` WHERE ( `id` = 10 ) OR ( `title` = 'www' )
return this.where({id: 10, title: "www", _logic: 'OR'}).select();
}
//修改逻辑为 XOR
where2(){
//SELECT * FROM `think_user` WHERE ( `id` = 10 ) XOR ( `title` = 'www' )
return this.where({id: 10, title: "www", _logic: 'XOR'}).select();
}
}
module.exports = class extends think.Model {
where1(){
//SELECT * FROM `think_user` WHERE ( `id` > 10 AND `id` < 20 )
return this.where({id: {'>': 10, '<': 20}}).select();
}
//修改逻辑为 OR
where2(){
//SELECT * FROM `think_user` WHERE ( `id` < 10 OR `id` > 20 )
return this.where({id: {'<': 10, '>': 20, _logic: 'OR'}}).select()
}
}
module.exports = class extends think.Model {
where1(){
//SELECT * FROM `think_user` WHERE ( `title` = 'test' ) AND ( ( `id` IN (1,2,3) ) OR ( `content` = 'www' ) )
return this.where({
title: 'test',
_complex: {id: ['IN', [1, 2, 3]],
content: 'www',
_logic: 'or'
}
}).select()
}
}
field
{String} 查询字段,支持 AS
。return
{this}设置 SQL 语句中的查询字段,默认为 *
。设置后会赋值到 this.options.field
属性上,便于后续解析。
module.exports = class extends think.Model{
async getList() {
// SQL: SELECT `d_name` FROM `test_d`
const data1 = await this.field('d_name').select();
// SQL: SELECT `c_id`,`d_name` FROM `test_d`
const data2 = await this.field('c_id,d_name').select();
// SQL: SELECT c_id AS cid,`d_name` FROM `test_d`
const data3 = await this.field('c_id AS cid, d_name').select();
}
}
field
{String} 查询字段,不支持 AS
。return
{this}查询时设置反选字段(即:不查询配置的字段,而是查询其他的字段),会添加 this.options.field
和 this.options.fieldReverse
属性,便于后续分析。
该功能的实现方式为:查询数据表里的所有字段,然后过滤掉配置的字段。
module.exports = class extends think.Model{
async getList() {
// SQL: SELECT `id`, `c_id` FROM `test_d`
const data1 = await this.fieldReverse('d_name').select();
}
}
table
{String} 表名,支持值为一个 SELECT 语句hasPrefix
{Boolean} table
里是否已经含有了表前缀,默认值为 false
return
{this}设置当前模型对应的表名,如果 hasPrefix 为 false 且 table 不是 SQL 语句,那么表名会追加 tablePrefix
,最后的值会设置到 this.options.table
属性上。
如果没有设置该属性,那么最后解析 SQL 时通过 mode.tableName
属性获取表名。
union
{String} union 查询字段all
{boolean} 是否使用 UNION ALLreturn
{this}设置 SQL 中的 UNION 查询,会添加 this.options.union
属性,便于后续分析。
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` UNION (SELECT * FROM think_pic2)
return this.union('SELECT * FROM think_pic2').select();
}
getList2(){
//SELECT * FROM `think_user` UNION ALL (SELECT * FROM `think_pic2`)
return this.union({table: 'think_pic2'}, true).select();
}
}
join
{String | Object | Array} 要组合的查询语句,默认为 LEFT JOIN
return
{this}组合查询,支持字符串、数组和对象等多种方式。会添加 this.options.join
属性,便于后续分析。
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` LEFT JOIN think_cate ON think_group.cate_id=think_cate.id
return this.join('think_cate ON think_group.cate_id=think_cate.id').select();
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` LEFT JOIN think_cate ON think_group.cate_id=think_cate.id RIGHT JOIN think_tag ON think_group.tag_id=think_tag.id
return this.join([
'think_cate ON think_group.cate_id=think_cate.id',
'RIGHT JOIN think_tag ON think_group.tag_id=think_tag.id'
]).select();
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` INNER JOIN `think_cate` AS c ON think_user.`cate_id`=c.`id`
return this.join({
table: 'cate',
join: 'inner', //join 方式,有 left, right, inner 3 种方式
as: 'c', // 表别名
on: ['cate_id', 'id'] //ON 条件
}).select();
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM think_user AS a LEFT JOIN `think_cate` AS c ON a.`cate_id`=c.`id` LEFT JOIN `think_group_tag` AS d ON a.`id`=d.`group_id`
return this.alias('a').join({
table: 'cate',
join: 'left',
as: 'c',
on: ['cate_id', 'id']
}).join({
table: 'group_tag',
join: 'left',
as: 'd',
on: ['id', 'group_id']
}).select()
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` LEFT JOIN `think_cate` ON think_user.`id`=think_cate.`id` LEFT JOIN `think_group_tag` ON think_user.`id`=think_group_tag.`group_id`
return this.join({
cate: {
on: ['id', 'id']
},
group_tag: {
on: ['id', 'group_id']
}
}).select();
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM think_user AS a LEFT JOIN `think_cate` AS c ON a.`id`=c.`id` LEFT JOIN `think_group_tag` AS d ON a.`id`=d.`group_id`
return this.alias('a').join({
cate: {
join: 'left', // 有 left,right,inner 3 个值
as: 'c',
on: ['id', 'id']
},
group_tag: {
join: 'left',
as: 'd',
on: ['id', 'group_id']
}
}).select()
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` LEFT JOIN `think_cate` ON think_user.`id`=think_cate.`id` LEFT JOIN `think_group_tag` ON think_user.`id`=think_group_tag.`group_id` LEFT JOIN `think_tag` ON (think_user.`id`=think_tag.`id` AND think_user.`title`=think_tag.`name`)
return this.join({
cate: {on: 'id, id'},
group_tag: {on: ['id', 'group_id']},
tag: {
on: { // 多个字段的 ON
id: 'id',
title: 'name'
}
}
}).select()
}
}
module.exports = class extends think.Model {
async getList(){
let sql = await this.model('group').buildSelectSql();
//SELECT * FROM `think_user` LEFT JOIN ( SELECT * FROM `think_group` ) ON think_user.`gid`=( SELECT * FROM `think_group` ).`id`
return this.join({
table: sql,
on: ['gid', 'id']
}).select();
}
}
order
{String | Array | Object} 排序方式return
{this}设置 SQL 中的排序方式。会添加 this.options.order
属性,便于后续分析。
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` ORDER BY id DESC, name ASC
return this.order('id DESC, name ASC').select();
}
getList1(){
//SELECT * FROM `think_user` ORDER BY count(num) DESC
return this.order('count(num) DESC').select();
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` ORDER BY id DESC,name ASC
return this.order(['id DESC', 'name ASC']).select();
}
}
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` ORDER BY `id` DESC,`name` ASC
return this.order({
id: 'DESC',
name: 'ASC'
}).select();
}
}
aliasName
{String} 表别名return
{this}设置表别名。会添加 this.options.alias
属性,便于后续分析。
module.exports = class extends think.Model {
getList(){
//SELECT * FROM think_user AS a;
return this.alias('a').select();
}
}
having
{String} having 查询的字符串return
{this}设置 having 查询。会设置 this.options.having
属性,便于后续分析。
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` HAVING view_nums > 1000 AND view_nums < 2000
return this.having('view_nums > 1000 AND view_nums < 2000').select();
}
}
group
{String} 分组查询的字段return
{this}设定分组查询。会设置 this.options.group
属性,便于后续分析。
module.exports = class extends think.Model {
getList(){
//SELECT * FROM `think_user` GROUP BY `name`
return this.group('name').select();
}
}
distinct
{String} 去重的字段return
{this}去重查询。会设置 this.options.distinct
属性,便于后续分析。
module.exports = class extends think.Model {
getList(){
//SELECT DISTINCT `name` FROM `think_user`
return this.distinct('name').select();
}
}
data
{Object} 要添加的数据添加前置操作。
data
{Object} 要添加的数据添加后置操作。
删除后置操作。
data
{Object} 要更新的数据更新前置操作。
有时候希望提交了某值则更新,如果值为空的话就不更新的功能,那么可以通过这个方法来操作:
module.exports = class extends think.Model {
beforeUpdate(data) {
for (const key in data) {
// 如果值为空则不更新
if(data[key] === '') {
delete data[key];
}
}
return data;
}
}
data
{Object} 要更新的数据更新后置操作。
data
{Object} 查询的单条数据return
{Object | Promise}find
查询后置操作。
data
[Array] 查询的数据return
{Array | Promise}select
查询后置操作。
data
{Object} 要添加的数据,如果数据里某些字段在数据表里不存在会自动被过滤掉options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 返回插入的 ID添加一条数据,返回值为插入数据的 id。
如果数据表没有主键或者没有设置 auto increment
等属性,那么返回值可能为 0。如果插入数据时手动设置主键的值,那么返回值也可能为 0。
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertId = await model.add({name: 'xxx', pwd: 'yyy'});
}
}
有时候希望通过 REPLACE INTO
来代替 INSERT INTO
,那么可以传递 options.replace
:
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertId = await model.add({
name: 'xxx',
pwd: 'yyy'
}, {
replace: true
});
}
}
如果想用 INSERT IGNORE INTO
,那么可以:
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertId = await model.add({
name: 'xxx',
pwd: 'yyy'
}, {
ignore: true
});
}
}
有时候需要借助数据库的一些函数来添加数据,如:时间戳使用 mysql 的 CURRENT_TIMESTAMP
函数,这时可以借助 exp
表达式来完成。
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertId = await model.add({
name: 'test',
time: ['exp', 'CURRENT_TIMESTAMP()']
});
}
}
data
{Object} 要添加的数据where
{Object} where 条件,会通过 where 方法设置 where 条件return
{Promise}当 where 条件未命中到任何数据时才添加数据。
module.exports = class extends think.Controller {
async addAction(){
const model = this.model('user');
//第一个参数为要添加的数据,第二个参数为添加的条件,根据第二个参数的条件查询无相关记录时才会添加
const result = await model.thenAdd({name: 'xxx', pwd: 'yyy'}, {email: 'xxx'});
// result returns {id: 1000, type: 'add'} or {id: 1000, type: 'exist'}
}
}
也可以把 where 条件通过 this.where
方法直接指定,如:
module.exports = class extends think.Controller {
async addAction(){
const model = this.model('user');
const result = await model.where({email: 'xxx'}).thenAdd({name: 'xxx', pwd: 'yyy'});
// result returns {id: 1000, type: 'add'} or {id: 1000, type: 'exist'}
}
}
注:thenAdd()
本质是 find()
和 add()
方法的包装,有使用 beforeAdd()
, afterAdd()
, beforeFind()
, afterFind()
钩子函数的需要注意一下。
dataList
{Array} 要添加的数据列表options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 返回插入的 ID 列表一次添加多条数据。
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertIds = await model.addMany([
{name: 'xxx', pwd: 'yyy'},
{name: 'xxx1', pwd: 'yyy1'}
]);
}
}
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertIds = await model.addMany([
{name: 'xxx', pwd: 'yyy'},
{name: 'xxx1', pwd: 'yyy1'}
], {
ignore: true // 使用 INSERT IGNORE INTO
});
}
}
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertIds = await model.addMany([
{name: 'xxx', pwd: 'yyy'},
{name: 'xxx1', pwd: 'yyy1'}
], {
replace: true // 使用 REPLACE INTO
});
}
}
options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 返回插入的 ID 列表添加从 options 解析出来子查询的结果数据。
module.exports = class extends think.Controller {
async addAction(){
let model = this.model('user');
let insertIds = await model.selectAdd({table: 'user_2'});
}
}
module.exports = class extends think.Controller {
async addAction(){
const user = this.model('user');
const user2 = this.model('user2').field('name');
const options = await user2.parseOptions();
let insertIds = await user.field('name').selectAdd(options);
}
}
module.exports = class extends think.Controller {
async addAction(){
const user = this.model('user');
const user2 = this.model('user2').field('name');
const options = await user2.parseOptions();
options.replace = true; // 使用 REPLACE INTO
let insertIds = await user.field('name').selectAdd(options);
}
}
module.exports = class extends think.Controller {
async addAction(){
const user = this.model('user');
const user2 = this.model('user2').field('name');
const options = await user2.parseOptions();
options.ignore = true; // 使用 INSERT IGNORE INTO
let insertIds = await user.field('name').selectAdd(options);
}
}
options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 返回影响的行数删除数据。
module.exports = class extends think.Controller {
async deleteAction(){
let model = this.model('user');
let affectedRows = await model.where({id: ['>', 100]}).delete();
}
}
data
{Object} 要更新的数据options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 返回影响的行数更新数据。
module.exports = class extends think.Controller {
async updateAction(){
let model = this.model('user');
let affectedRows = await model.where({name: 'thinkjs'}).update({email: 'admin@thinkjs.org'});
}
}
默认情况下更新数据必须添加 where 条件,以防止误操作导致所有数据被错误的更新。如果确认是更新所有数据的需求,可以添加 1=1
的 where 条件进行,如:
module.exports = class extends think.Controller {
async updateAction(){
let model = this.model('user');
let affectedRows = await model.where('1=1').update({email: 'admin@thinkjs.org'});
}
}
有时候更新值需要借助数据库的函数或者其他字段,这时候可以借助 exp
来完成。
module.exports = class extends think.Controller {
async updateAction(){
let model = this.model('user');
let affectedRows = await model.where('1=1').update({
email: 'admin@thinkjs.org',
view_nums: ['exp', 'view_nums+1'],
update_time: ['exp', 'CURRENT_TIMESTAMP()']
});
}
}
data
{Object} 要更新的数据where
{Object} where 条件return
{Promise}当 where 条件未命中到任何数据时添加数据,命中数据则更新该数据。
注:thenUpdate()
本质是 find()
和 add()
、update()
方法的包装,有使用 beforeAdd()
, afterAdd()
, beforeFind()
, afterFind()
, beforeUpdate
, afterUpdate()
钩子函数的需要注意一下。
dataList
{Array} 要更新的数据列表options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 影响的行数更新多条数据,dataList 里必须包含主键的值,会自动设置为更新条件。
this.model('user').updateMany([{
id: 1, // 数据里必须包含主键的值
name: 'name1'
}, {
id: 2,
name: 'name2'
}])
field
{String} 字段名step
{Number} 增加的值,默认为 1return
{Promise}字段值增加。
module.exports = class extends think.Model {
updateViewNums(id){
return this.where({id: id}).increment('view_nums', 1); //将阅读数加 1
}
updateViewAndUserNums(id) {
return this.where({id}).increment(['view_nums', 'user_nums'], 1); //将阅读数和阅读人数加 1
}
updateViewAndUserNums(id) {
return this.where({id}).increment({view_nums: 2, user_nums: 1}); //将阅读数加2,阅读人数加 1
}
}
field
{String} 字段名step
{Number} 减少的值,默认为 1return
{Promise}字段值减少。
module.exports = class extends think.Model {
updateViewNums(id){
return this.where({id: id}).decrement('coins', 10); //将金币减 10
}
}
options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 返回单条数据查询单条数据,返回的数据类型为对象。如果未查询到相关数据,返回值为 {}
。
module.exports = class extends think.Controller {
async listAction(){
let model = this.model('user');
let data = await model.where({name: 'thinkjs'}).find();
//data returns {name: 'thinkjs', email: 'admin@thinkjs.org', ...}
if(think.isEmpty(data)) {
// 内容为空时的处理
}
}
}
可以通过 think.isEmpty 方法判断返回值是否为空。
options
{Object} 操作选项,会通过 parseOptions 方法解析return
{Promise} 返回多条数据查询多条数据,返回的数据类型为数组。如果未查询到相关数据,返回值为 []
。
module.exports = class extends think.Controller {
async listAction(){
let model = this.model('user');
let data = await model.limit(2).select();
//data returns [{name: 'thinkjs', email: 'admin@thinkjs.org'}, ...]
if(think.isEmpty(data)){
}
}
}
可以通过 think.isEmpty 方法判断返回值是否为空。
options
{Number | Object} 操作选项,会通过 parseOptions 方法解析pageFlag
{Boolean} 当页数不合法时处理,true 为修正到第一页,false 为修正到最后一页,默认不修正return
{Promise}分页查询,一般需要结合 page
方法一起使用。如:
module.exports = class extends think.Controller {
async listAction(){
let model = this.model('user');
let data = await model.page(this.get('page')).countSelect();
}
}
返回值数据结构如下:
{
pageSize: 10, //每页显示的条数, think-model@1.1.8 之前该字段为 pagesize
currentPage: 1, //当前页
count: 100, //总条数
totalPages: 10, //总页数
data: [{ //当前页下的数据列表
name: "thinkjs",
email: "admin@thinkjs.org"
}, ...]
}
有时候总条数是放在其他表存储的,不需要再查当前表获取总条数了,这个时候可以通过将第一个参数 options
设置为总条数来查询。
module.exports = class extends think.Controller {
async listAction(){
const model = this.model('user');
const total = 256;
// 指定总条数查询
const data = await model.page(this.get('page')).countSelect(total);
}
}
field
{String} 字段名,多个字段用逗号隔开num
{Boolean | Number} 需要的条数return
{Promise}获取特定字段的值,可以设置 where、group 等条件。
** 获取单个字段的所有列表 **
module.exports = class extends think.Controller {
async listAction(){
const data = await this.model('user').getField('c_id');
// data = [1, 2, 3, 4, 5]
}
}
** 指定个数获取单个字段的列表 **
module.exports = class extends think.Controller {
async listAction(){
const data = await this.model('user').getField('c_id', 3);
// data = [1, 2, 3]
}
}
** 获取单个字段的一个值 **
module.exports = class extends think.Controller {
async listAction(){
const data = await this.model('user').getField('c_id', true);
// data = 1
}
}
** 获取多个字段的所有列表 **
module.exports = class extends think.Controller {
async listAction(){
const data = await this.model('user').getField('c_id,d_name');
// data = {c_id: [1, 2, 3, 4, 5], d_name: ['a', 'b', 'c', 'd', 'e']}
}
}
** 获取指定个数的多个字段的所有列表 **
module.exports = class extends think.Controller {
async listAction(){
const data = await this.model('user').getField('c_id,d_name', 3);
// data = {c_id: [1, 2, 3], d_name: ['a', 'b', 'c']}
}
}
** 获取多个字段的单一值 **
module.exports = class extends think.Controller {
async listAction(){
const data = await this.model('user').getField('c_id,d_name', true);
// data = {c_id: 1, d_name: 'a'}
}
}
field
{String} 字段名,如果不指定那么值为 *
return
{Promise} 返回总条数获取总条数。
module.exports = class extends think.Model{
// 获取总条数
getScoreCount() {
// SELECT COUNT(score) AS think_count FROM `test_d` LIMIT 1
return this.count('score');
}
}
field
{String} 字段名return
{Promise}对字段值进行求和。
module.exports = class extends think.Model{
// 获取字段值之和
getScoreSum() {
// SELECT SUM(score) AS think_sum FROM `test_d` LIMIT 1
return this.sum('score');
}
}
field
{String} 字段名return
{Promise}求字段的最小值。
module.exports = class extends think.Model{
// 获取最小值
getScoreMin() {
// SELECT MIN(score) AS think_min FROM `test_d` LIMIT 1
return this.min('score');
}
}
field
{String} 字段名return
{Promise}求字段的最大值。
module.exports = class extends think.Model{
// 获取最大值
getScoreMax() {
// SELECT MAX(score) AS think_max FROM `test_d` LIMIT 1
return this.max('score');
}
}
field
{String} 字段名return
{Promise}求字段的平均值。
module.exports = class extends think.Model{
// 获取平均分
getScoreAvg() {
// SELECT AVG(score) AS think_avg FROM `test_d` LIMIT 1
return this.avg('score');
}
}
sqlOptions
{String | Object} 要执行的 sql 选项return
{Promise} 查询的数据指定 SQL 语句执行查询,sqlOptions
会通过 parseSql 方法解析,使用该方法执行 SQL 语句时需要自己处理安全问题。
module.exports = class extends think.Model {
getMysqlVersion() {
return this.query('select version();');
}
}
sqlOptions
{String | Object} 要操作的 sql 选项return
{Promise}执行 SQL 语句,sqlOptions
会通过 parseSql 方法解析,使用该方法执行 SQL 语句时需要自己处理安全问题。
module.exports = class extends think.Model {
xxx() {
return this.execute('set @b=5;call proc_adder(2,@b,@s);');
}
}
sqlOptions
{String | Object} 要解析的 SQL 语句...args
{Array} 解析的数据return
{Object}解析 SQL 语句,将 SQL 语句中的 __TABLENAME__
解析为对应的表名。通过 util.format 将 args 数据解析到 sql 中。
module.exports = class extends think.Model {
getSql(){
const sql = 'SELECT * FROM __GROUP__ WHERE id=10';
const sqlOptions = this.parseSql(sql);
//{sql: "SELECT * FROM think_group WHERE id=10"}
}
getSql2(){
const sql = 'SELECT * FROM __GROUP__ WHERE id=10';
const sqlOptions = this.parseSql({sql, debounce: false});
//{sql: SELECT * FROM think_group WHERE id=10", debounce: false}
}
}
options
{Object} 要合并的 options,会合并到 this.options
中一起解析return
{Promise}解析 options。where、limit、group 等操作会将对应的属性设置到 this.options
上,该方法会对 this.options
进行解析,并追加对应的属性,以便在后续的处理需要这些属性。
const options = await this.parseOptions({limit: 1});
/**
options = {
table: '',
tablePrefix: '',
pk: '',
field: '',
where: '',
limit: '',
group: '',
...
}
*/
调用 this.parseOptions
解析后,this.options
属性会被置为空对象 {}
。
return
{Promise}开启事务。
return
{Promise}提交事务。
return
{Promise}回滚事务。
module.exports = class extends think.Model {
async addData() {
// 如果添加成功则 commit,失败则 rollback
try {
await this.startTrans();
const result = await this.add({});
await this.commit();
return result;
} catch(e){
await this.rollback();
}
}
}
如果事务操作过程中需要实例化多个模型操作,那么需要让模型之间复用同一个数据库连接,具体见 model.db。
fn
{Function} 要执行的函数,如果有异步操作,需要返回 Promisereturn
{Promise}使用事务来执行传递的函数,函数要返回 Promise。如果函数返回值为 Resolved Promise,那么最后会执行 commit,如果返回值为 Rejected Promise(或者报错),那么最后会执行 rollback。
module.exports = class extends think.Model {
async updateData(data){
const result = await this.transaction(async () => {
const insertId = await this.add(data);
return insertId;
})
}
}
由于事务里的操作需要在同一个连接里执行,如果处理过程中涉及多个模型的操作,需要多个模型复用同一个数据库连接,这时可以通过 model.db
方法达到复用数据库连接的效果。
module.exports = class extends think.Model {
async updateData(data){
const result = await this.transaction(async () => {
const insertId = await this.add(data);
// 通过 db 方法让 user_cate 模型复用当前模型的数据库连接
const userCate = this.model('user_cate').db(this.db());
let result = await userCate.add({user_id: insertId, cate_id: 100});
return result;
})
}
}
由于手动调用 db
方法比较麻烦,并且容易遗漏。从 think-model@1.5.0
开始,支持配置 reuseDB: true
后自动复用数据库连接,就不用再手动调用 db
方法了。这个方法是通过将数据库连接挂在到 ctx
上来完成的,也就是说在一个请求生命周期内都会复用这个连接,除非是通过 model
方法实例化时手动传入 reuseDB: false
关闭当前实例的复用。
由于 Service
中默认没有 ctx
对象,如果也想复用数据库连接的话,可以自己包装下,将 ctx
传入进去,然后将 model
方法重新改下即可。
export class BaseService extends think.Service {
constructor(ctx) {
this.ctx = ctx;
}
model(name, config) {
return this.ctx.model(name, config);
}
}
key
{String} 缓存 key,如果不设置会获取 SQL 语句的 md5 值作为 keyconfig
{Mixed} 缓存配置return
{this}设置查询缓存,只在 select
、find
、getField
等查询相关的方法下有效。会自动合并 cache Adapter、model cache 的配置。
// cache adapter 配置
exports.cache = {
type: 'file',
file: {
handle: fileCache,
...
}
}
// model adapter 配置
exports.model = {
type: 'mysql',
mysql: {
handle: mysqlModel,
...
cache: { // 额外的缓存配置
type: 'file',
handle: fileCache
}
}
}
最终会将 cache adapter 配置、model cache 配置、以及参数里的配置合并起来作为 cache 的配置。
module.exports = class extends think.Controller {
indexAction() {
// 设置缓存 key 为 userList,有效期为 2 个小时
return this.model('user').cache('userList', {timeout: 2 * 3600 * 1000}).select();
}
}
lock
{Boolean} 是否 lockreturn
{this}SELECT 时加锁,在 SELECT 语句后面加上 FOR UPDATE
。
module.exports = class extends think.Controller {
async indexAction() {
const user = this.model('user');
const data = await user.lock(true).where({id: 1}).find();
await user.where({id: data}).update({score: 1});
}
}
options
{object} noParentheses
{boolean} 返回的字符串前后不加圆括号return
{String}根据条件生成 SELECT 语句。
module.exports = class extends think.Controller {
async indexAction() {
const user = this.model('user');
const sql = await user.where({id: 1}).buildSelectSql();
}
}
为了查询语句有更高的性能,我们认为,在一次 SQL 语句查询期间,有相同的 SQL 语句需要执行时,那么返回的值是一样的,那么就可以把第一次的查询结果缓存,然后同步给后面的查询语句即可,我们称之为 debounce
。
如果不希望开启这个功能,那么可以在数据库配置中添加 debounce: false
来关闭,如:
const mysql = require('think-model-mysql');
exports.model = {
type: 'mysql',
mysql: {
handle: mysql, // Adapter handle
user: 'root', // 用户名
password: '', // 密码
database: '', // 数据库
host: '127.0.0.1', // host
port: 3306, // 端口
connectionLimit: 1, // 连接池的连接个数,默认为 1
prefix: '', // 数据表前缀,如果一个数据库里有多个项目,那项目之间的数据表可以通过前缀来区分
debounce: false // 关闭 debounce 功能
}
}
数据库的编码一般会设置为 utf8
,但 utf8 并不支持 emoji 表情,如果需要数据库支持 emoji 表情,需要将数据库编码设置为 utf8mb4
。
同时需要在数据库配置中添加或修改 charset
的值为 utf8mb4
,如:
const mysql = require('think-model-mysql');
exports.model = {
type: 'mysql',
mysql: {
handle: mysql, // Adapter handle
user: 'root', // 用户名
password: '', // 密码
database: '', // 数据库
host: '127.0.0.1', // host
port: 3306, // 端口
connectionLimit: 1, // 连接池的连接个数,默认为 1
prefix: '', // 数据表前缀,如果一个数据库里有多个项目,那项目之间的数据表可以通过前缀来区分
charset: 'utf8mb4'
}
}
after(Find|Select)
钩子之后关联模型数据未获取因为关联模型也是利用这几个钩子来实现的,如果在继承类中复写了这几个方法的话需要手动的调用基类中的同名方法才会执行关联模型数据获取。
module.exports = class extends think.Model {
async afterFind(...args) {
await super.afterFind(...args);
//do something...
}
}