当前位置: 首页 > news >正文

网站建设背景怎么写海外推广解决方案

网站建设背景怎么写,海外推广解决方案,怎么创建网站 免费的,海外贸易平台有哪些Go语言技巧 - GORM实战剖析基本用法和原理解析 - 转载前言GORM库的官方文档Talk is Cheap. Show me the code.创建查询更新删除原生SQL使用GORM的核心思路梳理一个对象 一行数据选择生效字段 核心结构体 字段数组缩短链式调用聚焦微服务的场景避免引入非原生MySQL的特性从查…

Go语言技巧 - GORM实战剖析基本用法和原理解析 - 转载

  • 前言
  • GORM库的官方文档
  • Talk is Cheap. Show me the code.
    • 创建
    • 查询
    • 更新
    • 删除
    • 原生SQL
  • 使用GORM的核心思路梳理
    • 一个对象 = 一行数据
    • 选择生效字段 = 核心结构体 + 字段数组
    • 缩短链式调用
    • 聚焦微服务的场景
    • 避免引入非原生MySQL的特性
  • 从查询接口了解GORM的核心实现
    • 两个核心文件
      • 1. Find的主要代码
      • 2. tx.callbacks.Query()的实现
      • 3. Execute的执行逻辑
      • 4. Callback的注册
      • 5. Query函数的实现
      • 6.核心-构建SQL的实现
  • 小结

前言

本文转自b站up Junedayday

Go语言技巧 - 7.【GORM实战剖析】基本用法和原理解析

Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili: https://space.bilibili.com/293775192
公众号: golangcoding

GORM库的官方文档

GORM库作为Go语言最受欢迎的ORM框架,提供了非常丰富的功能,大家可以通过阅读中文官网了解详情。

这里,先着重介绍一个背景:GORM内部会区分v1与v2两个版本,其中

  • v1的包导入路径为 github.com/jinzhu/gorm
  • v2的包导入路径为 gorm.io/gorm

v1与v2对使用者来说体验相差不大,今天就主要针对v2版本进行讲解。

Talk is Cheap. Show me the code.

接下来,我先给出一套个人比较推荐的CRUD代码。

创建

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
// 直接创建
result := db.Create(&user)
// 指定字段创建
db.Select("Name", "Age", "CreatedAt").Create(&user)// 批量创建
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)

推荐:

  • 通常:直接用结构体或结构体的切片进行创建;
  • 特殊:加上指定的字段,也就是其余字段不生效,如上面的Birthday。

查询

// 查询所有对象
var users []User  
result := db.Find(&users)// 指定查询条件(where) 这里的name和Age:指定查询传入结构体中的哪些字段
db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)// 限制返回数量
db.Limit(10).Offset(5).Find(&users)// 查询部分字段(即从select * 改造为 select name, age)
db.Select("name", "age").Find(&users)// 其余扩展
db.Order("age desc, name").Find(&users)

推荐:

  • 普通场景:简单查询用Find+Where的函数结合实现,结合Limit+Offset+Order实现分页等高频功能;
  • 追求性能:可以引入Select避免查询所有字段,但会导致返回结果部分字段不存在的奇怪现象,需要权衡;
  • 复杂查询:例如Join+子查询等,推荐使用下面的原生SQL,用GORM拼接的体验并不好。

更新

// 更新通常包含两块,一个是要更新的字段Select+Updates,另一个是被更新数据的条件Where
db.Model(&user).Where(&User{Name: "jinzhu"}, "name", "Age").Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})

零值问题:参考https://gorm.io/zh_CN/docs/update.html#%E6%9B%B4%E6%96%B0%E5%A4%9A%E5%88%97 下的注释

推荐:

  • 普通场景:利用Select+Updates指定更新字段,利用Where指定更新条件;
  • 特殊场景:复杂SQL用原生SQL。

删除

// 删除条件不建议太复杂,所以可以用简单的Where条件来拼接
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})

推荐:

  • 普通场景:利用Where限定删除条件,不建议太复杂;
  • 软删除:在实际项目中,不太建议用硬删除的方式,而是用软删除,即更新一个标记字段。

原生SQL

// 原生SQL,推荐在复杂sql场景下使用
db.Raw("SELECT id, name, age FROM users WHERE name = ?", 3).Scan(&result)

使用GORM的核心思路梳理

一个对象 = 一行数据

示例中的一个User对象,完整地对应到具体users表中的一行数据,让整个框架更加清晰明了。每当数据库增加了一列,就对应地在结构体中加一个字段。这里有两个注意点:

  1. 不要在核心结构体User中加入非表中的数据,如一些计算的中间值,引起二义性;
  2. gorm.Model可以提升编码效率(会减少重复编码),但会限制数据库表中字段的定义,慎用(个人更希望它能开放成一个接口);

选择生效字段 = 核心结构体 + 字段数组

