GORM实战:如何根据主键策略选择.Save()与.Create()

Gorm Save更新踩坑记录|Gorm Save主键冲突|Duplicate entry ‘xxxx‘ for key ‘PRIMARY 在我最近使用Gorm进行字段更新的过程中,我遇到了一个问题。当我尝试更新status字段时,即使该字段的值没有发生变化,Gorm还是提示我“Duplicate entry 'xxxx' for key 'PRIMARY'”。 阅读详情

1. 从一次线上事故说起:为什么选错方法会出大问题

几年前,我在一个用户管理系统中踩过一个坑,至今记忆犹新。当时有个需求是同步外部系统的用户数据到我们自己的数据库,逻辑很简单:如果用户ID存在就更新信息,不存在就创建新记录。我心想,这不就是典型的“保存”操作吗?于是很自然地用了 Save() 方法。

代码大概长这样:

func SyncUser(user User) error {
    return db.Save(&user).Error
}

看起来没问题对吧?但上线后没多久,监控就报警了——数据库出现了大量主键冲突的错误。我查了半天才发现,问题出在有些用户的ID虽然是数字,但在外部系统里是字符串类型,同步过来后GORM认为这些ID是零值(因为Go里int的零值是0),于是每次都执行插入操作,自然就冲突了。

这就是我今天想跟你聊的核心问题:在GORM里,.Save()和.Create()看起来都能“保存”数据,但它们的底层逻辑完全不同,用错了轻则报错,重则数据混乱。特别是当你的表主键策略比较复杂时,更需要理解这两个方法的脾气。

简单来说,.Create()是个直肠子,它只干一件事:插入新记录。如果你给的数据主键不为零,它会认为你想插入一个已存在的记录,直接报错。而.Save()是个聪明人,它会先看看主键是否存在,存在就更新,不存在就插入。但正是这个“聪明”,有时候会带来意想不到的麻烦。

2. 深入骨髓:.Create() 的纯粹插入哲学

2.1 当主键为零值时:标准的插入操作

让我们先彻底搞懂 .Create()。这个方法的设计哲学很纯粹:我就是来创建新记录的,别让我干别的。

最典型的场景就是自增主键。假设我们有个用户表:

type User struct {
    ID   uint   `gorm:"primaryKey;autoIncrement"`
    Name string
    Age  int
}

// 场景1:标准插入
user := User{Name: "张三", Age: 25}
result := db.Create(&user)
// 生成的SQL:INSERT INTO users (name, age) VALUES ('张三', 25)
// user.ID 会被自动填充为数据库生成的自增ID

这里的关键点在于,ID字段是零值(0),GORM看到后就知道:“哦,这是个新用户,我要生成新ID”。插入成功后,user.ID会被自动赋值为数据库返回的自增ID。

我建议你在实际开发中养成一个习惯:每次.Create()之后,都检查一下生成的主键值。这不仅能确认插入成功,还能在需要关联操作时拿到正确的ID。

if err := db.Create(&user).Error; err != nil {
    log.Printf("插入失败: %v", err)
    return err
}
log.Printf("新用户创建成功,ID: %d", user.ID)

2.2 当主键非零时:为什么.Create()会报错

现在来看一个容易出错的场景。有时候我们从外部系统拿到数据,里面已经包含了ID:

// 场景2:带ID的插入尝试
user := User{ID: 100, Name: "李四", Age: 30}
err := db.Create(&user).Error
// 这里会报错!错误信息类似:
// Error 1062: Duplicate entry '100' for key 'PRIMARY'

为什么?因为.Create()看到ID字段不是零值(0),它就认为你想插入一个ID为100的记录。如果数据库里已经存在ID=100的用户,就会触发主键冲突;即使不存在,GORM也会严格按照你给的ID去插入,而不是使用自增。

这里有个重要细节:GORM判断“主键是否存在”的标准是“是否为零值”。对于数值类型是0,对于字符串是"",对于指针是nil。所以如果你用了*uint作为ID类型,那么nil时GORM会认为是新记录。

2.3 批量插入的陷阱与技巧

批量插入是.Create()的强项,但也有一些坑需要注意:

// 正确的批量插入
users := []User{
    {Name: "王五", Age: 28},
    {Name: "赵六", Age: 32},
    {Name: "孙七", Age: 45},
}

// 方法1:直接Create切片
result := db.Create(&users)
fmt.Printf("插入了%d条记录", result.RowsAffected)

// 方法2:分批插入(大数据量时推荐)
batchSize := 100
result = db.CreateInBatches(&users, batchSize)

但这里有个性能陷阱:如果你一次性插入几万条记录,可能会遇到数据库连接超时或者内存问题。我建议超过1000条就考虑分批,并且最好在事务中操作:

