游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

使用gorm。DefaultTableNameHandler

  业务背景
  有这样的业务场景,线上一个表tablea,线上环境还有一个镜像表tableamirror,你需要当请求中有一些tag标识的时候,访问tableamirror表先安装sqlite
  https:wangxiaoming。blog。csdn。netarticledetails121884736代码
  可以使用DefaultTableNameHandler来实现加前缀或者后缀功能。import(code。byted。orggopkggormcontext)typedbStagingPostfixKeyTypestruct{}vardbStagingPostfixKeydbStagingPostfixKeyType{}funcWithDbStagingPostfix(ctxcontext。Context,postfixstring)context。Context{returncontext。WithValue(ctx,dbStagingPostfixKey,postfix)}funcReWriteTableName(){gorm。DefaultTableNameHandlerfunc(dbgorm。DB,defaultTableNamestring)string{ifv:db。Ctx。Value(dbStagingPostfixKey);v!nil{returndefaultTableNamev。(string)}returndefaultTableName}}测试代码packagemysqlimport(fmttestinggithub。comjinzhugormgithub。comjinzhugormdialectssqlitegithub。commattngosqlite3)typeProductstruct{gorm。ModelCodestringPriceuint}func(Product)TableName()string{returnhaxproducts}funcTest(ttesting。T){db,err:gorm。Open(sqlite3,test。db)iferr!nil{panic(failedtoconnectdatabase)}deferdb。Close()gorm。DefaultTableNameHandlerfunc(dbgorm。DB,defaultTableNamestring)string{returnhaxdefaultTableName}db。LogMode(true)Migratetheschemadb。AutoMigrate(Product{})db。Create(Product{Code:L1212,Price:1000})varproductProductdb。First(product,1)varproducts〔〕Productdb。Find(products)fmt。Printf(Totalcountd,len(products))}
  执行结果:(Usersxxxgosrcxxxxxx。xxGoProjectmysqlsqllitetest。go:33)〔2021121421:43:33〕〔1。38ms〕INSERTINTOhaxproducts(createdat,updatedat,deletedat,code,price)VALUES(2021121421:43:33,2021121421:43:33,NULL,L1212,1000)〔1rowsaffectedorreturned〕(Usersxxxgosrcxxxxxx。xxGoProjectmysqlsqllitetest。go:35)〔2021121421:43:33〕〔0。23ms〕SELECTFROMhaxproductsWHEREhaxproducts。deletedatISNULLAND((haxproducts。id1))ORDERBYhaxproducts。idASCLIMIT1〔1rowsaffectedorreturned〕((Usersxxxgosrcxxxxxx。xxGoProjectmysqlsqllitetest。go:37)〔2021121421:43:33〕nosuchtable:haxhaxproducts((Usersxxxgosrcxxxxxx。xxGoProjectmysqlsqllitetest。go:37)〔2021121421:43:33〕〔0。10ms〕SELECTFROMhaxhaxproductsWHEREhaxhaxproducts。deletedatISNULL〔0rowsaffectedorreturned〕Totalcount0PASS:Test(0。00s)PASS
  根据执行结果,可以看到,创建语言与查询单条记录时表名为haxproducts但是查询多条记录时,却使用了表名haxhaxproducts。这个就是坑1
  查询单个记录时使用了TableName()返回的表名,而在查询结果为Array时,表名在TableName()的基础上又添加了前缀。
  Gorm结构体一般分析如下structtypeDBstruct(gormmain。go)代表数据库连接,每次操作数据库会创建出clone对象。方法gorm。Open()返回的值类型就是这个结构体指针。typeScopestruct(gormscope。go)当前数据库操作的信息,每次添加条件时也会创建clone对象。typeCallbackstruct(gormcallback。go)数据库各种操作的回调函数,SQL生成也是靠这些回调函数。每种类型的回调函数放在单独的文件里,比如查询回调函数在gormcallbackquery。go,创建的在gormcallbackcreate。godb。First()代码分析
  First()方法位于gormmain。go文件中,。callCallbacks(s。parent。callbacks。queries)调用了query回调函数。file:gormmain。goFirstfindfirstrecordthatmatchgivenconditions,orderbyprimarykeyfunc(sDB)First(outinterface{},where。。。interface{})DB{newScope:s。NewScope(out)newScope。Search。Limit(1)returnnewScope。Set(gorm:orderbyprimarykey,ASC)。inlineCondition(where。。。)。callCallbacks(s。parent。callbacks。queries)。db}
  Callback结构体中定义queries为函数指针数组,而默认值的初始化在gormcallbackquery。go的init()方法中,查询方法为queryCallback,而queryCallback()方法又调用到scope。prepareQuerySQL(),scope中的方法真正生成SQL的地方。file:gormcallback。gotypeCallbackstruct{loggerloggercreates〔〕func(scopeScope)updates〔〕func(scopeScope)deletes〔〕func(scopeScope)queries〔〕func(scopeScope)rowQueries〔〕func(scopeScope)processors〔〕CallbackProcessor}file:gormcallbackquery。goDefinecallbacksforqueryingfuncinit(){DefaultCallback。Query()。Register(gorm:query,queryCallback)DefaultCallback。Query()。Register(gorm:preload,preloadCallback)DefaultCallback。Query()。Register(gorm:afterquery,afterQueryCallback)}queryCallbackusedtoquerydatafromdatabasefuncqueryCallback(scopeScope){。。。scope。prepareQuerySQL()。。。}
  跟踪代码到scope。go文件,函数TableName()是获取数据库表名的地方。它按如下顺序来确定表名:scope。Search。tableName查询条件中设置了表名,则直接使用scope。Value。(tabler)值对象实现了tabler接口(方法TableName()string),则从调用方法获取scope。Value。(dbTabler)值对象实现了dbTabler接口(方法TableName(DB)string),则从调用方法获取若以上条件都不成立,则从scope。GetModelStruct()中获取对象的结构体信息,从结构体名生成表名
  具体可见scope。go源码file:gormscope。gofunc(scopeScope)prepareQuerySQL(){ifscope。Search。raw{scope。Raw(scope。CombinedConditionSql())}else{scope。Raw(fmt。Sprintf(SELECTvFROMvv,scope。selectSQL(),scope。QuotedTableName(),scope。CombinedConditionSql()))}return}QuotedTableNamereturnquotedtablenamefunc(scopeScope)QuotedTableName()(namestring){ifscope。Search!nillen(scope。Search。tableName)0{ifstrings。Contains(scope。Search。tableName,){returnscope。Search。tableName}returnscope。Quote(scope。Search。tableName)}returnscope。Quote(scope。TableName())}TableNamereturntablenamefunc(scopeScope)TableName()string{ifscope。Search!nillen(scope。Search。tableName)0{returnscope。Search。tableName}iftabler,ok:scope。Value。(tabler);ok{returntabler。TableName()}iftabler,ok:scope。Value。(dbTabler);ok{returntabler。TableName(scope。db)}returnscope。GetModelStruct()。TableName(scope。db。Model(scope。Value))}
  对比以上条件,示例中的Product结构体定义了方法TableName()string,符合条件2,那么db。First(product,1)使用的表名就是haxproducts。db。Find()代码分析
  Find()代码如下,与First()同样是使用了callbacks。queries回调方法,不同点在于设置了newScope。Search。Limit(1)只返回一个结果、增加了按id排序。Findfindrecordsthatmatchgivenconditionsfunc(sDB)Find(outinterface{},where。。。interface{})DB{returns。NewScope(out)。inlineCondition(where。。。)。callCallbacks(s。parent。callbacks。queries)。db}
  在debug模式下跟踪代码到scope。TableName()中时,两次查询的区别显示出来了:它们的结果值类型不同。db。First(product,1)的值类型为结构体的指针Product,而db。Find(products)的值类型是数组的指针〔〕Product,从而导致db。Find(products)进入条件scope。GetModelStruct()。TableName(scope。db。Model(scope。Value))},需要靠分析struct结构体来生成表名。file:gormmodelstruct。goTableNamereturnsmodelstablenamefunc(sModelStruct)TableName(dbDB)string{s。l。Lock()defers。l。Unlock()ifs。defaultTableNamedb!nils。ModelType!nil{Setdefaulttablenameiftabler,ok:reflect。New(s。ModelType)。Interface()。(tabler);ok{s。defaultTableNametabler。TableName()}else{tableName:ToTableName(s。ModelType。Name())db。parent。RLock()ifdbnil(db。parent!nil!db。parent。singularTable){tableNameinflection。Plural(tableName)}db。parent。RUnlock()s。defaultTableNametableName}}returnDefaultTableNameHandler(db,s。defaultTableName)}
  默认表名s。defaultTableName为空值时先进行求值,reflect。New(s。ModelType)。Interface()。(tabler)先判断是否实现了tabler接口,有则调用其TableName()取值;否则的话从结构体的名字来生成表名。结果返回之前再调用DefaultTableNameHandler(db,s。defaultTableName)方法。
  这个ModelStruct的TableName方法与scope。TableName()中的逻辑两个不一致的地方:scope。TableName()会判断是否实现tabler与dbTabler两个接口,而这里只判断了tablerscope。TableName()是将tableName结果直接返回的,而这里多调用了DefaultTableNameHandler()。
  因为逻辑scope。TableName()的存在,当重写DefaultTableNameHandler()方法时,就会出现表前缀再次被添加了表名前。问题2
  DefaultTableNameHandler()在多数据库时出现混乱
  通过以上代码的分析,于是发现了另一个坑:当一个程序中使用两个不同的数据库时,重写方法DefaultTableNameHandler()会影响到两个数据库中的表名。其中一个数据库需要设置表前缀时,访问另一个数据库的表也可能会被加上前缀。因为是包级别的方法,整个代码里只能设置一次值。file:gormmodelstruct。goDefaultTableNameHandlerdefaulttablenamehandlervarDefaultTableNameHandlerfunc(dbDB,defaultTableNamestring)string{returndefaultTableName}总结当给结构体实现了TableName()方法时,就不要设置DefaultTableNameHandler了。保持所有Model的表名生成方式一致,要么全部使用自动生成的表名,要么全部实现tabler接口(实现TableName()方法)当需要使用多个数据库时,要避免设置DefaultTableNameHandler强烈建议:所有Model结构体全部实现tabler接口欢迎关注:程序员财富自由之路
  在这里插入图片描述参考资料http:blog。vikazhou。com20200209GORMProblemAnalyze

2021年中国网络文学出海研究报告网络文学丨研究报告核心摘要:发展概况:国内网络文学产业产能溢出,企业向外寻求发展,目前中国网络文学出海传播已经在东南亚地区与欧美建立影响力,中国网络文学出海范围覆盖……自营外卖平台小程序餐饮外卖,未来将成为餐饮业的生态标配现在做餐饮的,不管是饭店还是餐饮外卖,基本都上了第三方外卖平台,然而有很多商家表示,平时看似外卖订单挺多,其实并不赚钱,并且到店的客户少之又少。听餐饮老板的心声:现……艾瑞全程助力快手科技成功登陆香港联合交易所2021年2月5日,快手科技(1024。HK)正式在香港联交所挂牌上市交易,早盘即以338港元高开,总市值超1。3万亿港元,跻身港股市值排行前列。艾瑞咨询作为快手科技行业顾问全……LK分享集中式自动驾驶控制域中的高精地图数据解析算法下一代自动驾驶架构设计中已经倾向于以软件定义汽车的方式集合,主要涉及中央集中处理单元的方式会存在较大的改变。这一改变主要体现在外围传感单元将不再有单独的分布式处理单元进行数据前……个性化地图配置自定义POI,让你的地图一目了然地图上密密麻麻的POI信息,找起来是不是很烦躁?千篇一律的POI文字和图标,看起来是不是很呆板?在原有的个性化地图1。0版本中,箩筐基于地图矢量数据,可对点、线、面……LK分享智能网联时代下,贾跃亭的法拉第未来有未来吗?在乐视网退市后的一年的时间里,法拉第未来FF在大洋彼岸上市成功,是这两天几乎所有媒体关注的焦点所在。贾布斯重新回归聚光灯下,让这位逐渐从我们视线前隐退的网红,再度聚焦了全球关注……名企头条魅族为清明节不当文案道歉字节跳动回应印冻结其账魅族为清明节不当文案道歉,官博暂停运营两天魅族科技发布公告,对清明节推文严重用词不当进行道歉,并表示将更加严谨地把控平台内容输出,今日起官方微博暂停运营两天。魅族科技昨日……化州低调富豪掌舵佛山多元化大型集团,身价64亿化州神秘富豪:掌舵佛山多元化大型集团,身价64亿推荐语:化州市,广东省茂名市代管的县级市,古称石龙,因盛产化橘红而被命名为中国化橘红之乡,除此之外,化州物产丰富,还是中国……聚焦AI,TSINGSEE青犀视频新品EasyCVR智能边缘随着5G、AI、边缘计算、物联网(IoT)、云计算等技术的快速发展,万物互联已经从概念逐渐转变为现实,全新的行业生态AIoT正在开启新时代。巨大的市场潜力与AI等新兴技术不断融……朋友圈文案微信置顶文案1、今天你快乐了吗。2、什么都明白的人最温柔也最冷漠。3、别急着失望总有人跨越山海为你而来4、好好生活慢慢相遇5、别让平凡的生活耗尽你对生活的向往。……EOSR5系统的顶级机型佳能相机较高水准的COMS传感器面向专业人士的佳能EOSR5与佳能EOSR6同时发布,是佳能无反光镜EOSR系统中当前的顶级机型。作为佳能的第二代机型,EOSR5在功能上实现了飞跃。除了跃迁至稳定的45……华硕TUFGAMINGAX3000刺客信条定制版火热开售TUFGAMINGAX3000电竞特工路由,自亮相市场后在业内一石激起千层浪,其卓越的性能超级的稳定性,颠覆了大家对传统路由器产品的认知。9月15日,华硕电竞特工TUFA……
壹周优看点(第二期)新闻回顾银保监会发布2021年二季度银行业保险业主要监管指标数据2021年二季度末,保险公司总资产24万亿元,较年初增加16439亿元,较年初增长7。4。其中,产险……怎样快速套苹果袋,苹果套袋果把口紧好还是松好?感谢邀请今天我家第一天开始套袋,我家每年套袋都要在十万以上,基本都是我们自家人套袋,偶尔亲戚朋友来帮帮忙,不用雇人。我来谈谈对苹果套袋的一点看法。怎样能够快速的套苹果袋。……Meta透露时间表混合现实技术几年后就能实现Meta作为全球元宇宙的领头羊,其一举一动都受到业内的高度关注。据路透,Facebook母公司Meta在周四的在线会议中告诉广告客户,其混合现实技术将在几年后实现。……1分钟搞懂什么是JS代理对象(proxies)今天我们将尝试下花1分钟的时间简单地了解下什么是JS代理对象(proxies)?我们可以这样理解,JS代理就相当于在对象的外层加了一层拦截,在拦截方法里我们可以自定义一些个性化……当前的形势是否对农民利好?农业会不会走以国家农场为主导的路子这是一些不懂农业的人的美好想象!只能是死路一条。因为,从根本上违背农业规律!过去苏联搞过集体农庄失败了,我们的人民公社失败了,当然还有坚持这条路的国家,依然靠外国供养!……iPhone13多家供应商限电停产,你的十三香还能按时出货吗每经编辑:杜宇iPhone13最近火了!9月17日晚8时,苹果新款iPhone13开售,用户们直接把官网买崩了!iPhone13首批售罄连夜补货,多个话题冲上了热搜。……联想IoT秋季发布会9月26日召开新品会是投影仪吗?近日,宅秘了解到,智慧联想官微宣布开启联想消费生态IoT秋季新品发布会直播预约。据悉,此次发布会将于9月26日19:30开始。除了联想中国区消费IoT事业部的总经理冯蔚东以及产……5款最新旗舰手机温度横评华为令人意外,iQOO发热最高最近发布了很多安卓旗舰手机,这些手机的体验究竟如何,其实不能只看处理器、屏幕、相机,还要看机身发热,根据小白测评公布的最新温度测试,华为P50Pro温度表现令人意外,是5款新旗……标准续航的汉EV与低配毛豆3,到底该选谁?2021年被誉为碳中和元年,环保理念开始走进万千家庭。消费者在选购家用轿车时也优先将新能源汽车纳入考虑的范围。但说到今年关注度最高的新能源车型,不得不提及的就是比亚迪汉EV与特……保价寄快递,货品损伤被拒赔!德邦还称揽件司机酒驾已调岗,需进N海都全媒体记者唐明亮快递员在揽货时,确认货品是好的才收的,现场也拍了照,到现在却不承认。近日,福州的王女士遭遇一件糟心事。原来,前段时间,她委托德邦快递回寄2个马……全球智能手机销量榜出炉,小米超越苹果,小米汽车也有新进展美方禁止台积电为华为代工芯片后,华为想了很多办法,希望能够绕开美方的禁令来解决此次的芯片危机。但是美方再一次修改规则,禁止华为及其子公司成为含有美国技术芯片的最终收货人以及使用……鲁大师公布8月新机流畅榜,搭载小米MIUI12。5的MIX4在人们使用智能手机的过程中,最影响心情的大概就是卡顿、掉帧、不流畅了吧,尤其是玩游戏的时候,遇上手机卡顿分分钟能气的人原地暴走。而决定手机流畅度的主要有两个因素,一个是处理器,……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网