gorm源码解析(一):ORM概览
文章目录
- 什么是ORM
- 为什么需要ORM框架
- ORM框架的要素
- 方言
- 尽可能提高性能
- 总结
什么是ORM
ORM(Object-Relational Mapping)是对象关系映射的框架
开发者可以使用熟悉的面向对象方式来操作数据库,而无需直接编写繁琐的SQL语句
具体来说就是ORM框架根据元数据完成以下操作:
- 执行增删改时:完成从对象到sql的转换。例如当用户
create
一个对象时,ORM框架需要根据该对象,生成一条INSERT的sql - 解析查询结果时:完成从结果到对象的转换。当从mysql server接收到查询结果时,ORM框架需要将其转换为业务model对象
为什么需要ORM框架
试想如果没有ORM框架,用标准库sql.DB
操作数据库:
假设有一张简单的user表,有3个字段,id
,name
,number
CREATE TABLE `user` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(100) NOT NULL DEFAULT '',`number` int NOT NULL DEFAULT '0',PRIMARY KEY (`id`)
) ENGINE=InnoDB 、 DEFAULT CHARSET=utf8mb4
表里有一行数据:
1 | tom | 12 |
---|
要用go原生的sql包查一行记录有以下流程:
- 注册db驱动:如果用mysql,匿名
import github.com/go-sql-driver/mysql
即可- 该包的init方法会完成mysql驱动的注册
- 创建sql.DB实例
- 执行sql:调db.QueryContext执行查询操作
- 读取查询结果:调rows.Next和rows.Scan,将结果设置操传给rows.Scan的指针中
import ("context""database/sql""fmt"// 注册mysql驱动_ "github.com/go-sql-driver/mysql""testing"
)func TestDB(t *testing.T) {// 初始化sql.DBdb, err := sql.Open("mysql", "账号:密码@(连接地址)/数据库名称")if err != nil {panic(err)}ctx := context.Background()// 执行查询rows, err := db.QueryContext(ctx, "select * from user where id = ?", 1)id := 0name := ""number := 0// 接收查询结果for rows.Next() {rows.Scan(&id, &name, &number)}// 输出:1 tom 12fmt.Println(id, name, number)
}
以上的代码有如下问题:
- 需要手写sql,容易出错
- 例如容易写错字段名 ,写错关键字。需要真正执行sql时才能发现,go编译器无法发现
- 难以重构
- 如果要改字段名,需要找到所有出现老字段字段名的地方,一个个改
- 重复代码很多,样板代码满天飞,一部分精力花在业务无关的地方
- 例如:手动处理结果集时,每次接收查询结果时都要在for循环里先调
row.Next
,再调row.Scan
- 例如:手动处理结果集时,每次接收查询结果时都要在for循环里先调
- 开发效率低:需要自己花时间拼接sql,处理返回结果
而ORM框架可以帮我们解决以上问题
我们看看如果用gorm实现上述的功能应该怎么写:
定义model结构体:
type User struct { Id int64 Name string Number int64
} // 自定义表名
func (*User) TableName() string { return "user"
}
执行查询:
// 初始化grom.DB
db, err := gorm.Open(mysql.Open("账号:密码@(连接地址)/数据库名称"))
if err != nil { panic(err)
} // 执行查询
var users []User
err = db.Where(User{ Id: 1,
}).Find(&users).Error
gorm会将Where方法传入的结构体中,不为零值的字段都解析成等值条件查询,然后将查询结果解析到users变量里,可以说极大提升开发效率
当然用ORM也不全是优点。就性能来说,肯定是直接写sql性能最好
毕竟orm框架执行时,最终还是转化为sql,但中间多经过了对象到sql的转化,性能有损失
但在工程上,性能有时候不是第一优先考虑,而是更关注工程效率:
- 交付效率
- 后期可维护性
这两点是ORM框架的优势
ORM框架的要素
ORM框架还需要考虑以下要素:
方言
sql有一套标准的规范,但有些db的某些sql功能并没有遵守sql规范,或者说不同db的实现有差别,例如:
- 引号不同:
- mysql是 `
- portgres是
"
- upsert语法不一样,
- mysql是
INSERT ... ON DUPLICATE KEY UPDATE
- postgres是
INSERT ON CONFLICT
- mysql是
因此ORM框架为了支持多种db,需要抽象出方言dialector
,每个db不同的实现需要调方言完成
尽可能提高性能
ORM框架需要提供较好的性能,例如:
- 元数据缓存:不是每次执行都要重新从model解析一遍元数据,而是只用解析一次,将解析的结果缓存下来
- 预编译缓存:一般使用预编译时的流程为;prepare一个stmt,执行sql,然后关闭stmt。但可以在ORM层面提供stmt的缓存,下次遇到相同的sql模板时,直接用预编译好的stmt
总结
本文介绍了什么是ORM框架,以及为什么需要ORM。接下来的文章将进入gorm框架原理和源码解析环节