err := db.Transaction(func(tx *gorm.DB) error {
    // 在事务中批量插入
    if err := tx.CreateInBatches(&users, 1000).Error; err != nil {
        return err // 自动回滚
    }
    return nil // 自动提交
})

另外,批量插入时钩子函数(Hooks) 的行为也需要注意。BeforeCreate和AfterCreate会对每个记录分别调用,如果钩子里有复杂逻辑,批量插入的性能优势可能会被抵消。

3. 双重人格:.Save() 的智能与危险

3.1 Save() 的核心逻辑:先更新,后插入的保障机制

如果说.Create()是单纯的,那么.Save()就是复杂的。它的行为可以概括为:尽一切可能让数据保存成功。

官方文档说得很清楚:Save()是一个upsert(更新或插入)操作。但它的具体实现比听起来更微妙:

  1. 检查主键:首先检查传入结构体的主键字段
  2. 主键存在 → 尝试更新(更新所有字段)
  3. 更新影响行数=0 → 回退到插入
  4. 主键不存在 → 直接插入

这个逻辑在源码中体现得很清楚。我读过GORM的源码,Save()方法的核心判断大致是这样的:

// 伪代码,展示Save的逻辑
func Save(value interface{}) error {
    // 检查主键是否为零值
    if 主键是零值 {
        return Create(value) // 直接插入
    }
    
    // 尝试更新所有字段
    result := UpdateAllFields(value)
    
    // 如果更新没有影响任何行
    if result.RowsAffected == 0 {
        // 回退到插入
        return CreateWithOnDuplicateKeyUpdate(value)
    }
    
    return nil
}

这种“先更新,失败再插入”的策略,本意是好的,但在某些场景下会出问题。

3.2 主键策略如何影响.Save()的行为

3.2.1 自增主键的典型场景

对于最常见的自增主键,.Save()的行为比较直观:

// 场景1:新记录(ID=0)
user := User{Name: "张三", Age: 25}  // ID默认为0
db.Save(&user)
// SQL: INSERT INTO users (name, age) VALUES ('张三', 25)

// 场景2:更新现有记录
user := User{ID: 1, Name: "张三", Age: 26}
db.Save(&user)
// SQL: UPDATE users SET name='张三', age=26 WHERE id=1

但这里有个隐藏的坑:如果更新操作没有修改任何值(比如所有字段都和数据库里一样),RowsAffected可能是0,这时.Save()会尝试插入,导致主键冲突!

3.2.2 非自增主键的注意事项

当主键不是自增,而是业务ID时,情况更复杂:

type Product struct {
    Code string `gorm:"primaryKey"`  // 商品编码作为主键
    Name string
    Price float64
}

// 假设数据库里没有 CODE="P001" 的商品
product := Product{Code: "P001", Name: "手机", Price: 2999.00}
db.Save(&product)
// 第一次执行:INSERT INTO products (code, name, price) VALUES ('P001', '手机', 2999.00)

// 第二次执行同样的Save
product.Price = 2799.00
db.Save(&product)
// 这次会执行:UPDATE products SET name='手机', price=2799.00 WHERE code='P001'
3.2.3 复合主键的特殊处理

复合主键的场景下,.Save()的行为又有所不同:

type UserRole struct {
    UserID uint `gorm:"primaryKey"`
    RoleID uint `gorm:"primaryKey"`
    // 复合主键:UserID + RoleID
}

// 必须所有主键字段都非零,Save()才会尝试更新
userRole := UserRole{UserID: 1, RoleID: 2}
db.Save(&userRole)
// 如果(1,2)存在则更新,否则插入

3.3 那些年我踩过的.Save()的坑

3.3.1 零值字段被意外更新

这是.Save()最危险的行为之一:它会更新所有字段,包括零值。

type Article struct {
    ID        uint
    Title     string
    Content   string
    ViewCount int  // 浏览数
}

// 假设数据库里有一条记录:
// ID: 1, Title: "旧标题", Content: "旧内容", ViewCount: 100

article := Article{ID: 1, Title: "新标题"}
db.Save(&article)

// 生成的SQL可能是:
// UPDATE articles SET title='新标题', content='', view_count=0 WHERE id=1
// 注意:Content和ViewCount被更新为零值了!

解决方案:如果你只想更新部分字段,应该用Updates()而不是Save():

// 只更新Title字段
db.Model(&Article{}).Where("id = ?", 1).Update("title", "新标题")

// 或者更新多个字段,但跳过零值
db.Model(&article).Updates(map[string]interface{}{
    "title": "新标题",
    // 不指定content和view_count,它们不会被更新
})
3.3.2 并发下的竞态条件

