解决 GORM 操作 MySQL 时的 Error 1170 和 Error 1062 错误

目录

解决 GORM 操作 MySQL 时的 Error 1170 和 Error 1062 错误

一、问题背景

二、Error 1170:TEXT 字段索引问题

2.1 错误原因

2.2 解决方案

2.2.1 修改模型(Model)添加 GORM 标签

2.2.2 同步数据库表结构

三、Error 1062:唯一键冲突问题

3.1 错误原因

3.2 解决方案

3.2.1 测试数据唯一性处理

3.2.2 数据库层预防

四、完整修复流程总结

4.1 模型定义最佳实践

4.2 数据库表结构脚本

4.3 测试数据生成(存储过程)

五、总结


一、问题背景

在使用 GORM 操作 MySQL 数据库时,遇到了两个常见错误:

  1. Error 1170BLOB/TEXT column 'xxx' used in key specification without a key length
  2. Error 1062Duplicate entry 'xxx' for key 'xxx'

本文结合具体代码和场景,详细记录问题成因及解决方案。

二、Error 1170:TEXT 字段索引问题

2.1 错误原因

  • 现象:尝试对TEXTLONGTEXT类型字段创建索引,但 MySQL 不允许直接对这类大文本类型加索引(需指定索引长度)。
  • 代码场景
    • 数据库表中字段类型为VARCHAR,但 GORM 模型未正确声明类型,导致自动推断为TEXT
    • 例如:模型中Username字段未加gorm:"type:varchar(255)"标签,GORM 默认推断为TEXT

2.2 解决方案

2.2.1 修改模型(Model)添加 GORM 标签

type User struct {
    Username string `gorm:"type:varchar(255);uniqueIndex;not null" json:"username"` // 关键:指定varchar类型和唯一索引
    // 其他字段...
}

  • type:varchar(255):强制 GORM 使用VARCHAR类型,避免推断为TEXT
  • uniqueIndex:添加唯一索引约束(等价于数据库UNIQUE INDEX)。
2.2.2 同步数据库表结构

-- 删除旧表(如果存在)
DROP TABLE IF EXISTS users;

-- 创建新表(确保字段类型为VARCHAR)
CREATE TABLE IF NOT EXISTS users (
    username VARCHAR(255) NOT NULL UNIQUE, -- 与模型一致
    -- 其他字段...
    INDEX idx_username (username) -- 索引自动适配VARCHAR
) ENGINE=InnoDB;

三、Error 1062:唯一键冲突问题

3.1 错误原因

  • 现象:插入数据时违反唯一索引约束(如用户名、邮箱重复)。
  • 常见场景
    • 测试用例中使用固定用户名(如testuser),多次执行导致重复。
    • 数据库已有相同数据(如历史测试残留)。

3.2 解决方案

3.2.1 测试数据唯一性处理
  • 方案 1:生成随机用户名

    // 测试用例中动态生成唯一用户名
    username := fmt.Sprintf("testuser_%d", time.Now().UnixNano())
    
  • 方案 2:使用数据库事务回滚(测试环境)

    func TestUserDAO_Create(t *testing.T) {
        db := setupTestDB() // 开启事务
        defer db.Rollback() // 测试结束后自动回滚,不保存数据
        
        user := &model.User{Username: "testuser"}
        // 执行创建操作...
    }
    
3.2.2 数据库层预防
  • 在模型中声明唯一索引(同 Error 1170 解决方案),利用 GORM 提前校验唯一性:

    type User struct {
        Email string `gorm:"type:varchar(255);uniqueIndex;not null" json:"email"` // 邮箱唯一索引
        // 其他字段...
    }
    

四、完整修复流程总结

4.1 模型定义最佳实践

package model

import (
	"time"

	"gorm.io/gorm"
)

type User struct {
	ID          uint           `json:"id" gorm:"primaryKey"`
	Username    string         `json:"username" gorm:"type:varchar(255);uniqueIndex;not null"`
	Password    string         `json:"-" gorm:"type:varchar(255);not null"` // 存储bcrypt哈希(60字符)
	Email       string         `json:"email" gorm:"type:varchar(255);uniqueIndex;not null"`
	Role        string         `json:"role" gorm:"type:enum('admin','editor','user');default:user"`
	CreatedAt   time.Time      `json:"created_at" gorm:"autoCreateTime:milli"` // 自动生成创建时间(毫秒级)
	UpdatedAt   time.Time      `json:"updated_at" gorm:"autoUpdateTime:milli"` // 自动更新时间(毫秒级)
	DeletedAt   gorm.DeletedAt `json:"deleted_at" gorm:"index"` // 软删除字段(带索引)
}

4.2 数据库表结构脚本

CREATE DATABASE IF NOT EXISTS user_management DEFAULT CHARACTER SET utf8mb4;
USE user_management;

CREATE TABLE IF NOT EXISTS users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    role ENUM('admin', 'editor', 'user') DEFAULT 'user',
    created_at TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3),
    updated_at TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
    deleted_at TIMESTAMP(3) NULL,
    INDEX idx_deleted_at (deleted_at)
) ENGINE=InnoDB;

4.3 测试数据生成(存储过程)

DELIMITER $$
CREATE PROCEDURE GenerateUserData()
BEGIN
    DECLARE i INT DEFAULT 0;
    WHILE i < 100 DO
        INSERT INTO users (username, password, email, role) VALUES (
            CONCAT('user_', UUID()), -- 唯一用户名
            '$2a$10$DWbP8QH...', -- 预先生成的bcrypt哈希
            CONCAT('user_', i, '@example.com'),
            ELT(FLOOR(1 + RAND() * 3), 'admin', 'editor', 'user')
        );
        SET i = i + 1;
    END WHILE;
END$$
DELIMITER ;

五、总结

  1. GORM 标签的重要性:通过gorm:"type:xxx"显式指定字段类型,避免自动推断导致的类型不匹配(如stringTEXT)。
  2. 唯一约束的两层校验:模型层(GORM 标签)+ 数据库层(索引)共同确保数据唯一性,提前拦截冲突。
  3. 测试数据管理:测试中使用随机值或事务回滚,避免固定值引发的重复问题。

通过以上方案,可有效解决 GORM 操作 MySQL 时的索引和唯一键冲突问题,提升代码健壮性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值