gorm关联查询
# gorm关联查询
本文总结golang的orm框架gorm的关联查询用法。gorm的关联查询并不依赖于数据库中的外键约束定义,是使用自己的一套规则来执行关联查询。
如下提供一个示例代码:
package main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"os"
"time"
)
var Db *gorm.DB
var MysqlUrl = "root:buzhidao@tcp(127.0.0.1:3306)/testGormPreload?charset=utf8mb4&parseTime=True&loc=Asia%2fShanghai&timeout=10s"
var MysqlMaxIdleCount = 10
var MysqlMaxOpenCount = 20
var LogFile = "./log"
var LogLevel = "debug"
var Logger *zap.Logger
/**
初始化logger日志对象
*/
func initLogger() {
hook := lumberjack.Logger{
Filename: LogFile, // 日志文件路径
MaxSize: 1024, // megabytes
MaxBackups: 3, // 最多保留3个备份
MaxAge: 7, //days
Compress: true, // 是否压缩 disabled by default
}
writeSyncer := zapcore.AddSync(&hook)
var level zapcore.Level
switch LogLevel {
case "debug":
level = zap.DebugLevel
case "info":
level = zap.InfoLevel
case "error":
level = zap.ErrorLevel
default:
level = zap.InfoLevel
}
core := zapcore.NewCore(
// 编码器配置
zapcore.NewJSONEncoder(NewEncoderConfig()),
// 打印到控制台和文件
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout),
writeSyncer),
// 日志级别
level,
)
// 开启开发模式,堆栈跟踪
caller := zap.AddCaller()
// 开启文件及行号
development := zap.Development()
// 设置初始化字段
filed := zap.Fields(zap.String("serviceName", "demoProject"))
// 构造日志
Logger = zap.New(core, caller, development, filed)
}
func NewEncoderConfig() zapcore.EncoderConfig {
return zapcore.EncoderConfig{
// Keys can be anything except the empty string.
TimeKey: "time",
LevelKey: "level",
NameKey: "name",
CallerKey: "caller",
MessageKey: "message",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
}
func TimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}
// 文章
type Topic struct {
Id int
Title string
//根据gorm约定,User对应的外键字段为UserId,所以实现User字段的自动填充
UserId int
User User
CategoryId int
Category Category //文章所属分类外键
}
// 用户
type User struct {
Id int
Name string
Topics []Topic
}
// 分类
type Category struct {
Id int
Name string
}
func initDb() *gorm.DB {
var err error
Db, err = gorm.Open(mysql.Open(MysqlUrl), &gorm.Config{})
//无法连接数据库则退出程序
if err != nil {
Logger.Fatal("gorm.Open error", zap.Error(err))
}
//配置数据库连接池
sqlDb, err := Db.DB()
if err != nil {
Logger.Fatal("Db.DB error", zap.Error(err))
}
sqlDb.SetMaxIdleConns(MysqlMaxIdleCount)
sqlDb.SetMaxOpenConns(MysqlMaxOpenCount)
return Db
}
func main() {
initLogger()
initDb()
models := []interface{}{
&Topic{},
&User{},
&Category{},
}
/**
1.执行建表语句:
执行该方法会自动为数据库中的表创建外键约束,然而该约束并非必须存在。
通过验证表明(手工删除数据库中的外键约束,并不再执行该函数AutoMigrate,因为该函数会自动创建外键约束),gorm的关联查询并依赖于数据库表的外键约束定义
*/
err := Db.Debug().AutoMigrate(models...)
if err != nil {
Logger.Fatal("db.Debug().AutoMigrate error", zap.Error(err))
}
//2.执行sql
/*INSERT INTO categories(id, name) VALUES (1, '测试分类');
INSERT INTO users(id, name) VALUES (1, '测试用户');
INSERT INTO topics(id, title, user_id, category_id) VALUES (1, '测试1', 1, 1);
INSERT INTO topics(id, title, user_id, category_id) VALUES (2, '测试2', 1, 1);*/
//3.执行预加载
topics, err := GetTopicsById(Db, 1)
if err != nil {
Logger.Error("GetTopicsById error", zap.Error(err))
}
Logger.Info("GetTopicsById ok", zap.Any("topics", topics))
user, err := GetUserById(Db, 1)
if err != nil {
Logger.Error("GetUserById error", zap.Error(err))
return
}
Logger.Info("GetUserById ok", zap.Any("user", user))
}
func GetUserById(db *gorm.DB, id int) (*User, error) {
user := &User{}
if err := Db.Preload("Topics").First(user).Error; err != nil {
Logger.Error("Db.Preload(\"Topics\").Find error", zap.Error(err))
return nil, err
}
return user, nil
}
func GetTopicsById(db *gorm.DB, id int) (*Topic, error) {
var topic Topic
err := db.Where("id=?", id).
Preload("Category").
Preload("User").
First(&topic).Error
if err != nil {
return nil, err
}
return &topic, nil
}
上次更新: 2021-05-25 17:10:07