在高并发场景下,.Save()可能引发竞态条件:

// 两个goroutine同时执行
go func() {
    user := User{ID: 1, Name: "张三"}
    db.Save(&user)  // 可能同时执行插入,导致冲突
}()

go func() {
    user := User{ID: 1, Name: "李四"}
    db.Save(&user)  // 同上
}()

解决方案:使用更明确的Create或Update,或者使用数据库的乐观锁/悲观锁。

3.3.3 与Where()条件联用的迷惑行为

.Save()和.Where()一起用时,行为可能不符合直觉:

user := User{ID: 1, Name: "张三", Age: 30}
db.Where("age > ?", 20).Save(&user)
// 生成的SQL:UPDATE users SET name='张三', age=30 WHERE age > 20 AND id=1
// 注意:WHERE条件被加上了id=1!

这是因为.Save()总是会包含主键条件,即使你显式指定了其他WHERE条件。

4. 实战决策指南:什么情况下该用哪个?

4.1 决策流程图:一眼看懂如何选择

面对具体场景时,你可以参考下面这个决策流程:

开始
  ↓
需要保存数据
  ↓
┌─────────────────┐
│ 数据是否来自数据库查询? │
└─────────┬───────┘
          │
          ├─ 是 → 使用 Updates() 或 Save()(明确要更新所有字段时)
          │
          └─ 否 → 主键是否确定存在?
                     │
                     ├─ 是 → 使用 Updates()(推荐)或 Save()
                     │
                     └─ 否 → 是否允许覆盖已存在记录?
                                │
                                ├─ 是 → 使用 Save() 或 Upsert
                                │
                                └─ 否 → 使用 Create()

4.2 典型场景对比分析

让我们通过几个真实场景来感受如何选择:

场景一:用户注册(明确是新记录)
// 明确知道是新用户,用Create最安全
func Register(username, password string) error {
    user := User{
        Username: username,
        Password: hashPassword(password),
        CreatedAt: time.Now(),
    }
    
    // 明确使用Create,避免意外覆盖
    if err := db.Create(&user).Error; err != nil {
        // 处理错误,可能是用户名重复等
        return fmt.Errorf("注册失败: %w", err)
    }
    
    // 注册成功,可以拿到新的UserID
    log.Printf("新用户注册成功: ID=%d, Username=%s", user.ID, user.Username)
    return nil
}
场景二:用户更新个人信息(部分更新)
// 只更新部分字段,用Updates而不是Save
func UpdateUserProfile(userID uint, updates map[string]interface{}) error {
    // 使用Updates只更新指定的字段
    result := db.Model(&User{}).
        Where("id = ?", userID).
        Updates(updates)
    
    if result.Error != nil {
        return result.Error
    }
    
    // 检查是否真的更新了记录
    if result.RowsAffected == 0 {
        return fmt.Errorf("用户不存在或数据未变更")
    }
    
    return nil
}

// 调用示例
err := UpdateUserProfile(1, map[string]interface{}{
    "nickname": "新昵称",
    "avatar":   "new_avatar.jpg",
    // 不更新其他字段
})
场景三:同步外部数据(可能需要更新或插入)
// 同步商品信息,外部系统可能推送新商品或更新已有商品
func SyncProduct(externalProduct ExternalProduct) error {
    product := Product{
        Code:  externalProduct.Code,  // 业务主键
        Name:  externalProduct.Name,
        Price: externalProduct.Price,
    }
    
    // 这里用Save是合适的,因为我们希望:
    // 1. 新商品 → 插入
    // 2. 已有商品 → 更新
    // 但要注意零值问题!
    
    // 更好的做法:使用Clauses处理冲突
    err := db.Clauses(clause.OnConflict{
        Columns:   []clause.Column{{Name: "code"}},  // 冲突列
        DoUpdates: clause.AssignmentColumns([]string{"name", "price"}),  // 更新哪些列
    }).Create(&product).Error
    
    return err
}

4.3 性能考量:为什么有时候要避免.Save()

.Save()的便利性是有代价的:

  1. 额外的检查开销:每次都要检查主键、尝试更新、可能回退到插入
  2. 全字段更新:即使你只想改一个字段,它也会更新所有字段
  3. 网络往返:如果更新行数为0,它还会尝试插入,意味着可能多一次数据库交互

在性能敏感的场景下,我建议:

// 高性能场景:明确知道是插入还是更新

// 情况1:明确插入
func BatchInsertUsers(users []User) error {
    // 使用CreateInBatches,明确是插入
    return db.CreateInBatches(users, 1000).Error
}

// 情况2:明确更新
func UpdateUserStatus(userID uint, status string) error {
    // 使用Update,只更新一个字段
    return db.Model(&User{}).
        Where("id = ?", userID).
        Update("status", status).Error
}

