Golang中GORM多对多关系如何添加额外字段

Golang中GORM多对多关系如何添加额外字段 我对GORM还不太熟悉,不太理解其后台的工作原理。

我有一个名为 user 的表和一个名为 user_type 的表,我正在尝试创建一个用户。

type User struct {
// .... 这里有一些字段
    AccountId                  *uuid.UUID           `json:"foreignid,omitempty" gorm:"column:account_id" immutable:"-"`
    TypeIds                     []uuid.UUID          `json:"typeids,omitempty" gorm:"-:all"`
    Types                       []Type                `json:"-" gorm:"many2many:user_type;"`
}


type Type struct {
    // .... 这里有一些字段
    TypeIds           []uuid.UUID     `json:"typeids,omitempty" gorm:"-:all"`
    Types             []Type           `json:"-" gorm:"many2many:type_on_type;foreignKey:id;joinForeignKey:type_id;References:id;joinReferences:inner_type_id"`
}

type UserType struct {
    UserId *uuid.UUID `json:"user" gorm:"column:user_id" immutable:"-" validate:"required"`
    TypeId  *string    `json:"type" gorm:"column:type_id"`
    Type   *string    `json:"type" gorm:"column:type"`
}
func (rcv *User) Create(context *gin.Context, body []byte) (interface{}, *mn.Error) {
    resourceSlice := reflect.New(reflect.MakeSlice(reflect.SliceOf(rcv.resourceType), 0, 0).Type())
    if goErr := json.Unmarshal(body, resourceSlice.Interface()); goErr != nil {
        return nil, mn.ErrorJSONUnmarshal.Clone(goErr.Error())
    }

kindTags = rcv.GetKindTags() // 我在这里有 type 列的最新值

   _ := tx.SetupJoinTable(&rpg.User{}, "Types", &UserTag{})
    result := db.Create(resourceSlice.Elem().Interface())
    if result.Error != nil {
        return nil,result.Error
    }
    return resourceSlice.Elem().Interface(), nil
}

当调用 Create() 时,会显示运行了两个查询。

1. INSERT INTO "data"."user_type" ("user_id","type_id")
2. INSERT INTO "data"."user" ()

我尝试在 user_type 表中添加一个额外的列 type

为此,我添加了一个 hook

func (ut *UserType) BeforeCreate(db *gorm.DB) error {
	db.Statement.SetColumn("type", ut.Type) // 需要在这里更新值
	return nil
}

在此之后,我可以看到查询中添加了 type 列,但值为 null。

INSERT INTO “user_type” (“user_id”,“type_id”,“type”) VALUES (‘3091a78e-5a3c-6ae2-f36b-a9af1aa3982b’,‘ed31a1ce-8805-62fb-e7bf-fb751d6beb73’,NULL) ON CONFLICT DO NOTHING

但是我无法使用包含列值的 kindTags 来为 ut.Type 填充更新后的值。

你能告诉我如何将值传递给 hook 吗?

谢谢


更多关于Golang中GORM多对多关系如何添加额外字段的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

进行如下更改:

kindTags := rcv.GetKindTags() // 在此处获取 kindTags

    // 假设 kindTags 是一个映射或具有某种结构以获取 Type 值
    // 设置连接表
    _ = tx.SetupJoinTable(&User{}, "Types", &UserType{})

    // 使用 resourceSlice 来设置用户和类型
    users := resourceSlice.Elem().Interface().([]User)
    for i := range users {
        for j := range users[i].Types {
            users[i].Types[j].Type = kindTags[users[i].Types[j].TypeId] // 或者根据你的方式获取值
        }
    }

func (ut *UserType) BeforeCreate(tx *gorm.DB) error {
    // 在此处基于某些逻辑设置 Type 值
    // 例如,如果 kindTags 在上下文或全局变量中可用
    ut.Type = "someValueFromKindTags" // 将此替换为获取 kindTags 值的实际逻辑
    return nil
}

更多关于Golang中GORM多对多关系如何添加额外字段的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在GORM中为多对多关系的连接表添加额外字段,需要正确设置关联模型和预加载。以下是解决方案:

