首页>>后端>>Golang->Golang中gorm库与标准sql库对比分析

Golang中gorm库与标准sql库对比分析

时间:2023-11-29 本站 点击:0

本文章不做全面比较,只是比较当数据表存在null字段时,基于gorm库和sql标准库的一个重要的差异。假设mysql存在如下一张表:

CREATETABLE`student`(`id`intNOTNULL,`name`varchar(20)DEFAULT'',`score`intDEFAULTNULL,`classes`intDEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb3
mysql>select*fromstudent;+----+-------+-------+---------+|id|name|score|classes|+----+-------+-------+---------+|1|test1|100|3||2|test2|100|NULL|+----+-------+-------+---------+2rowsinset(0.00sec)

ps: 当然一般创建数据表时,规范的做法是,字段需要not null default 0指定默认值。这里只是为了演示两种库的差异。

gorm

packagemainimport("fmt""gorm.io/driver/mysql""gorm.io/gorm")typeStudentstruct{NamestringIdintScorefloat64ClassesintAvgScorefloat64}funcmain(){dsn:="root:12345678@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"db,err:=gorm.Open(mysql.Open(dsn),&gorm.Config{})iferr!=nil{panic(err)}varstuStudentdb.Select("id,name,score,classes,score/classesasavg_score").Table("student").Where("id=?",2).Find(&stu)fmt.Println(stu)}

输出:

{test2210000}

可以看出null值被转换成了0,而且没有异常抛出。

sql

packagemainimport("fmt""database/sql"_"github.com/go-sql-driver/mysql")typeStudentstruct{NamestringIdintScorefloat64ClassesintAvgScorefloat64}funcmain(){db,err:=sql.Open("mysql","root:12345678@tcp(127.0.0.1:3306)/test")iferr!=nil{panic(err)}rows,err:=db.Query("selectid,name,score,classes,score/classesasavg_scorefromstudent")iferr!=nil{panic(err)}varstuStudentforrows.Next(){err:=rows.Scan(&stu.Id,&stu.Name,&stu.Score,&stu.Classes,&stu.AvgScore)iferr!=nil{panic(err)}fmt.Println(stu)}}

输出:

{test11100333.3333}panic:sql:Scanerroroncolumnindex3,name"classes":convertingNULLtointisunsupportedgoroutine1[running]:main.main()/xxx/core/sqls/yyy/test2.go:34+0x2d9exitstatus2

类型转换的时候抛了异常。查看Scan代码,包含一个convertAssignRows调用:

switchdv.Kind(){casereflect.Ptr:ifsrc==nil{dv.Set(reflect.Zero(dv.Type()))returnnil}dv.Set(reflect.New(dv.Type().Elem()))returnconvertAssignRows(dv.Interface(),src,rows)//目标类型为如下casereflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64://结果类型为nilifsrc==nil{returnfmt.Errorf("convertingNULLto%sisunsupported",dv.Kind())}s:=asString(src)i64,err:=strconv.ParseInt(s,10,dv.Type().Bits())iferr!=nil{err=strconvErr(err)returnfmt.Errorf("convertingdriver.Valuetype%T(%q)toa%s:%v",src,s,dv.Kind(),err)}dv.SetInt(i64)returnnilcasereflect.Uint,reflect.Uint8,reflect.Uint16,reflect.Uint32,reflect.Uint64:ifsrc==nil{returnfmt.Errorf("convertingNULLto%sisunsupported",dv.Kind())}s:=asString(src)u64,err:=strconv.ParseUint(s,10,dv.Type().Bits())iferr!=nil{err=strconvErr(err)returnfmt.Errorf("convertingdriver.Valuetype%T(%q)toa%s:%v",src,s,dv.Kind(),err)}dv.SetUint(u64)returnnilcasereflect.Float32,reflect.Float64:ifsrc==nil{returnfmt.Errorf("convertingNULLto%sisunsupported",dv.Kind())}s:=asString(src)f64,err:=strconv.ParseFloat(s,dv.Type().Bits())iferr!=nil{err=strconvErr(err)returnfmt.Errorf("convertingdriver.Valuetype%T(%q)toa%s:%v",src,s,dv.Kind(),err)}dv.SetFloat(f64)returnnilcasereflect.String:ifsrc==nil{returnfmt.Errorf("convertingNULLto%sisunsupported",dv.Kind())}switchv:=src.(type){casestring:dv.SetString(v)returnnilcase[]byte:dv.SetString(string(v))returnnil}}

当查询结果类型为nil, 目标类型为int时,会抛类型转换的异常。但是如果目标类型为指针类型,不会抛异常,所以将代码改为:

packagemainimport("fmt""database/sql"_"github.com/go-sql-driver/mysql")typeStudentstruct{NamestringIdintScorefloat64Classes*intAvgScore*float64}funcmain(){db,err:=sql.Open("mysql","root:12345678@tcp(127.0.0.1:3306)/test")iferr!=nil{panic(err)}rows,err:=db.Query("selectid,name,score,classes,score/classesasavg_scorefromstudent")iferr!=nil{panic(err)}varstuStudentforrows.Next(){err:=rows.Scan(&stu.Id,&stu.Name,&stu.Score,&stu.Classes,&stu.AvgScore)iferr!=nil{panic(err)}fmt.Println(stu)}}

输出:

{test111000xc0000163580xc000016360}{test22100<nil><nil>}

结论:基于SQL库的查询需要注意结果字段为NULL的情况,对应结构体字段需要声明为指针类型。那么,为什么基于gorm库的不需要考虑这种情况呢?

//assignstmt.ReflectValueifstmt.Dest!=nil{stmt.ReflectValue=reflect.ValueOf(stmt.Dest)forstmt.ReflectValue.Kind()==reflect.Ptr{ifstmt.ReflectValue.IsNil()&&stmt.ReflectValue.CanAddr(){stmt.ReflectValue.Set(reflect.New(stmt.ReflectValue.Type().Elem()))}stmt.ReflectValue=stmt.ReflectValue.Elem()}if!stmt.ReflectValue.IsValid(){db.AddError(ErrInvalidValue)}}

因为gorm库本身已经做了这种兼容处理。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Golang/273.html