// 情况3:需要upsert但关心性能
func UpsertProduct(product Product) error {
    // 使用原生upsert,一次数据库交互
    return db.Clauses(clause.OnConflict{
        Columns:   []clause.Column{{Name: "code"}},
        UpdateAll: true,
    }).Create(&product).Error
}

5. 高级话题:自定义主键与复杂场景

5.1 非ID主键字段的配置

GORM默认寻找名为ID或Id的字段作为主键。如果你的主键字段名不同,需要显式标记:

// 使用Email作为主键
type Admin struct {
    Email    string `gorm:"primaryKey;size:100"`
    Name     string
    Role     string
}

// 或者使用自定义结构体标签
type Customer struct {
    CustNo   string `gorm:"column:customer_no;primaryKey"`
    Name     string
    // ...
}

// 对于这种非ID主键,Save()的行为:
admin := Admin{Email: "admin@example.com", Name: "管理员"}
db.Save(&admin)
// 会检查Email是否已存在,存在则更新,否则插入

5.2 使用Upsert替代Save的可行性

在某些场景下,使用clause.OnConflict实现Upsert比.Save()更可控:

// 方法1:使用OnConflict实现upsert
func UpsertUser(user User) error {
    return db.Clauses(clause.OnConflict{
        Columns:   []clause.Column{{Name: "id"}},  // 冲突依据
        DoUpdates: clause.AssignmentColumns([]string{"name", "email", "updated_at"}),  // 更新哪些列
    }).Create(&user).Error
}

// 方法2:更新所有非主键字段
func UpsertAllFields(user User) error {
    return db.Clauses(clause.OnConflict{
        Columns:   []clause.Column{{Name: "id"}},
        UpdateAll: true,  // 更新所有非主键字段
    }).Create(&user).Error
}

// 方法3:自定义更新逻辑
func UpsertWithCustomUpdate(user User) error {
    return db.Clauses(clause.OnConflict{
        Columns: []clause.Column{{Name: "id"}},
        DoUpdates: clause.Assignments(map[string]interface{}{
            "login_count": gorm.Expr("login_count + 1"),
            "last_login":  time.Now(),
        }),
    }).Create(&user).Error
}

Upsert的优点:

  • 更明确的冲突处理逻辑
  • 可以控制更新哪些字段
  • 通常性能更好(一次数据库交互)

5.3 事务中的Save与Create行为

在事务中,.Save()和.Create()的行为需要特别注意:

err := db.Transaction(func(tx *gorm.DB) error {
    // 在事务中创建用户
    user := User{Name: "事务用户"}
    if err := tx.Create(&user).Error; err != nil {
        return err  // 回滚
    }
    
    // 同时创建用户配置
    config := UserConfig{
        UserID: user.ID,  // 使用刚创建的UserID
        Theme:  "dark",
    }
    
    // 这里用Create,因为明确是新记录
    if err := tx.Create(&config).Error; err != nil {
        return err  // 回滚,包括前面的用户创建
    }
    
    return nil  // 提交事务
})

在事务中使用.Save()要特别小心,因为它的“更新或插入”逻辑可能导致意外的数据覆盖。如果事务需要回滚,但.Save()已经更新了某些记录,这些更新是无法撤销的(除非使用更高级的保存点)。

6. 最佳实践与性能优化

6.1 根据主键策略制定方法选择规范

基于不同的主键策略,我建议这样选择:

自增主键场景:
  • 新记录:总是用Create(),让数据库生成ID
  • 更新记录:用Updates()指定要更新的字段
  • 只有确定需要upsert时才用Save()
UUID或业务主键场景:
  • 新记录:用Create(),但确保主键已正确设置
  • 更新记录:用Updates()或Save()(如果确定要全字段更新)
  • 考虑使用:clause.OnConflict实现更可控的upsert
无主键或复合主键场景:
  • 尽量避免使用Save(),因为行为可能不符合预期
  • 明确使用Create()或Update()

6.2 监控与调试:如何知道Save和Create实际做了什么

在实际项目中,了解GORM实际执行的SQL很重要:

// 方法1:启用Debug模式
db.Debug().Save(&user)
// 输出:执行的SQL语句

// 方法2:使用Logger记录所有SQL
newLogger := logger.New(
    log.New(os.Stdout, "\r\n", log.LstdFlags),
    logger.Config{
        SlowThreshold: time.Second,
        LogLevel:      logger.Info,
        Colorful:      true,
    },
)

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    Logger: newLogger,
})

// 方法3:在特定会话中调试
db.Session(&gorm.Session{DryRun: true}).Save(&user)
// DryRun模式只生成SQL不执行,用于调试

