GORM操作MSSQL查询获取的UUID值与数据库存储值不一致
问题
模型定义
type Invoice struct { ID uint `gorm:"primarykey" json:"-"` UID uuid.UUID `gorm:"type:UNIQUEIDENTIFIER;uniqueIndex:idx_UniqueID" json:"id"` }
数据库中存储的字段值如下图所示:
但通过GORM从数据库检索数据时,获取到的UID值与数据库内存储的原值存在差异,查询返回值如下图:
请问该问题是否是模型定义环节存在错误导致?相关接口处理handler代码如下:
func GetInvoices(c *fiber.Ctx) error { invoices := []responses.Invoice{} var invoicesCount int64 page, pageSize, searchString, orderBy := handles.Pages(c) switch orderBy { case "customer_name": orderBy = "CustomerName" case "currency_id": orderBy = "ForeignCurrenycID" default: orderBy = "ID" } if err := database.DB.Model(&models.Invoice{}).Select("ID").Where("InvNumber like ? or CustomerName like ? or Tax_Number like ? or Registration like ? or Description like ?", searchString, searchString, searchString, searchString, searchString).Count(&invoicesCount).Error; err != nil { logs.ErrorLogger.Println(err.Error()) return c.Status(400).JSON(fiber.Map{"msg": err.Error()}) } if err := database.DB.Scopes(handles.Paginate(c)).Order(orderBy).Find(&invoices).Where("InvNumber like ? or CustomerName like ? or Tax_Number like ? or Registration like ? or Description like ?", searchString, searchString, searchString, searchString, searchString).Error; err != nil { logs.ErrorLogger.Println(err.Error()) return c.Status(400).JSON(fiber.Map{"msg": err.Error()}) } for _, inv := range invoices{ fmt.Println(inv.UID) } totalPages := math.Ceil(float64(invoicesCount) / float64(pageSize)) return c.Status(200).JSON(fiber.Map{"page": page, "pageSize": pageSize, "totalPages": totalPages, "totalItems": invoicesCount, "data": &invoices}) }
回答
该问题不是模型定义环节错误导致,核心问题出在GORM查询逻辑的写法错误,直接触发了UID字段读取值与库内存储不一致的现象。
具体问题点
- GORM链式调用顺序错误,过滤条件完全未生效
现有数据查询逻辑写为.Order(orderBy).Find(&invoices).Where(...),GORM中Find属于终结执行方法,调用后会立刻生成SQL并执行查询,后面拼接的Where过滤条件根本不会被加入到本次查询的SQL中,相当于直接做了全表查询,返回的结果集和预期带过滤条件的结果完全不匹配,很容易出现记录对不上、UID不符的情况。
额外提一句,代码中currency_id映射的排序字段写为ForeignCurrenycID存在拼写错误,正确拼写应为ForeignCurrencyID,触发该排序规则时会报列不存在的SQL错误,可以一并修正。 - 查询未绑定正确模型,存在字段扫描错位风险
执行数据查询时,既没有调用Model(&models.Invoice{})指定关联的模型,也没有显式调用Table()指定表名,GORM会直接根据传入的[]responses.Invoice结构体推断表结构与字段映射关系。如果responses.Invoice的字段顺序、字段标签、字段类型和实际表结构、models.Invoice定义不完全对齐,就会出现数据库返回的字段值被扫描到错误结构体字段上的问题,直接表现就是UID这类字段读到的值和库内存储完全不符。
另外现有Count查询中写的.Select("ID")属于冗余写法,虽然不会影响计数结果,但没有实际存在的必要。
修复方案
- 调整GORM链式调用顺序,所有过滤、排序、分页条件必须放在
Find这类终结方法之前 - 查询时统一绑定
models.Invoice作为模型,避免表结构、字段映射错位,查询完成后手动将模型结构转换为接口响应需要的responses.Invoice结构 - 公共查询条件可以提前拼装复用,避免重复编写相同的Where逻辑
修正后的核心查询代码如下:
func GetInvoices(c *fiber.Ctx) error { page, pageSize, searchString, orderBy := handles.Pages(c) switch orderBy { case "customer_name": orderBy = "CustomerName" case "currency_id": // 修正原有拼写错误 orderBy = "ForeignCurrencyID" default: orderBy = "ID" } invoices := []responses.Invoice{} var invoicesCount int64 // 提前拼装公共查询条件,复用逻辑,减少重复代码 baseQuery := database.DB.Model(&models.Invoice{}).Where( "InvNumber like ? or CustomerName like ? or Tax_Number like ? or Registration like ? or Description like ?", searchString, searchString, searchString, searchString, searchString, ) // 计数查询复用基础条件,去掉冗余的Select("ID") if err := baseQuery.Count(&invoicesCount).Error; err != nil { logs.ErrorLogger.Println(err.Error()) return c.Status(400).JSON(fiber.Map{"msg": err.Error()}) } // 查询数据:所有条件放在Find之前,先查询模型结构,保证字段映射正确 var invoiceModels []models.Invoice if err := baseQuery.Scopes(handles.Paginate(c)).Order(orderBy).Find(&invoiceModels).Error; err != nil { logs.ErrorLogger.Println(err.Error()) return c.Status(400).JSON(fiber.Map{"msg": err.Error()}) } // 手动完成模型到响应结构的转换,彻底避免结构体自动映射带来的错位问题 for _, inv := range invoiceModels { fmt.Println(inv.UID) // 此处打印的UID会与数据库存储值完全一致 invoices = append(invoices, responses.Invoice{ UID: inv.UID, // 按照responses.Invoice的实际结构补充其余字段赋值 }) } totalPages := math.Ceil(float64(invoicesCount) / float64(pageSize)) return c.Status(200).JSON(fiber.Map{ "page": page, "pageSize": pageSize, "totalPages": totalPages, "totalItems": invoicesCount, "data": &invoices, }) }
补充说明:如果修正上述逻辑后仍存在UUID值异常,请确认项目使用的
uuid.UUID类型已正确实现sql.Scanner和driver.Valuer接口,主流的github.com/google/uuid库v1.1.0及以上版本已原生支持SQL Server的UNIQUEIDENTIFIER类型读写,不需要额外调整模型定义的标签配置。
内容的提问来源于stack exchange,提问作者Luqman Jr
相关产品推荐
相关产品推荐