// 定义UserType模型作为连接表
type UserType struct {
    UserID    *uuid.UUID `gorm:"primaryKey;column:user_id"`
    TypeID    *uuid.UUID `gorm:"primaryKey;column:type_id"`
    Type      string     `gorm:"column:type"` // 额外字段
    CreatedAt time.Time
    UpdatedAt time.Time
}

// 修改User模型
type User struct {
    ID        *uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
    Name      string
    Types     []Type `gorm:"many2many:user_type;joinForeignKey:UserID;JoinReferences:TypeID"`
}

// 修改Type模型
type Type struct {
    ID        *uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
    Name      string
    Users     []User `gorm:"many2many:user_type;joinForeignKey:TypeID;JoinReferences:UserID"`
}

// 在初始化时设置连接表
func init() {
    db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})
    
    // 注册连接表
    err := db.SetupJoinTable(&User{}, "Types", &UserType{})
    if err != nil {
        panic(err)
    }
    
    err = db.SetupJoinTable(&Type{}, "Users", &UserType{})
    if err != nil {
        panic(err)
    }
}

// 创建用户并设置额外字段
func CreateUserWithType(db *gorm.DB, userName string, typeIDs []uuid.UUID, typeValue string) error {
    user := &User{
        Name: userName,
        Types: []Type{},
    }
    
    // 查找类型
    var types []Type
    if err := db.Where("id IN ?", typeIDs).Find(&types).Error; err != nil {
        return err
    }
    
    // 创建用户
    if err := db.Create(user).Error; err != nil {
        return err
    }
    
    // 手动创建关联记录并设置额外字段
    for _, t := range types {
        userType := &UserType{
            UserID: user.ID,
            TypeID: t.ID,
            Type:   typeValue, // 设置额外字段
        }
        if err := db.Create(userType).Error; err != nil {
            return err
        }
    }
    
    return nil
}

// 或者使用Association模式
func CreateUserWithAssociation(db *gorm.DB, userName string, types []Type, typeValue string) error {
    user := &User{
        Name:  userName,
        Types: types,
    }
    
    // 创建用户但不自动创建关联
    if err := db.Omit("Types").Create(user).Error; err != nil {
        return err
    }
    
    // 手动添加关联并设置额外字段
    for _, t := range types {
        userType := &UserType{
            UserID: user.ID,
            TypeID: t.ID,
            Type:   typeValue,
        }
        if err := db.Create(userType).Error; err != nil {
            return err
        }
    }
    
    return nil
}

// 查询时包含额外字段
func GetUserWithTypes(db *gorm.DB, userID uuid.UUID) (*User, error) {
    var user User
    
    // 预加载关联并选择额外字段
    err := db.Preload("Types", func(db *gorm.DB) *gorm.DB {
        return db.Joins("JOIN user_type ON user_type.type_id = types.id AND user_type.user_id = ?", userID)
    }).First(&user, "id = ?", userID).Error
    
    if err != nil {
        return nil, err
    }
    
    // 如果需要获取连接表的额外字段
    var userTypes []UserType
    if err := db.Where("user_id = ?", userID).Find(&userTypes).Error; err != nil {
        return nil, err
    }
    
    return &user, nil
}

对于你的具体问题,需要在创建关联时直接设置UserType模型的字段值,而不是依赖hook:

func (rcv *User) Create(context *gin.Context, body []byte) (interface{}, error) {
    var user User
    if err := json.Unmarshal(body, &user); err != nil {
        return nil, err
    }
    
    // 创建用户
    if err := db.Create(&user).Error; err != nil {
        return nil, err
    }
    
    // 获取kindTags中的type值
    kindTags := rcv.GetKindTags()
    typeValue := kindTags["type"] // 假设这是你的type值
    
    // 为每个TypeID创建关联记录
    for _, typeID := range user.TypeIds {
        userType := &UserType{
            UserID: user.ID,
            TypeID: &typeID,
            Type:   typeValue, // 直接设置额外字段
        }
        if err := db.Create(userType).Error; err != nil {
            return nil, err
        }
    }
    
    return user, nil
}

这样可以直接在创建UserType记录时设置type字段的值,而不需要通过hook传递。

回到顶部