这是我参与「第五届青训营 」笔记创作活动的第 5 天
一、本堂课重点内容
- 今天把掘金《后端入门 - Go 语言原理与实践》的课全部看完了
- 主要对Gorm框架这块再进行扩充和记录
二、详细知识点介绍
Gorm设计原理
SQL 生成
为什么这么做?
- 可以自定义Clause Builder
- 方便扩展 Clause
- 自由选择 Clauses
怎么做
首先,我们先了解一下SQL语句的结构:
SELECT `name`, `age` -- SELECT Clause
FROM `user` -- FROM Clause
Where expression -- WHERE Clause
age > 18 AND
name like 李%
ORDER BY age DESC -- ORDER BY Clause
LIMIT 100 OFFSET 0 -- LIMIT Clause
FOR UPDATE -- FOR Clause
一条较为复杂的sql语句由以上这些成分构成,而在Gorm中如何生成并执行这样的sql呢
以上面这条sql为例,在gorm中我们可以这样实现这条sql的构造 : db.Where("age > ?", 18).Where("name like ?", "李%").Limit(100).Order("age desc").Find(&user)
前面的链式结构函数构成了GORM中的Clauses,同时也与sql中的各个Clause块对应,而最后一条Find()函数则决定了返回的类型以及执行的方法
而GORM是如何通过这些chinable_api将Clauses添加至GORM Statement中的
这里以Where为例,看一下gorm的源码
// Where add conditions
func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {
// 获取GORM Statement实例
tx = db.getInstance()
// 为Statement添加Clauses
if conds := tx.Statement.BuildCondition(query, args...); len(conds) > 0 {
tx.Statement.AddClause(clause.Where{Exprs: conds})
}
return
}
而对于终结这些语句,Gorm通过GORM FInisher 放过发来执行GORM Statement
同样这里以Find为例,通过gorm的源码了解一下
// Find finds all records matching given conditions conds
func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB) {
tx = db.getInstance()
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)
}
接着,GORM Callbacks通过解析GORM Statement 执行对应的sql语句,来获取数据库反馈的结果
func (p*processor) Excecute(db *DB) *DB {
stmt := db.Statement
stmt.BuildClauses = []string{"SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT"........}
// callbacks/query.go
stmt.Build(stmt.BuildClauses...)
stmt.ConnPool.Exec(stmt.SQL.String(),stmt.Vars)
}
自定义Builder
由于不同数据库,甚至是不同版本的数据库支持的SQL不同,我们有时候需要自定义Builder来解决问题
比如 在MySQL < 8 或 MariaDB 中 : SELECT * FROM 'user' LOCK IN SHARE MODE
和 MySQL 8 版本中: SELECT * FROM 'user' FOR SHARE OF 'user'
而这两条SQL所表达的意思和执行结果也是相同的
扩展子句
我们通过"gorm.io/hints"包可以很轻松的在原有sql的基础下,扩展出更复杂的sql
通过下面这些简单的李子便能够轻松理解
插件
而我们自己编写的这些插件是如何在gorm中工作的呢?
GORM生成并执行SQL分为Finisher Method、决定Statement类型、执行Callbacks、生成SQL并执行这几个过程,
而我们编写的插件系统就在后三个过程中起到作用,通过我们编写的插件,我们可以自定义的调整使用基础的Create等函数
通过这些插件,我们可以做到灵活定制并且自由扩展我们的应用,做到多租户,多数据库、读写分离,加解密、混沌工程等作用
连接池
数据库连接池CoonPool是什么?
有了解过JavaWeb开发的同学可能会对Druid或是HikariCP等数据库连接池有所了解。
数据库连接池是一个含有多个数据库连接的抽象区域,他负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
为什么要使用连接池
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等
四、个人总结
掘金的最后一堂课中所讲到的实战项目目前理解起来还有一点困难,主要是关于RPC框架这部分的使用 个人以前学Java的时候,也学习使用过Spring Cloud、Euraka的服务注册、服务发现、服务熔断等机制,当时没有趁手的项目拿去实战训练,也有点生疏了,现在对上Golang这一门没怎么学习过的全新语言学起来还是有点压力
之后继续加油!