6.3 常见错误模式与避坑指南

根据我的经验,这些错误最常见:

  1. 错误:用Save更新部分字段

    // 反例
    user := User{ID: 1, Name: "新名字"}
    db.Save(&user)  // 会把其他字段设为零值!
    
    // 正例
    db.Model(&User{}).Where("id = ?", 1).Update("name", "新名字")
    
  2. 错误:在循环中频繁Save

    // 反例:N+1问题
    for _, user := range users {
        db.Save(&user)  // 每次循环都执行一次数据库操作
    }
    
    // 正例:批量操作
    db.CreateInBatches(users, 100)  // 或批量更新
    
  3. 错误:忽略Save的返回值

    // 反例
    db.Save(&user)  // 不检查错误和影响行数
    
    // 正例
    result := db.Save(&user)
    if result.Error != nil {
        return result.Error
    }
    if result.RowsAffected == 0 {
        log.Println("没有记录被更新,可能执行了插入")
    }
    
  4. 错误:混合使用Model和Save

    // 反例:未定义行为
    db.Model(&User{}).Where("age > ?", 18).Save(&user)
    
    // 正例:分开操作
    // 查询
    db.Model(&User{}).Where("age > ?", 18).First(&user)
    // 更新
    db.Save(&user)
    

6.4 性能优化技巧

  1. 批量操作优先:总是考虑批量插入/更新而不是循环单条操作
  2. 明确指定字段:使用Select和Omit减少不必要的数据传输
  3. 避免全字段更新:除非必要,否则不用Save()的全字段更新
  4. 合理使用索引:确保Save()和Create()操作的条件字段有索引
  5. 连接池配置:调整GORM和数据库的连接池参数
// 性能优化示例
sqlDB, err := db.DB()
if err != nil {
    log.Fatal(err)
}

// 配置连接池
sqlDB.SetMaxIdleConns(10)           // 最大空闲连接
sqlDB.SetMaxOpenConns(100)          // 最大打开连接
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大生命周期

// 批量插入优化
func BatchInsertOptimized(users []User) error {
    return db.Session(&gorm.Session{
        CreateBatchSize: 1000,      // 每批1000条
        SkipHooks:       true,      // 跳过钩子提升性能
    }).Create(&users).Error
}

在实际项目中,我通常会给团队制定这样的规范:默认使用Create()和Updates(),只有明确需要upsert逻辑时才考虑Save(),并且要加上详细的注释说明为什么用Save()而不是其他方法。这样既能保证代码清晰,又能避免意外的数据问题。

记住,GORM的.Save()和.Create()不是可以随意互换的——它们有着根本不同的语义和行为。理解这些差异,根据你的具体场景和主键策略做出明智选择,才能写出既正确又高效的数据库操作代码。

GORM更新操作全解析:Create、Save、Update与Updates对比 ORM(对象关系映射)是数据库操作的重要抽象层,通过将数据库表映射为编程语言中的对象,极大简化了数据持久化操作。GORM作为Golang生态中最流行的ORM框架,其更新操作包含Create、Save、Update和Updates等多种方法,每种方法在SQL生成、钩子触发和零值处理等方面存在显著差异。理解这些差异对编写高效、安全的数据库操作代码至关重要,特别是在需要处理批量更新、零值覆盖或需要跳过钩子执行的性能敏感场景。本文通过对比分析这些方法的底层实现原理,帮助开发者根据实际需求选择最合适的更新策略,提升应 阅读详情

相关推荐

Golang REST API开发实战:从零构建高性能后端服务

REST API作为现代Web开发的核心技术,采用HTTP协议实现客户端与服务器的无状态通信。其设计原则包括资源导向、统一接口和可缓存性,通过标准HTTP方法对资源进行操作。在微服务架构盛行的今天,Golang凭借其卓越的并发模型和高效性能成为构建REST API的理想选择。goroutine和channel机制让高并发处理变得简单安全,而net/http标准库提供了完善的HTTP服务能力。本文以Gin框架为例,结合GORM数据库操作,详细演示如何从环境配置开始,逐步实现路由定义、数据交互、中间件集成等核心

weixin_33695450的博客 495

GORM实战:Save和Create的正确使用姿势,避免踩坑指南

本文深入解析GORM框架中Save和Create方法的核心差异与使用场景,帮助开发者避免常见陷阱。通过对比表格、代码示例和性能分析,详细阐述两者在INSERT、UPDATE、主键处理等方面的行为差异,并提供最佳实践建议,助力编写高效、健壮的数据库操作代码。

kmeans3miner的博客 192

GoFly框架解析:基于Gin+Vue3的极速全栈开发实践

