以一个订单库为例
entity
package entity
import (
"gorm.io/gorm"
)
// User 结构体,对应users表
type User struct {
gorm.Model
Username string `gorm:"not null"`
Password string `gorm:"not null"`
Email string `gorm:"unique"`
FullName string `gorm:""`
Address string `gorm:""`
PhoneNumber string `gorm:""`
Orders []Order `gorm:"foreignKey:UserID"`
}
// Order 结构体,对应orders表
type Order struct {
gorm.Model
UserID uint64 `gorm:"not null"`
OrderNumber string `gorm:"not null"`
OrderDate string `gorm:"not null"`
TotalAmount float64 `gorm:"not null"`
OrderStatus string `gorm:"not null"`
PaymentMethod string `gorm:""`
ShippingAddress string `gorm:""`
}
// Product 结构体,对应products表
type Product struct {
gorm.Model
Name string `gorm:"not null"`
Description string `gorm:""`
Price float64 `gorm:"not null"`
StockQuantity int `gorm:"not null"`
}
sql
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS gorm_order;
-- 使用创建好的数据库
USE gorm_order;
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE,
full_name VARCHAR(255),
address VARCHAR(255),
phone_number VARCHAR(255),
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
deleted_at TIMESTAMP
);
-- 插入用户数据示例
INSERT INTO users (username, password, email, full_name, address, phone_number, created_at, updated_at, deleted_at)
VALUES
('user1', 'password1', 'user1@example.com', '张三', '北京市朝阳区', '13812345678', '2024-11-25 18:00:00', '2024-11-25 18:00:00', NULL),
('user2', 'password2', 'user2@example.com', '李四', '上海市浦东新区', '13987654321', '2024-11-25 18:10:00', '2024-11-25 18:10:00', NULL),
('user3', 'password3', 'user3@example.com', '王五', '广州市天河区', '13654321876', '2024-11-25 18:20:00', '2024-11-25 18:20:00', NULL);
-- 创建订单表
CREATE TABLE IF NOT EXISTS orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
order_number VARCHAR(255) NOT NULL,
order_date TIMESTAMP NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
order_status ENUM('pending', 'processing', 'completed', 'cancelled') NOT NULL,
payment_method VARCHAR(255),
shipping_address VARCHAR(255),
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
deleted_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 插入订单数据示例
INSERT INTO orders (user_id, order_number, order_date, total_amount, order_status, payment_method, shipping_address, created_at, updated_at, deleted_at)
VALUES
(1, 'ORDER001', '2024-11-25 19:00:00', 100.50, 'pending', 'credit_card', '北京市朝阳区XX小区XX栋', '2024-11-25 19:00:00', '2024-11-25 19:00:00', NULL),
(2, 'ORDER002', '2024-11-25 19:10:00', 200.00, 'processing', 'paypal', '上海市浦东新区XX大厦XX室', '2024-11-25 19:10:00', '2024-11-25 19:10:00', NULL),
(1, 'ORDER003', '2024-11-25 19:20:00', 50.25, 'completed', 'bank_transfer', '北京市朝阳区XX街道XX号', '2024-11-25 19:20:00', '2024-11-25 19:20:00', NULL);
-- 创建商品表
CREATE TABLE IF NOT EXISTS products (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description VARCHAR(255),
price DECIMAL(10, 2) NOT NULL,
stock_quantity INT NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
deleted_at TIMESTAMP
);
-- 插入商品数据示例
INSERT INTO products (name, description, price, stock_quantity, created_at, updated_at, deleted_at)
VALUES
('商品1', '这是一款很棒的商品', 50.00, 100, '2024-11-25 19:30:00', '2024-11-25 19:30:00', NULL),
('商品2', '具有特色的商品', 80.00, 50, '2024-11-25 19:40:00', '2024-11-25 19:40:00', NULL),
('商品3', '热门商品', 120.00, 30, '2024-11-25 19:50:00', '2024-11-25 19:50:00', NULL);
-- 创建订单商品关联表(多对多关系)
CREATE TABLE IF NOT EXISTS order_products (
order_id BIGINT UNSIGNED NOT NULL,
product_id BIGINT UNSIGNED NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- 插入订单商品关联数据示例
INSERT INTO order_products (order_id, product_id, quantity)
VALUES
(1, 1, 2),
(1, 2, 1),
(2, 2, 3),
(3, 3, 1);
CREATE TABLE user_order_infos (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL,
order_id BIGINT UNSIGNED NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
deleted_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (order_id) REFERENCES orders(id)
);
-- 插入user_order_infos表数据示例
INSERT INTO user_order_infos (user_id, order_id, created_at, updated_at, deleted_at)
VALUES
-- 用户user1(id为1)的订单ORDER001(id为1)关联记录
(1, 1, '2024-11-25 20:00:00', '2024-11-25 20:00:00', NULL),
-- 用户user1(id为1)的订单ORDER003(id为3)关联记录
(1, 3, '2024-11-25 20:10:00', '2024-11-25 20:10:00', NULL),
-- 用户user2(id为2)的订单ORDER002(id为2)关联记录
(2, 2, '2024-11-25 20:20:00', '2024-11-25 20:20:00', NULL);
Has Many
has many 与另一个模型建立了一对多的连接。 不同于 has one,拥有者可以有零或多个关联模型。
// User 结构体,对应users表
type User struct {
gorm.Model
Username string `gorm:"not null"`
Password string `gorm:"not null"`
Email string `gorm:"unique"`
FullName string `gorm:""`
Address string `gorm:""`
PhoneNumber string `gorm:""`
Orders []Order `gorm:"foreignKey:UserID"`
}
// Order 结构体,对应orders表
type Order struct {
gorm.Model
UserID uint64 `gorm:"not null"`
OrderNumber string `gorm:"not null"`
OrderDate string `gorm:"not null"`
TotalAmount float64 `gorm:"not null"`
OrderStatus string `gorm:"not null"`
PaymentMethod string `gorm:""`
ShippingAddress string `gorm:""`
}
以用户的订单为例,一个用户会存在多个订单,而这也就是一种一对多的关系。
数据表内容如下
mysql> select * from users;
+----+----------+-----------+-------------------+-----------+-----------------------+--------------+---------------------+---------------------+------------+
| id | username | password | email | full_name | address | phone_number | created_at | updated_at | deleted_at |
+----+----------+-----------+-------------------+-----------+-----------------------+--------------+---------------------+---------------------+------------+
| 1 | user1 | password1 | user1@example.com | 张三 | 北京市朝阳区 | 13812345678 | 2024-11-25 18:00:00 | 2024-11-25 18:00:00 | NULL |
| 2 | user2 | password2 | user2@example.com | 李四 | 上海市浦东新区 | 13987654321 | 2024-11-25 18:10:00 | 2024-11-25 18:10:00 | NULL |
| 3 | user3 | password3 | user3@example.com | 王五 | 广州市天河区 | 13654321876 | 2024-11-25 18:20:00 | 2024-11-25 18:20:00 | NULL |
+----+----------+-----------+-------------------+-----------+-----------------------+--------------+---------------------+---------------------+------------+
3 rows in set (0.00 sec)
mysql> select * from orders;
+----+---------+--------------+---------------------+--------------+--------------+----------------+------------------------------------+---------------------+---------------------+------------+
| id | user_id | order_number | order_date | total_amount | order_status | payment_method | shipping_address | created_at | updated_at | deleted_at |
+----+---------+--------------+---------------------+--------------+--------------+----------------+------------------------------------+---------------------+---------------------+------------+
| 1 | 1 | ORDER001 | 2024-11-25 19:00:00 | 100.50 | pending | credit_card | 北京市朝阳区XX小区XX栋 | 2024-11-25 19:00:00 | 2024-11-25 19:00:00 | NULL |
| 2 | 2 | ORDER002 | 2024-11-25 19:10:00 | 200.00 | processing | paypal | 上海市浦东新区XX大厦XX室 | 2024-11-25 19:10:00 | 2024-11-25 19:10:00 | NULL |
| 3 | 1 | ORDER003 | 2024-11-25 19:20:00 | 50.25 | completed | bank_transfer | 北京市朝阳区XX街道XX号 | 2024-11-25 19:20:00 | 2024-11-25 19:20:00 | NULL |
+----+---------+--------------+---------------------+--------------+--------------+----------------+------------------------------------+---------------------+---------------------+------------+
3 rows in set (0.00 sec)
以id为1的用户为例,他在orders表里存在两条记录,而这也表示has many,也就是业务上的模型存在一对多的关系。
接下来我们看看代码怎么写
package main
import (
"GormDemo/entity"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func main() {
mysqlConnect := "root:000000@tcp(192.168.23.233:3306)/gorm_order?charset=utf8mb4&parseTime=True&loc=Local"
//GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.23.1' IDENTIFIED BY '000000' WITH GRANT OPTION;
mysqlDialector := mysql.New(mysql.Config{
DSN: mysqlConnect,
DefaultStringSize: 256,
})
db, err := gorm.Open(mysqlDialector, &gorm.Config{
PrepareStmt: true,
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
fmt.Println(err.Error())
}
var user1 *entity.User
_ = QueryOrderInfo(db, user1)
}
func QueryOrderInfo(tx *gorm.DB, users *entity.User) error {
var userlist []entity.User
find := tx.Model(&entity.User{}).Preload("Orders").Find(&userlist)
fmt.Println("Row:", find.Row())
fmt.Println("users:", len(userlist))
PrintAllUser(userlist)
return nil
}
func PrintAllUser(userlist []entity.User) {
n := len(userlist)
fmt.Println("===============================")
for i := 0; i < n; i++ {
fmt.Println("UserId:", userlist[i].ID)
fmt.Println("UserName:", userlist[i].Username)
orderLen := len(userlist[i].Orders)
if orderLen < 1 {
fmt.Println("该用户无订单!")
}
for j := 0; j < orderLen; j++ {
fmt.Println(">OrderId:", userlist[i].Orders[j].ID)
fmt.Println(">TotalAmount:", userlist[i].Orders[j].TotalAmount)
fmt.Println(">OrderStatus:", userlist[i].Orders[j].OrderStatus)
}
fmt.Println("===============================")
}
}
运行结果
可以看到,这里userId为1的存在2个订单也都打印出来了。
2024/11/27 15:31:12 D:/development/Go Project/GormDemo/main.go:179
[2.605ms] [rows:3] SELECT * FROM `user_order_infos` WHERE `user_order_infos`.`user_id` IN (1,2,3)
2024/11/27 15:31:12 D:/development/Go Project/GormDemo/main.go:179
[1.505ms] [rows:3] SELECT * FROM `orders` WHERE `orders`.`id` IN (1,3,2) AND `orders`.`deleted_at` IS NULL
2024/11/27 15:31:12 D:/development/Go Project/GormDemo/main.go:179
[23.610ms] [rows:3] SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL
2024/11/27 15:31:12 D:/development/Go Project/GormDemo/main.go:180
[0.444ms] [rows:-] SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL
Row: &{<nil> 0xc00020a900}
users: 3
===============================
UserId: 1
UserName: user1
>OrderId: 1
>TotalAmount: 100.5
>OrderStatus: pending
>OrderId: 3
>TotalAmount: 50.25
>OrderStatus: completed
===============================
UserId: 2
UserName: user2
>OrderId: 2
>TotalAmount: 200
>OrderStatus: processing
===============================
UserId: 3
UserName: user3
该用户无订单!
===============================
Many2Many
Many to Many 会在两个 model 中添加一张连接表。
在刚刚我们有用户表也有订单表,现在我们使用以订单产品为中间表。
sql命令所示
-- 创建订单商品关联表(多对多关系)
CREATE TABLE IF NOT EXISTS order_products (
order_id BIGINT UNSIGNED NOT NULL,
product_id BIGINT UNSIGNED NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
-- 插入订单商品关联数据示例
INSERT INTO order_products (order_id, product_id, quantity)
VALUES
(1, 1, 2),
(1, 2, 1),
(2, 2, 3),
(3, 3, 1);
mysql查询结果
mysql> select * from order_products;
+----------+------------+----------+
| order_id | product_id | quantity |
+----------+------------+----------+
| 1 | 1 | 2 |
| 1 | 2 | 1 |
| 2 | 2 | 3 |
| 3 | 3 | 1 |
+----------+------------+----------+
4 rows in set (0.00 sec)
接下来看看代码怎么写
entity
这里我们order_products作为连接表,其中存在order_id和product_id因此,我们需要在Order结构体中定义Products []*Product `gorm:"many2many:order_products"` ,然后在Product结构体中定义Orders []*Order `gorm:"many2many:order_products"`
// Order 结构体,对应orders表
type Order struct {
gorm.Model
UserID uint64 `gorm:"not null"`
OrderNumber string `gorm:"not null"`
OrderDate string `gorm:"not null"`
TotalAmount float64 `gorm:"not null"`
OrderStatus string `gorm:"not null"`
PaymentMethod string `gorm:""`
ShippingAddress string `gorm:""`
Users []*User `gorm:"many2many:user_order_infos"`
Products []*Product `gorm:"many2many:order_products"` // 添加这一行用于表示与产品的多对多关系
}
// Product 结构体,对应products表
type Product struct {
gorm.Model
Name string `gorm:"not null"`
Description string `gorm:""`
Price float64 `gorm:"not null"`
StockQuantity int `gorm:"not null"`
Orders []*Order `gorm:"many2many:order_products"` // 添加这一行用于表示与订单的多对多关系
}
我们看看主程序代码编写
package main
import (
"GormDemo/entity"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func main() {
mysqlConnect := "root:000000@tcp(192.168.23.233:3306)/gorm_order?charset=utf8mb4&parseTime=True&loc=Local"
//GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.23.1' IDENTIFIED BY '000000' WITH GRANT OPTION;
mysqlDialector := mysql.New(mysql.Config{
DSN: mysqlConnect,
DefaultStringSize: 256,
})
db, err := gorm.Open(mysqlDialector, &gorm.Config{
PrepareStmt: true,
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
fmt.Println(err.Error())
}
_ = GetAllOrder(db)
}
func GetAllOrder(tx *gorm.DB) []entity.Order {
var orderlist []entity.Order
err := tx.Model(&entity.Order{}).Preload("Products").Find(&orderlist).Error
if err != nil {
fmt.Println("Failed to Get orderList")
}
fmt.Println("len(orderlist):", len(orderlist))
fmt.Println("===============================")
for i := 0; i < len(orderlist); i++ {
fmt.Println("OrderId:", orderlist[i].ID)
fmt.Println("TotalAmount:", orderlist[i].TotalAmount)
fmt.Println("UserID:", orderlist[i].UserID)
if len(orderlist[i].Products) < 1 {
fmt.Println("该订单不存在对应商品!")
} else {
for j := 0; j < len(orderlist[i].Products); j++ {
fmt.Println(">Name:", orderlist[i].Products[j].Name)
fmt.Println(">StockQuantity:", orderlist[i].Products[j].StockQuantity)
fmt.Println(">Description:", orderlist[i].Products[j].Description)
}
}
fmt.Println("===============================")
}
return orderlist
}
运行结果
2024/11/27 16:26:23 D:/development/Go Project/GormDemo/main.go:218
[2.011ms] [rows:4] SELECT * FROM `order_products` WHERE `order_products`.`order_id` IN (1,2,3)
2024/11/27 16:26:23 D:/development/Go Project/GormDemo/main.go:218
[1.493ms] [rows:3] SELECT * FROM `products` WHERE `products`.`id` IN (1,2,3) AND `products`.`deleted_at` IS NULL
2024/11/27 16:26:23 D:/development/Go Project/GormDemo/main.go:218
[19.477ms] [rows:3] SELECT * FROM `orders` WHERE `orders`.`deleted_at` IS NULL
len(orderlist): 3
===============================
OrderId: 1
TotalAmount: 100.5
UserID: 1
>Name: 商品1
>StockQuantity: 100
>Description: 这是一款很棒的商品
>Name: 商品2
>StockQuantity: 50
>Description: 具有特色的商品
===============================
OrderId: 2
TotalAmount: 200
UserID: 2
>Name: 商品2
>StockQuantity: 50
>Description: 具有特色的商品
===============================
OrderId: 3
TotalAmount: 50.25
UserID: 1
>Name: 商品3
>StockQuantity: 30
>Description: 热门商品
===============================
可以看到这里查询出来了结果。
我们分析sql语句发现,当我们定义many2many:order_products时,执行了
2024/11/27 16:26:23 D:/development/Go Project/GormDemo/main.go:218
[2.011ms] [rows:4] SELECT * FROM `order_products` WHERE `order_products`.`order_id` IN (1,2,3)
而这个也对应着mysql中的
mysql> select * from order_products;
+----------+------------+----------+
| order_id | product_id | quantity |
+----------+------------+----------+
| 1 | 1 | 2 |
| 1 | 2 | 1 |
| 2 | 2 | 3 |
| 3 | 3 | 1 |
+----------+------------+----------+
4 rows in set (0.00 sec)
也就是说,当我们使用Preload("Products")时,会去找到Order这个Model里的字段Products成员变量,然后读取到gorm:"many2many:order_products",所以他会先去查询order_products这个表,然后把数据加载到Order结构体里的Products,然后在查询数据时我们看到:
2024/11/27 16:26:23 D:/development/Go Project/GormDemo/main.go:218
[1.493ms] [rows:3] SELECT * FROM `products` WHERE `products`.`id` IN (1,2,3) AND `products`.`deleted_at` IS NULL
也就说明了,会去查询这个products表,然后根据查询的结果左连接回表给Order,然后当执行Find()时,就会查询把刚刚预加载查询出来的数据填充到这个orderlist里。
2024/11/27 16:26:23 D:/development/Go Project/GormDemo/main.go:218
[19.477ms] [rows:3] SELECT * FROM `orders` WHERE `orders`.`deleted_at` IS NULL