字段类型的推导原理摘要本文详细解析了 GORM 中字段类型映射到数据库类型的完整推导过程。核心结论是当 Go 结构体字段为int类型且未显式指定size标签时GORM 会根据其Kind推导出Size64最终在 MySQL 中映射为bigint。推导的优先级为1) 显式SIZE标签2) 根据 Go 类型推导DataType3) 显式TYPE标签可能覆盖DataType4) 若Size仍为 0则根据 Go 类型推导Size。最终DataType和Size共同决定数据库中的具体类型如int、bigint。为什么我PeopleModel.Age在gorm标签里写的type:int,为什么生成的表是bigint? 首先gorm会根据数据类型DataType和长度Size判断这个字段在数据库表里应该用什么类型。1.没有指定sizegorm会根据DataType进行反推,源码如下go里int对应的size64对应到mysql里就是bigintiffield.Size0{switchreflect.Indirect(fieldValue).Kind(){casereflect.Int,reflect.Int64,reflect.Uint,reflect.Uint64,reflect.Float64:field.Size64casereflect.Int8,reflect.Uint8:field.Size8casereflect.Int16,reflect.Uint16:field.Size16casereflect.Int32,reflect.Uint32,reflect.Float32:field.Size32}}2.指定了Size,比如:Ageintgorm:size:32,而int32这个长度在mysql里对应int.综上Size的来源有两种:标签size显式指定、gorm根据反射推导。 ┌────────┬────────────────────────┬────────────────────────────────────────────┐ │ 优先级 │ 来源 │ 例子 │ ├────────┼────────────────────────┼────────────────────────────────────────────┤ │1│ SIZE 标签 │ gorm:size:32→ field.Size32│ ├────────┼────────────────────────┼────────────────────────────────────────────┤ │2│ Go 类型的 reflect.Kind │int→64,int32→32,int16→16,int8→8│ └────────┴────────────────────────┴────────────────────────────────────────────┘3.field.Size 的确定 SIZE 标签存在 ├─ 是 → 用 SIZE 标签的值 └─ 否 → 根据 Go 类型推断int/int64/uint/uint64/float64→64int8/uint8→8int16/uint16→16int32/uint32/float32→32field.DataType 的确定 TYPE 标签存在 ├─ 是 → 匹配 schema 常量int/uint/bool/float/string/time/bytes │ ├─ 是 → 用该常量后续走GORM 内部映射会参考 Size │ └─ 否 → 当自定义类型原样透传给数据库相当于跳过了gorm └─ 否 → 根据 Go 类型推断int→Int,string→String...4.当datatype和size都没有gorm先推导哪个1.读 SIZE 标签 → field.Size2.推导 Go 类型 → field.DataType3.读 TYPE 标签覆盖 → field.DataType4.Size 还是0 → 根据 Go 类型推导 field.Size5.既然先datatype后size那么size的推导是根据字段后的类型还是datatype 是根据字段后的类型源码如图iffield.Size0{switchreflect.Indirect(fieldValue).Kind(){// ← 看的是 Go 类型casereflect.Int,reflect.Int64,reflect.Uint,reflect.Uint64,reflect.Float64:field.Size64casereflect.Int8,reflect.Uint8:field.Size8casereflect.Int16,reflect.Uint16:field.Size16casereflect.Int32,reflect.Uint32,reflect.Float32:field.Size32}}//根据type标签得到DataTypeifval,ok:field.TagSettings[TYPE];ok{lowerVal:DataType(strings.ToLower(val))switchlowerVal{caseBool,Int,Uint,Float,String,Time,Bytes:field.DataTypelowerVal// ← 匹配到常量走这里default:field.DataTypeDataType(val)// ← 没匹配到走这里}}//field 第 233 行如果没写type,会根据 Go 类型推导 DataTypeswitchreflect.Indirect(fieldValue).Kind(){casereflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64:field.DataTypeInt...}//根据DataType选择对应的类型switchfield.DataType{caseschema.Bool:returnbooleancaseschema.Int,schema.Uint:returndialector.getSchemaIntAndUnitType(field)caseschema.Float:returndialector.getSchemaFloatType(field)caseschema.String:returndialector.getSchemaStringType(field)caseschema.Time:returndialector.getSchemaTimeType(field)caseschema.Bytes:returndialector.getSchemaBytesType(field)default:returndialector.getSchemaCustomType(field)//里面是sqltype : string(field.DataType)}//根据Size的大小选择对应的类型长度func(dialector Dialector)getSchemaIntAndUnitType(field*schema.Field)string{switch{casefield.Size8:returnconstraint(tinyint)casefield.Size16:returnconstraint(smallint)casefield.Size24:returnconstraint(mediumint)casefield.Size32:returnconstraint(int)default:returnconstraint(bigint)// ← 你的 Age 走到了这里}}
GORM的字段类型推导源码解析
发布时间:2026/7/5 2:10:29
字段类型的推导原理摘要本文详细解析了 GORM 中字段类型映射到数据库类型的完整推导过程。核心结论是当 Go 结构体字段为int类型且未显式指定size标签时GORM 会根据其Kind推导出Size64最终在 MySQL 中映射为bigint。推导的优先级为1) 显式SIZE标签2) 根据 Go 类型推导DataType3) 显式TYPE标签可能覆盖DataType4) 若Size仍为 0则根据 Go 类型推导Size。最终DataType和Size共同决定数据库中的具体类型如int、bigint。为什么我PeopleModel.Age在gorm标签里写的type:int,为什么生成的表是bigint? 首先gorm会根据数据类型DataType和长度Size判断这个字段在数据库表里应该用什么类型。1.没有指定sizegorm会根据DataType进行反推,源码如下go里int对应的size64对应到mysql里就是bigintiffield.Size0{switchreflect.Indirect(fieldValue).Kind(){casereflect.Int,reflect.Int64,reflect.Uint,reflect.Uint64,reflect.Float64:field.Size64casereflect.Int8,reflect.Uint8:field.Size8casereflect.Int16,reflect.Uint16:field.Size16casereflect.Int32,reflect.Uint32,reflect.Float32:field.Size32}}2.指定了Size,比如:Ageintgorm:size:32,而int32这个长度在mysql里对应int.综上Size的来源有两种:标签size显式指定、gorm根据反射推导。 ┌────────┬────────────────────────┬────────────────────────────────────────────┐ │ 优先级 │ 来源 │ 例子 │ ├────────┼────────────────────────┼────────────────────────────────────────────┤ │1│ SIZE 标签 │ gorm:size:32→ field.Size32│ ├────────┼────────────────────────┼────────────────────────────────────────────┤ │2│ Go 类型的 reflect.Kind │int→64,int32→32,int16→16,int8→8│ └────────┴────────────────────────┴────────────────────────────────────────────┘3.field.Size 的确定 SIZE 标签存在 ├─ 是 → 用 SIZE 标签的值 └─ 否 → 根据 Go 类型推断int/int64/uint/uint64/float64→64int8/uint8→8int16/uint16→16int32/uint32/float32→32field.DataType 的确定 TYPE 标签存在 ├─ 是 → 匹配 schema 常量int/uint/bool/float/string/time/bytes │ ├─ 是 → 用该常量后续走GORM 内部映射会参考 Size │ └─ 否 → 当自定义类型原样透传给数据库相当于跳过了gorm └─ 否 → 根据 Go 类型推断int→Int,string→String...4.当datatype和size都没有gorm先推导哪个1.读 SIZE 标签 → field.Size2.推导 Go 类型 → field.DataType3.读 TYPE 标签覆盖 → field.DataType4.Size 还是0 → 根据 Go 类型推导 field.Size5.既然先datatype后size那么size的推导是根据字段后的类型还是datatype 是根据字段后的类型源码如图iffield.Size0{switchreflect.Indirect(fieldValue).Kind(){// ← 看的是 Go 类型casereflect.Int,reflect.Int64,reflect.Uint,reflect.Uint64,reflect.Float64:field.Size64casereflect.Int8,reflect.Uint8:field.Size8casereflect.Int16,reflect.Uint16:field.Size16casereflect.Int32,reflect.Uint32,reflect.Float32:field.Size32}}//根据type标签得到DataTypeifval,ok:field.TagSettings[TYPE];ok{lowerVal:DataType(strings.ToLower(val))switchlowerVal{caseBool,Int,Uint,Float,String,Time,Bytes:field.DataTypelowerVal// ← 匹配到常量走这里default:field.DataTypeDataType(val)// ← 没匹配到走这里}}//field 第 233 行如果没写type,会根据 Go 类型推导 DataTypeswitchreflect.Indirect(fieldValue).Kind(){casereflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64:field.DataTypeInt...}//根据DataType选择对应的类型switchfield.DataType{caseschema.Bool:returnbooleancaseschema.Int,schema.Uint:returndialector.getSchemaIntAndUnitType(field)caseschema.Float:returndialector.getSchemaFloatType(field)caseschema.String:returndialector.getSchemaStringType(field)caseschema.Time:returndialector.getSchemaTimeType(field)caseschema.Bytes:returndialector.getSchemaBytesType(field)default:returndialector.getSchemaCustomType(field)//里面是sqltype : string(field.DataType)}//根据Size的大小选择对应的类型长度func(dialector Dialector)getSchemaIntAndUnitType(field*schema.Field)string{switch{casefield.Size8:returnconstraint(tinyint)casefield.Size16:returnconstraint(smallint)casefield.Size24:returnconstraint(mediumint)casefield.Size32:returnconstraint(int)default:returnconstraint(bigint)// ← 你的 Age 走到了这里}}