在 查询 和 更新 接口里,我推荐的使用方法是采用核心结构体User+一个fields的数组,前者保存具体的数据、也实现了结构体复用,后者则选择生效的字段。

这种风格代码和Google推荐的API风格非常像,可读性很棒。

  • 这里还遗留了一个问题,就是fields数组里的字符串必须手输,可以考虑结合go generate自动生成这些fields的字符串常量,减少出错的概率。

缩短链式调用

GORM的主要风格是链式调用,类似于Builder设计模式、串联堆起一个SQL语句。这种调用方式扩展性很强,但会带来了一个很严重的问题:容易写出一个超长的链式调用,可维护成本大幅度提高。

所以,在我的推荐使用方式里,区分了两种场景:

  1. 简单场景 - 核心结构体 + 字段数组
  2. 复杂场景 - 原生SQL

聚焦微服务的场景

作为一个ORM工具,GORM要考虑兼容各种SQL语句,内部非常庞大的。但如今更多地是考虑微服务的场景,这就能抛开大量的历史包袱,实现得更加简洁。这里我简单列举三个不太推荐使用的SQL特性:

  1. 减少group by - 考虑将聚合字段再单独放在一个表中
  2. 抛弃join - 多表关联采用多次查询(先查A表,然后用In语句去B表查)、或做一定的字段冗余(即同时放在A、B两个表里)
  3. 抛弃子查询,将相关逻辑放在代码里

当然,真实业务研发过程中无法完全避免复杂SQL,我们只能有意识地减少引入复杂度。

避免引入非原生MySQL的特性

GORM除了常规的SQL功能,还提供了一些高级特性、模型关联、钩子等,非常炫酷。

但我不推荐大家在实际项目中使用这些特性。只有尽可能地保证这个框架简洁,才能保证代码后续的可维护性。

熟悉MySQL历史的朋友都知道,存储过程在以前相当一段时间都是很好的工具,但如今都倡导去存储过程。GORM的这些特性和存储过程有异曲同工之处:一个将业务逻辑放在了数据库,另一个则放到了ORM框架里,会导致后续的迁移成本变高。(这也是我不推荐使用 gorm.Model的重要原因。)

从查询接口了解GORM的核心实现

两个核心文件

在GORM库中,有两个核心的文件,也是我们调用频率最高的函数所在:chainable_api.go和 finisher_api.go。顾名思义,前者是整个链式调用的中间部分,后者则是最终获取结果的函数。以查询为例:

db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)

其中Where是chainable,也就是还在拼接SQL条件,Find则是触发真正查询的finisher。

如果一开始过于关注chainable调用,很容易陷入构造SQL的细节,所以这块代码建议从finisher入手,深入看看一个SQL的到底是怎么在GORM中拼接并执行的。

1. Find的主要代码

func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {tx = db.getInstance()// conds是查询的条件,这里忽略,我们默认已经在前面的Chainable中完成了所有参数的拼接if len(conds) > 0 {if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...); len(exprs) > 0 {tx.Statement.AddClause(clause.Where{Exprs: exprs})}}tx.Statement.Dest = dest// 关键的执行逻辑return tx.callbacks.Query().Execute(tx)
}

2. tx.callbacks.Query()的实现

func (cs *callbacks) Query() *processor {// Query 是从processors的 map 中取出 queryreturn cs.processors["query"]
}// 这个对应的processor是 gorm.DB,也就是执行DB的Execute
func initializeCallbacks(db *DB) *callbacks {return &callbacks{processors: map[string]*processor{"create": {db: db},"query":  {db: db},"update": {db: db},"delete": {db: db},"row":    {db: db},"raw":    {db: db},},}
}

3. Execute的执行逻辑

抛开一些周边逻辑,我们聚焦于下面的核心逻辑:

func (p *processor) Execute(db *DB) *DB {// processor中注册了多个函数,按顺序执行。// 核心的查询逻辑也在这里面for _, f := range p.fns {f(db)}return db
}

而fns又是来自callbacks

func (p *processor) compile() (err error) {// 对 callbacks 会做排序if p.fns, err = sortCallbacks(p.callbacks); err != nil {p.db.Logger.Error(context.Background(), "Got error when compile callbacks, got %v", err)}return
}

4. Callback的注册

func RegisterDefaultCallbacks(db *gorm.DB, config *Config) {// 默认注册了create/query/delete/update/raw 五种 callback 大类,这里以query为例queryCallback := db.Callback().Query()queryCallback.Register("gorm:query", Query)queryCallback.Register("gorm:preload", Preload)queryCallback.Register("gorm:after_query", AfterQuery)if len(config.QueryClauses) == 0 {config.QueryClauses = queryClauses}queryCallback.Clauses = config.QueryClauses
}

5. Query函数的实现