在Web开发领域,框架是提升开发效率、统一技术栈的核心工具。其原理在于通过封装通用模块、约定项目结构,为开发者提供一套开箱即用的解决方案,从而减少重复劳动、降低技术选型成本。对于Go语言而言,虽然其本身以高性能和简洁著称,但构建一个完整的Web应用仍需集成路由、ORM、认证等多个组件,这正是GoFly这类全栈框架的技术价值所在。它基于Gin和Vue 3等成熟生态,将最佳实践模块化,实现了从数据库操作到前端渲染的快速开发闭环。在实际应用场景中,这类框架特别适合需要快速迭代的中后台管理系统、企业级应用原型和AP

weixin_34354173的博客 563

GORM实战:如何根据主键智能选择.Save()与.Create()操作

本文深入解析GORM中.Save()与.Create()的核心区别与智能选择策略,通过主键识别机制、性能优化技巧及实战案例,帮助开发者高效处理数据库操作。特别针对主键冲突、批量处理等常见问题提供解决方案,提升开发效率与系统性能。

qsc9012345的博客 441

Claude Code插件深度解析:Go开发者的IDE智能代理配置与排障指南

代码智能代理是现代IDE中融合大模型能力与本地开发环境的新型编程助手,其核心原理在于实时提取编辑器上下文(如AST、导入路径、项目配置)并安全传输至云端模型服务进行推理。技术价值体现在精准理解项目规范、自动适配依赖版本与编码风格,显著提升Go等强约束语言的生成可靠性。典型应用场景包括HTTP Handler骨架生成、GORM模型同步、单元测试覆盖、错误处理重构及性能瓶颈分析。本文聚焦Claude Code插件在VS Code中的真实能力边界、网络与认证配置要点,以及针对'unable to connect

weixin_33910759的博客 351

RGB接口电容触摸屏驱动(二)接口与时序说明

RGB接口电容触摸屏驱动(二)接口与时序说明

杰克拉力船长个人博客 4920

GORM实战:Save和Create方法到底怎么选?5个真实场景帮你避坑

本文深入解析GORM中Save和Create方法的核心差异,通过5个真实业务场景(如电商订单幂等性、用户画像更新等)展示如何避免数据覆盖和主键冲突问题。重点对比两者的SQL生成逻辑、主键处理策略及性能表现,帮助开发者做出精准选择,提升数据库操作安全性和效率。

stem5的博客 457

别再纠结选哪个向量库了!我用Spring AI + Redis Stack 7.2,5分钟搞定企业知识库RAG原型

本文介绍了如何利用Spring AI和Redis Stack 7.2快速构建企业级RAG应用,解决向量数据库选型难题。Redis Stack 7.2集成了RediSearch模块,支持向量相似性搜索,具备高性能、低成本、零学习成本等优势,特别适合中小团队快速搭建知识库系统。通过实战代码示例,展示了从环境准备到核心功能实现的完整流程。

weixin_29061041的博客 272

使用GORM操作数据库

