当前位置: 首页 > 后端技术 > PHP

gorm如何保证协程的安全

时间:2023-03-29 18:44:56 PHP

gorm官方文档提供了如何正确使用链式调用的例子以及导致协程不安全的反例。知道如何正确使用它们,知道其中的原理,可以让你更加安心。下面我们从文档示例和源码入手,分析Gorm在链式调用时如何保证协程的安全?源码分析下面是一段gorm的常用使用代码,先初始化连接,然后根据链式调用进行增删改查。db,err:=gorm.Open(mysql.Open(dsn),&gorm.Config{})iferr!=nil{panic("连接数据库失败")}tx:=db.Where("age=22").Where("name='小明'").Find(&user)通过gorm.Open()初始化连接,得到一个*gorm.DB结构指针db。结构体的克隆属性为1.typeDBstruct{*ConfigErrorerrorRowsAffectedint64Statement*Statementcloneint}funcOpen(dialectorDialector,opts...Option)(db*DB,errerror){//...db=&DB{Config:config,clone:1}//...}通过db.Where()进行链式查询,Where()方法也会返回一个*gorm.DB结构指针tx。//Whereaddconditionsfunc(db*DB)Where(queryinterface{},args...interface{})(tx*DB){tx=db.getInstance()ifconds:=tx.Statement.BuildCondition(query,参数...);len(conds)>0{tx.Statement.AddClause(clause.Where{Exprs:conds})}return}查看源码,可以发现每一个chain方法中,都必须先获取*gorm.DB结构指针tx,即tx=db.getInstance()。func(db*DB)getInstance()*DB{ifdb.clone>0{tx:=&DB{Config:db.Config,Error:db.Error}ifdb.clone==1{//使用新语句克隆tx.Statement=&Statement{DB:tx,ConnPool:db.Statement.ConnPool,Context:db.Statement.Context,Clauses:map[string]clause.Clause{},Vars:make([]interface{},0,8),}}else{//withclone语句tx.Statement=db.Statement.clone()tx.Statement.DB=tx}returntx}returndb}传入的*gorm.DB指针指向的结构体时属性clone为1,将克隆一个DB结构,并返回一个新的指针指向这个DB结构,即创建一个新的会话。当clone为0时,返回原指针指向的地址。db,err:=gorm.Open(mysql.Open(dsn),&gorm.Config{})iferr!=nil{panic("连接数据库失败")}db.Where().Where()//等价因为tx1=db.Where()tx2=tx.Where()根据上面的分析,在这个示例代码中,由于db指针指向gorm.DB结构体的clone属性为1。tx1会指向一个cloneclone属性为0的新gorm.DB结构。因此,tx.Where()不会复制新结构(不会创建新会话),即tx2和tx1都指向同一个gorm.DB结构,这与db不同。例子经过上面的分析,再看看官方文档中的例子,就可以明白如何使用协程安全的链式调用了。Example1db,err:=gorm.Open(sqlite.Open("test.db"),&gorm.Config{})//安全地使用新初始化的*gorm.DBfori:=0;我<100;i++{godb.Where(...).First(&user)}在100个协程中,db.Where()会分别拷贝gorm.DB结构体,返回其指针,即创建一个新的session,然后继续连锁电话。所以100个协程中,链式调用拼接的sql查询不会互相干扰。Example2tx:=db.Where("name=?","jinzhu")//不安全复用语句fori:=0;我<100;i++{gotx.Where(...).First(&user)}本例中tx指向一个新的gorm.DB结构,clone为0,所以tx.Where()不会生成新的结构,即是的,它不会创建新会话。那么在这100个协程中,tx指向的gorm.BD是共享的,这样就会造成协程之间相互干扰的问题。示例3tx:=db.Where("name=?","jinzhu").Session(&gorm.Session{})//在“新会话方法”之后是安全的fori:=0;我<100;i++{gotx.Where(...).First(&user)//`name='jinzhu'`会被应用到query}通过Session()方法创建一个新的session,并设置session的clone属性tx指向的新结构设置为1。“tx具有示例1中的db的效果”。示例4ctx,_:=context.WithTimeout(context.Background(),time.Second)ctxDB:=db.WithContext(ctx)//在“新会话方法”之后是安全的fori:=0;我<100;i++{goctxDB.Where(...).First(&user)}ctx,_:=context.WithTimeout(context.Background(),time.Second)ctxDB:=db.Where("名称=?","jinzhu").WithContext(ctx)//在`newsessionmethod`之后是安全的fori:=0;我<100;i++{goctxDB.Where(...).First(&user)//`name='jinzhu'`将应用于查询}这两个方法重用了Session方法。结束!引用链方法-来自Howgormensurescoroutinesecurity的GORM文章