func Query(db *gorm.DB) {if db.Error == nil {// 构建查询的 SQL 语句BuildQuerySQL(db)// 查询数据if !db.DryRun && db.Error == nil {rows, err := db.Statement.ConnPool.QueryContext(db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...)if err != nil {db.AddError(err)return}defer rows.Close()// 将结果输出到目标结构体中gorm.Scan(rows, db, false)}}
}

6.核心-构建SQL的实现

func BuildQuerySQL(db *gorm.DB) {// SQL为空,表示需要自己构建if db.Statement.SQL.String() == "" {db.Statement.SQL.Grow(100) // 分配初始空间if len(db.Statement.Selects) > 0 { // 表示只select某几个字段,而不是select *} else if db.Statement.Schema != nil && len(db.Statement.Omits) > 0 {// Omit表示忽略特定字段} else if db.Statement.Schema != nil && db.Statement.ReflectValue.IsValid() {// 查询到指定结构体}// 对join的处理,涉及到多表关联,暂时忽略if len(db.Statement.Joins) != 0 {} else {db.Statement.AddClauseIfNotExists(clause.From{})}// 用一个map去重,符合名字中的 IfNotExists 含义db.Statement.AddClauseIfNotExists(clauseSelect)// 最后拼接出完整 SQL 的地方db.Statement.Build(db.Statement.BuildClauses...)}
}

小结

本文旨在介绍GORM的推荐使用方式,并简单阅读对接数据库的相关代码。这里分享我的四个观点:

  1. Builder设计模式 - 在面对复杂场景中,Builder设计模式扩展性很好,可分为两个阶段:存储数据+处理数据;GORM的调用就是采用了chainable+finisher的两段实现,前者保存SQL相关元数据,后者拼接SQL并执行;

  2. 负重前行 - GORM是一个负重前行的框架:它不仅支持了所有原生SQL的特性,也增加了很多类似Hook的高级特性,导致这个框架非常庞大。如果团队没有历史包袱,更推荐节制地使用GORM特性,适当封装一层;

  3. interface{}问题 - GORM中许多函数入参的数据类型都是interface{},底层又用reflect支持了多种类型,这种实现会导致两个问题:
    a. reflect导致的底层的性能不高(这点还能接受)
    b. interface{}如果传入了不支持的复杂数据类型时,排查问题麻烦,往往要运行程序时才会报错

  4. 高频拼接重复SQL - 在一个程序运行过程中,执行的SQL语句都比较固定,而变化的往往是参数;从GORM的实现来看,每次执行都需要重新拼接一次SQL语句,是有不小的优化空间的,比如引入一定的cache。

http://www.lbrq.cn/news/2566783.html

相关文章:

  • 服务器如何做网站南宁市优化网站公司
  • iis怎么查看网站的域名巨量算数数据分析入口
  • wordpress防镜像seo关键词优化要多少钱
  • seo站内站怎么做手机百度网页版
  • 如何建设国际网站首页数据分析师报考官网
  • 徐州做网站的哪个好在线外链工具
  • 网站如何关闭2019网站seo
  • 沈阳男科医院哪家医院好seo软件优化
  • wordpress 代做网站拓客平台有哪些
  • 中国建设银行网站 公司机构客户佛山网站建设公司
  • 网站集约化平台建设分析专业网站优化
  • 12380网站建设情况汇报深圳网络推广引流
  • 建设一个旅游网站毕业设计百度店铺怎么入驻
  • wordpress 页面开发教程马鞍山网站seo
  • 河南代做网站semikron
  • 厦门建设与管理局网站优化系统的软件
  • 狗和人做愛网站谁知道好推建站
  • 北京建站公司哪个好百度识图在线使用一下
  • 网站的构造西安今天刚刚发生的新闻
  • 如何做企业网站及费用问题网站优化推广哪家好
  • 外贸网站个性设计百度seo引流怎么做
  • 泰州住房城乡建设网站怎样注册网站
  • 网站建设自己在家接单上海网站建设推广服务
  • 南京做网站哪家公司最好网络推广方法大全
  • 门户网站开发需求分析浙江网站推广公司
  • 企业网站颜色如何免费创建自己的网站平台
  • 个人网站要怎么做特色产品推广方案
  • 什么是网站抄袭免费建网站
  • 微网站栏目设置游戏交易平台
  • 张家口建设部网站2023年10月爆发新冠
  • InfluxDB 与 Node.js 框架:Express 集成方案(二)
  • 基于深度学习的医学图像分析:使用MobileNet实现医学图像分类
  • 除数博弈(动态规划)
  • C# 中抽象类、密封类、静态类和接口的区别
  • 【C++】第二十一节—一文详解 | 红黑树实现(规则+效率+结构+插入+查找+验证)
  • 无公网IP设置外网可访问本地瑞友天翼应用虚拟化系统