type
status
date
slug
summary
tags
category
icon
password

前置知识简介:链式调用与方法

gorm中,方法是用来进行具体处理逻辑的函数。
gorm中的方法有三种,Chain MethodFinisher MethodNew Session Method.
  1. chain method可以用来将特定筛选条件增加到数据库的状态中;
    1. 常见的有db.where,db.select等
  1. finisher method可以立即执行回调,生成并执行sql语句。
    1. 比如create,first,find等
  1. new session method用于新建会话,相当于将db的状态更新。
    1. gorm通过链式的方式允许我们调用这些函数,也就是说你可以很方便的用
       
这样的方式完成代码,而不必一层套一层的函数( A(B(param a,param b)) )
值得注意的是,chain method与finisher method都会返回一个新的db实例
 

db的clone

我们先来看一下下面的两段代码
 
这两段代码,各自打开了一个数据库,有筛选条件,有查询操作,不同的是,第一段代码自始至终都只用db这一个数据库实例,而第二段后面都在用tx这一个db.where返回的db新实例进行查询操作。
 
你知道这两段代码中的查询操作翻译成sql语句是什么吗?
思考三秒钟,以下是答案:
 
相信几乎所有人都能得到第一段代码的正确结果,而对于第二段代码,我们惊奇地发现,select语句中的查询条件居然包含了之前代码中所用到的。
gorm官方文档指出了这一现象,很贴心地说这一种方式使得数据库实例不可reuse,毕竟查询条件都被污染了,而要究其根底,还得去翻阅源码。
 
让我们先看一下DB的结构
其中statement用来存储和该DB变量状态相关的信息,我们前面说过的where等查询条件,就和statement这个量密切相关。
我们重点放在这个clone上。
 
可以先看一下初次用这个db实例,该clone变量会变为多少
以下是open函数的源码的一部分:
 
可以看到,clone的初始值为1,我们现在知道,当我们连接数据库,得到一个db实例的时候,clone默认为1;
继续,让我们看一下session函数,即db配置函数的源码的一部分:
我们看到,当config.newdb为false时,clone为2,否则为1;
 
我们知道了这么多 某某情况下clone值为多少,现在我们需要知道 clone值对应的处理逻辑是什么。
 
如此,我们不妨看一下db.where函数的源码,作为我们开始时展示的例子,他一定有用到clone这一属性:
我们从这里能看到,where传入的所有参数,都不是直接作用于db身上的,而是通过db.getinstance,得到一个变量,再对这个变量进行操作。
我们顺下去,看这个db.getinstance的源码:
 
终于,我们在这里看到了clone的处理逻辑。
 
当clone小于或等于0,db.getinstance直接返回db本身;否则先生成一个与db共享一些量的tx,但是clone这个字段被设置为默认值,也就是0;
 
当clone等于1时,tx生成一个新的statement,独立于原来db的statement,这个新的statement的clauses(相当于筛选条件,比如where中的语句就被这一变量处理)为空,并不含有之前的内容;
 
当clone大于0且不为1时,tx的状态直接调用db.statement.clone()函数,并将db调整为tx;
以下为这个函数的源码的一部分:
我们可以看到,这个函数将之前的db的statement中的clause全部复制到了newStmt中,这相当于tx这个DB实例拥有之前所添加过的所有条件。这里,就是gorm官方文档中所说的 not reusable的根本所在。
 
现在,让我们回到一开始的那个例子
这段代码中,第一行db.clone默认为1;
第二行db.where生成并返回了一个新实例tx,where中的条件被加进来tx.statement中,然后又有一个where函数,相当于tx.where().Find(),逻辑和之前一样,最后相当于tx2.Find(),在tx2这个实例上查找符合条件的记录;
第三行,我们复用了db,db的statement中的条件不含有第二行所用过的,因为之前都被加进了tx,tx2中,和db的状态无关;
 
这段代码中,tx为db.where返回的那一个新的DB实例,必然会带上name=“jinzhu”这一条件,而且注意,根据之前我们看到的getinstance函数的源码,这个tx的clone是默认值:0;也就是说,在日后通过这个tx调用getinstance时,由于clone为0,getinstance直接返回tx本身,这也就是为什么后面所有条件都会累加到tx自己的statement上。
 
那么,有什么解决办法吗?
显然是有的。
我们可以使用诸如Session,Debug等函数,这些函数内部的设置会使得clone的值符合我们的需求;
以session函数为例,传入一个空的sesion结构体,其中的NewDB字段为默认值:false,按照我们之前的源码:
这个tx的clone被设置成了2,那么其后使用where等函数时,调用的getinstance便不会直接返回tx本身,按照之前所看到的源码,应该是生成一个新的DB实例,且含有之前的状态中的条件(注意,这里含有的条件显然是tx的条件,而不是由tx上一个生成的新实例的条件,这些新实例是平行关系,互不影响)。
 
全文终。
 
欲望的满足 飞书群聊如何自动通知github仓库代码提交
Alex
Alex
某不知名青年|web2.5人士|喜欢猫与美少女
公告
type
status
date
slug
summary
tags
category
icon
password
有事请邮箱联系:alexwu7@outlook.com
🚀🚀🚀