GORM 预加载和自引用

GORM 预加载和自引用

预加载

从传统的外键约束说起

在传统的数据库设计中,如果我们想创建一个主表和从表,得在创建从表语句的时候添加外键约束(一般在创建的时候添加)。

[CONSTRAINT] [外键约束名称] FOREIGN KEY(外键字段名) REFERENCES 主表名(主键字段名)

比如现在有一个部门表和一个员工表,他们之间是一对多关系,部门表是主表,员工表是从表。那么在创建员工表的时候,我们得为它添加上外键约束:

# 先建立主表:department表
create table department(
	id int primary key auto_increment,
	dep_name varchar(20),
	dep_location varchar(20)
);

#  创建从表 employee 并添加外键约束 emp_depid_fk
create table employee(
	id int primary key auto_increment,
	name varchar(20),
	age int,
	dep_id int, -- 外键对应主表的主键
	-- 创建外键约束
	constraint emp_depid_fk foreign key (dep_id) references department(id)
);

但是这样做存在一些问题,因为数据表的设计一般是在项目开发的起始阶段,我们不太可能一开始就把数据表设计的很完美,后期可能会对数据表之间的关系做一些调整,而添加外键约束无疑是提前把数据表之间的关系给写死了(如果没有添加级联操作的话),耦合度比较低。

ORM数据库

ORM数据库就考虑到了这一点,我们以GORM框架举例。在创建数据表的时候,可以不用对数据表添加任何约束,而是把这项任务延后到了创建models的时候,降低了耦合度,后期可以直接修改代码而不用去修改数据库。

我们来举一个例子,假如现在要创建三张数据表,学生表与成绩表是多对一,成绩表与课程表之间是一对多:

USE test01;
CREATE TABLE student(
	sno VARCHAR(20),
	NAME VARCHAR(20),
	age INT
);
CREATE TABLE grade (
	sno VARCHAR(20),
	grade INT,
	cno VARCHAR(20)
);
CREATE TABLE course (
	cno VARCHAR(20),
	cname VARCHAR(20)
);

INSERT INTO student VALUES("01", "张三", 19);
INSERT INTO student VALUES("02", "李四", 20);
INSERT INTO student VALUES("02", "王五", 21);

INSERT INTO grade VALUES("01", 90, "0001");
INSERT INTO grade VALUES("01", 80, "0002");
INSERT INTO grade VALUES("01", 30, "0003");
INSERT INTO grade VALUES("02", 80, "0001");
INSERT INTO grade VALUES("02", 50, "0002");
INSERT INTO grade VALUES("02", 60, "0003");

INSERT INTO course VALUES("0001", "数学");
INSERT INTO course VALUES("0002", "语文");
INSERT INTO course VALUES("0003", "英语");

在这里插入图片描述

我们在创建数据表的时候没有指定任何约束,而是把这项工作延后到了创建models的时候:

type student struct {
	Sno string `gorm:"primary_key"`
	Name string
	Age int
	Grades []grade `gorm:"foreignKey:Sno;association_foreignkey:Sno"`
}
func (student) TableName() string {
	return "student"
}

type grade struct {
	Sno string
	Grade int
	Cno string
}
func (grade) TableName() string {
	return "grade"
}

type course struct {
	Cno string `gorm:"primary_key"`
	Cname string
	Grades []grade `gorm:"foreignKey:Cno;association_foreignkey:Cno"`
}
func (course) TableName() string {
	return "course"
}

在主表中,我们创建了从表映射结构体的切片,然后使用结构体tag来指定外键约束的字段,使用这种方式来表达主表和从表之间的关系。

预加载

创建好models以后,我们试着使用查询语句来查询表中的信息,比如查询学生表中张三的信息:

var st student
db.Where("Name=?", "张三").Find(&st)
// {01 张三 19 []}

我们可以发现,虽然学号、姓名和年龄都被打印出来了,但是从表的信息却没有打印出来,虽然在一般情况下,从表的信息是不用打印出来的,但是我们在student结构体中声明了Grades字段,这个字段的值按道理是应该和其他字段一样被打印出来的。

这个时候就需要使用预加载了:

db.Preload("Grades").Where("Name=?", "张三").Find(&st)
// {01 张三 19 [{01 90 0001} {01 80 0002} {01 30 0003}]}

Preload中指定的是映射结构体中的切片字段名。

自定义预加载

我们还可以自定义预加载,比如我们只想显示张三的数学成绩:

db.Preload("Grades", func(db *gorm.DB) *gorm.DB {
		// 原生的sql语句, 这里的sno和cno是数据库中的字段,不是结构体中的字段
		return db.Where("sno=? and cno=?", "01", "0001")
	}).Where("Name=?", "张三").Find(&st)
// {01 张三 19 [{01 90 0001}]}

自引用

假如现在有一张员工表:

在这里插入图片描述

CREATE TABLE employee (
	id INT,
	lid INT,
	NAME VARCHAR(20)
);
INSERT INTO employee VALUES(1, 0, "领导");
INSERT INTO employee VALUES(2, 1, "员工1");
INSERT INTO employee VALUES(3, 1, "员工2");

三个字段分别是员工编号、领导编号和员工姓名,某条数据的lid可能是另一条数据的id,即一个员工可能是另一个员工的领导,一张表中的数据构成了一对多关系,这就是自引用。

在models中可以这样来写tag:

type employee struct {
	Id int
	Lid int
	Name string
	Employees []employee `gorm:"foreignkey:Lid;association_foreignkey:Id"`
}
func (employee) TableName() string {
	return "employee"
}

然后我们可以使用预加载来打印领导的信息:

var emp employee
db.Preload("Employees").Where("Name=?", "领导").Find(&emp)
fmt.Println(emp)
// {1 0 领导 [{2 1 员工1 []} {3 1 员工2 []}]}

参考资料:

GORM 中文文档

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值