依赖 github.com/jinzhu/gorm 定义实体类 可以通过定义嵌套gorm.Model这个结构体的类型来定义实体类,gorm.Model中定义了数据库表的一些常用基本字段 type Model struct { ID uint `gorm:"primary_key"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time } 比如我们定义User实体 type User struct{ gorm.Mo

weixin_44627989的博客 3760

SAP Business One自定义表与字段实战:从零构建企业专属数据模型

本文详细介绍了如何在SAP Business One中创建自定义表与字段,从零构建企业专属数据模型。通过实战案例和避坑指南,帮助用户掌握业务适配、流程优化和权限隔离等关键技巧,提升系统灵活性和数据管理效率。特别适合需要扩展标准功能的SAP Business One用户。

weixin_28730927的博客 362

YOLOv8改进 | Conv篇 | DWRSeg扩张式残差助力小目标检测 (附修改后的C2f+Bottleneck)

本文内容给大家带来的DWRSeg中的DWR模块来改进YOLOv8中的C2f和Bottleneck模块,主要针对的是小目标检测,主要创新点可以总结如下:多尺度特征提取机制的深入研究和创新的DWR模块和SIR模块的提出这种方法使得网络能够更灵活地适应不同尺度的特征,从而更准确地识别和分割图像中的物体。通过本文你能够了解到DWRSeg的基本原理和框架,并且能够在你自己的网络结构中进行添加(DWRSeg需要增加一定的计算量一个DWR模块大概增加0.4GFLOPs)。

Snu77的博客 9572

如何基于 GORM 实现 CreateOrUpdate 方法

注意,Init 和 Create 的区别,如果没有找到,这里会把实例给初始化,不会存入 DB,可以看到 RowsAffected == 0 分支的处理,这里并不会走 Create 的 callback 函数。需要注意的是,所谓 OnConflict,并不一定是主键冲突,唯一键也包含在内。所以,要实现 CreateOrUpdate,我们可以将需要 Update 的属性通过 Assign 函数放进来,随后如果通过 Where 找到了记录,也会将 Assign 属性应用上,随后 Update。

知识分享官 4913

FortiWeb 相对路径遍历漏洞复现(CVE-2025-64446)

FortiWeb是Fortinet公司推出的企业级Web应用防火墙(WAF),专为保护Web应用和API抵御高级威胁设计。它集成人工智能与机器学习技术,通过双层检测引擎(HMM模型与SVM向量机)精准识别异常流量,可防御OWASP Top 10攻击、SQL 注入、XSS、DDoS及恶意机器人。其核心功能包括API自动化保护、虚拟补丁集成(兼容Acunetix等第三方扫描工具)及智能流量分析,支持HTTP/2、WebSocket等现代协议。(CVE-2025-64446)

iSee857的博客 468

Golang与Gorm处理MySQL时区问题的实战解析

本文深入解析Golang与Gorm处理MySQL时区问题的实战方案,涵盖统一UTC时区、显式指定本地时区及数据库层解决方案。通过详细代码示例和常见坑点分析,帮助开发者高效解决datetime存储与显示的时区差异问题,特别适用于国际化应用和分布式系统开发。

weixin_26800111的博客 216

阿波罗开发板硬件架构与STM32模块化设计详解

嵌入式开发板的模块化设计是提升硬件复用性、降低迭代成本的关键技术路径。其核心在于底板与核心板的电气解耦与功能分层,依托STM32系列MCU(如F429/F767)的丰富外设资源与引脚复用能力,实现信号完整性、电源域隔离与热管理的系统级协同。该架构显著支撑工业HMI、智能家居网关、便携医疗设备等典型场景,尤其在RGB LCD驱动、QSPI Flash内存映射、多协议通信(CAN/RS485/USB)及低功耗唤醒(WK_UP按键)等工程实践中展现出高可靠性与易调试性。本文围绕阿波罗开发板展开深度解析,覆盖最小系

weixin_42510201的博客 64

GEE 案例:基于陆地卫星8号(大气层顶反射率数据)LANDSAT/LC08/C02/T1_TOA的生态遥感指数RSEI计算、可视化和下载

摘要:基于GEE的遥感生态指数(RSEI)计算方法 本文介绍了利用Google Earth Engine(GEE)平台计算遥感生态指数(RSEI)的全流程方法。主要内容包括:研究区域定义、Landsat8数据获取与去云预处理、关键生态指标计算(地表温度LST、植被指数NDVI、湿度指数WET和建筑指数NDBSI)、主成分分析(PCA)降维处理,以及结果可视化与数据导出。通过结合多源遥感指标并采用归一化处理,构建了综合反映区域生态环境质量的RSEI指数。该方法为生态环境监测提供了高效的技术方案,所有计算过程均

此星光明博客 463

Golang Gorm乐观锁实战:并发控制与数据一致性

乐观锁是一种高效的并发控制机制,通过版本号或时间戳实现数据修改的冲突检测,相比悲观锁能显著提升系统吞吐量。其核心原理是在事务提交时验证数据是否被其他事务修改,适用于读多写少的高并发场景,如电商库存管理、订单状态变更等。Gorm作为Golang生态的主流ORM框架,提供了开箱即用的乐观锁支持,开发者只需在模型中添加版本号字段并标记`gorm:"version"`标签即可实现自动冲突检测。在实际工程中,乐观锁需要配合事务隔离级别调优、冲突监控等策略,对于秒杀等高冲突场景可结合分布式锁形成混合解决方案。

weixin_34203832的博客 439

构建个体结构脑网络的另一种方法

这篇文献提出了一种构建个体结构脑网络的方法即Morphometric INverse Divergence (MIND),通过T1像数据即可对每个被试构建一个脑网络,且通过多个特征进行构建而非单一的特征。即可获得由4个特征得到的个体结构网络矩阵,可以根据自己的需求采用不同的特征组合得到网络矩阵,也可以结合T1/T2像得到髓鞘化相关的网络矩阵,后续可修改代码批量处理自己的数据。这是文献贴出来的代码。在终端直接克隆到本地。2.修改路径运行代码。

weixin_52073820的博客 824

一次插入10万条数据不卡顿,,揭秘Django bulk_create的底层机制与优化策略

解决大数据插入性能瓶颈,揭秘Django ORM bulk_create的批量提交机制。适用于日志、报表等场景,通过减少数据库交互实现10万条数据高效写入,显著提升执行效率,避免卡顿,值得收藏。

VarLens的博客 428

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Visage Bezier路径完全参考:填充、描边、虚线与线帽线连接一次讲清

Visage 是一个 GPU 加速的跨平台 C++ UI 与 2D 图形库,其 `Path` 模块是绘制 Bézier 路径的核心抽象。本文从入门到实战,一次讲清 Visage Bézier 路径的完整用法:如何用 `moveTo`、`bezierTo` 等指令构建曲线,如何用 `fill` 填充并选择填充规则,如何一行代码 `stroke` 描边,以及如何设置虚线 `dash`、线帽 `EndC

gitblog_00161的博客 404

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Databasus 后端开发规范详解:Go + Gin + GORM + PostgreSQL 的工程实践

本文系统讲解 Databasus 后端(Go + Gin + GORM + PostgreSQL 技术栈)的编码规范:从代码排版与注释纪律、文件组织、控制器与 Swagger 注解、依赖注入(DI)模式、后台服务守卫,到存储文件生命周期管理、数据库迁移约定、测试策略、日志规范与现代 Go 惯用法。读完本文,你将掌握该仓库后端代码"为什么长这样"的完整规则集,并能按同样的模式为 Gin/GORM 项

gitblog_00670的博客 438

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

4-数据库表语go中模型对应关系

本文摘要: GORM模型定义与CRUD操作指南详细介绍了使用GORM进行数据库开发的核心技术要点。主要内容包括:1)模型定义规范,通过结构体标签映射数据表,包含主键、默认值、索引等配置;2)自动迁移建表AutoMigrate的使用方法和限制;3)完整的CRUD操作,涵盖单条/批量插入、条件查询、更新策略(Save/Update/Updates)、软硬删除实现;4)关键开发技巧如事务处理、分页查询、连接池配置;5)常见坑点解决方案,包括表名复数问题、零值更新、钩子触发条件等。文档提供了大量可运行代码示例,特别

wtt234的专栏 262

Mysql乐观锁与悲观锁详解

本文对比分析了乐观锁与悲观锁两种并发控制机制。悲观锁通过SELECT...FOR UPDATE实现,适用于写操作频繁场景,保证强一致性但性能较差;乐观锁通过版本号字段实现,适用于读多写少场景,性能高但需处理冲突。文章结合Gin+GORM框架提供了两种锁的具体实现示例,并给出选型建议:资金类业务用悲观锁,高并发场景用乐观锁或Redis+MQ方案。最后详细介绍了Redis预减库存+消息队列的秒杀系统架构,通过Lua脚本原子操作、异步落库和对账机制解决高并发下的库存一致性问题。

资深系统架构,深耕高并发研发与 AI 大模型工程化落地。技术栈以 Python (AI/NLP/RAG/Agent) 与 Golang (高并发架构) 为双核心,兼具 PHP 大型应用经验。专注算法落地、复杂系统设计及团队技术赋能 1201

深入解析utron:Go语言轻量级MVC框架的设计哲学与实践指南

在Go语言的Web开发生态中,utron框架以其"轻量级MVC"的设计理念脱颖而出。不同于其他重量级框架,utron专注于提供核心的MVC功能,同时保持Go语言的原生特性和简洁性。本文将深入探讨utron的设计哲学、架构选择,并提供实际项目中的最佳实践方案。 ## 🔍 utron的核心设计哲学:简约而不简单 utron的诞生源于对Go语言原生精神的坚持。框架作者Geofrey Ernest在

gitblog_00324的博客 397

utron框架终极指南:快速构建高性能Go Web应用

utron是一个专为Go语言设计的轻量级MVC框架,致力于帮助开发者快速构建高性能、可扩展的数据库驱动Web应用程序。如果你正在寻找一个既能保持Go语言简洁特性,又能提供完整Web开发功能的框架,utron将是你的理想选择。 ## 为什么选择utron框架? 在众多Go Web框架中,utron以其独特的优势脱颖而出: **极简设计哲学** - 框架代码易于理解,一天内即可掌握全部核心概念

gitblog_00825的博客 472

深入Yii2 ActiveRecord:数据库操作的10个高效用法

掌握Yii2 ActiveRecord高效操作数据库的10种方法,提升PHP开发效率。本篇php yii框架教程涵盖查询构建、关联模型、批量处理等核心技巧,适用于REST API和后台系统开发,让代码更简洁安全。值得收藏

QuickProceed的博客 432
上一篇: 深入解析httpx.AsyncClient()的stream方法超时机制与实战应用
下一篇: 深入解析Java 7中javax.net.ssl.SSLHandshakeException的TLS协议兼容性问题
python9snake
博客等级 码龄1天 0粉丝 1724原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值