1 Star 1 Fork 1

c./goframe框架

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
gdb_core_structure.go 12.11 KB
一键复制 编辑 原始数据 按行查看 历史
// 版权归GoFrame作者(https://goframe.org)所有。保留所有权利。
//
// 本源代码形式受MIT许可证条款约束。
// 如果未随本文件一同分发MIT许可证副本,
// 您可以在https://github.com/gogf/gf处获取。
// md5:a9832f33b234e3f3
package db类
import (
"context"
"database/sql/driver"
"reflect"
"strings"
"time"
gbinary "gitee.com/go_888/goframe/encoding/gbinary"
gerror "gitee.com/go_888/goframe/errors/gerror"
"gitee.com/go_888/goframe/internal/intlog"
"gitee.com/go_888/goframe/internal/json"
gtime "gitee.com/go_888/goframe/os/gtime"
gregex "gitee.com/go_888/goframe/text/gregex"
gstr "gitee.com/go_888/goframe/text/gstr"
gconv "gitee.com/go_888/goframe/util/gconv"
gutil "gitee.com/go_888/goframe/util/gutil"
)
// X取字段类型 通过名称检索并返回指定字段的字段类型字符串。 md5:aeb8d310c854c45a
func (c *X结构_Core) X取字段类型(上下文 context.Context, 字段名称, 表名称, 数据库名称 string) string {
field := c.X取字段信息对象(上下文, 字段名称, 表名称, 数据库名称)
if field != nil {
return field.X类型
}
return ""
}
// X取字段信息对象 通过字段名获取并返回该字段的类型对象。 md5:eeebff59dbaf1064
func (c *X结构_Core) X取字段信息对象(上下文 context.Context, 字段名称, 表名称, 数据库名称 string) *X结构_TableField {
fieldsMap, err := c.db.X取表字段信息Map(上下文, 表名称, 数据库名称)
if err != nil {
intlog.Errorf(
上下文,
`获取表 "%s" 的字段信息失败,模式为 "%s": %+v`,
表名称, 数据库名称, err,
)
return nil
}
for tableFieldName, tableField := range fieldsMap {
if tableFieldName == 字段名称 {
return tableField
}
}
return nil
}
// X底层ConvertDataForRecord 是一个非常重要的函数,用于将任何数据转换为
// 以便将其作为记录插入到表或集合中。
//
// 参数 `value` 应为 *map/map/*struct/struct 类型。
// 对于结构体,它支持嵌入式结构体定义。
// md5:27b867ec3a1c3c1d
func (c *X结构_Core) X底层ConvertDataForRecord(上下文 context.Context, interface{}, 表名称 string) (map[string]interface{}, error) {
var (
err error
data = X转换到Map(, true)
)
for fieldName, fieldValue := range data {
data[fieldName], err = c.db.X底层ConvertValueForField(
上下文,
c.X取字段类型(上下文, fieldName, 表名称, c.X取默认数据库名称()),
fieldValue,
)
if err != nil {
return nil, gerror.X多层错误并格式化(err, `转换记录值失败: %#v`, fieldValue)
}
}
return data, nil
}
// X底层ConvertValueForField 将值转换为记录字段的类型。
// 参数 `fieldType` 是目标记录字段。
// 参数 `fieldValue` 是要写入记录字段的值。
// md5:196c02c9f6cf3380
func (c *X结构_Core) X底层ConvertValueForField(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) {
var (
err error
convertedValue = fieldValue
)
// 如果`value`实现了`driver.Valuer`接口,那么它会使用该接口进行值的转换。 md5:ba72a317b79988a2
if valuer, ok := fieldValue.(driver.Valuer); ok {
if convertedValue, err = valuer.Value(); err != nil {
if err != nil {
return nil, err
}
}
return convertedValue, nil
}
// 默认值转换。 md5:99c30401ccc62832
var (
rvValue = reflect.ValueOf(fieldValue)
rvKind = rvValue.Kind()
)
for rvKind == reflect.Ptr {
rvValue = rvValue.Elem()
rvKind = rvValue.Kind()
}
switch rvKind {
case reflect.Slice, reflect.Array, reflect.Map:
// 它应该忽略字节类型。 md5:48e040f1decb1a5e
if _, ok := fieldValue.([]byte); !ok {
// 将值转换为JSON。 md5:f4977a0ae972c910
convertedValue, err = json.Marshal(fieldValue)
if err != nil {
return nil, err
}
}
case reflect.Struct:
switch r := fieldValue.(type) {
// 如果时间是零值,它将更新为nil,
// 这样在数据库中插入或更新的值将会是"null"。
// md5:058aebae61025f37
case time.Time:
if r.IsZero() {
convertedValue = nil
}
case gtime.X结构_时间:
if r.IsZero() {
convertedValue = nil
} else {
convertedValue = r.Time
}
case *gtime.X结构_时间:
if r.IsZero() {
convertedValue = nil
} else {
convertedValue = r.Time
}
case *time.Time:
// Nothing to do.
case X结构_Counter, *X结构_Counter:
// Nothing to do.
default:
// 如果`value`实现了iNil接口,
// 检查其IsNil()函数,如果返回true,
// 将把该值插入/更新到数据库中作为"null"。
// md5:b2415061d93829e6
if v, ok := fieldValue.(iNil); ok && v.X是否为Nil() {
convertedValue = nil
} else if s, ok := fieldValue.(iString); ok {
// 默认使用字符串转换。 md5:36cba4c54f848f87
convertedValue = s.String()
} else {
// 将值转换为JSON。 md5:f4977a0ae972c910
convertedValue, err = json.Marshal(fieldValue)
if err != nil {
return nil, err
}
}
}
}
return convertedValue, nil
}
// X底层CheckLocalTypeForField 检查并返回与给定数据库类型相对应的本地类型。 md5:d3191e6393b7e531
func (c *X结构_Core) X底层CheckLocalTypeForField(ctx context.Context, fieldType string, fieldValue interface{}) (X类型_LocalType, error) {
var (
typeName string
typePattern string
)
match, _ := gregex.X匹配文本(`(.+?)\((.+)\)`, fieldType)
if len(match) == 3 {
typeName = gstr.X删首尾空与字符(match[1])
typePattern = gstr.X删首尾空与字符(match[2])
} else {
typeName = gstr.X分割(fieldType, " ")[0]
}
typeName = strings.ToLower(typeName)
switch typeName {
case
fieldTypeBinary,
fieldTypeVarbinary,
fieldTypeBlob,
fieldTypeTinyblob,
fieldTypeMediumblob,
fieldTypeLongblob:
return X常量_LocalTypeBytes, nil
case
fieldTypeInt,
fieldTypeTinyint,
fieldTypeSmallInt,
fieldTypeSmallint,
fieldTypeMediumInt,
fieldTypeMediumint,
fieldTypeSerial:
if gstr.X是否包含并忽略大小写(fieldType, "unsigned") {
return X常量_LocalTypeUint, nil
}
return X常量_LocalTypeInt, nil
case
fieldTypeBigInt,
fieldTypeBigint,
fieldTypeBigserial:
if gstr.X是否包含并忽略大小写(fieldType, "unsigned") {
return X常量_LocalTypeUint64, nil
}
return X常量_LocalTypeInt64, nil
case
fieldTypeReal:
return X常量_LocalTypeFloat32, nil
case
fieldTypeDecimal,
fieldTypeMoney,
fieldTypeNumeric,
fieldTypeSmallmoney:
return X常量_LocalTypeString, nil
case
fieldTypeFloat,
fieldTypeDouble:
return X常量_LocalTypeFloat64, nil
case
fieldTypeBit:
// 建议使用 bit(1) 作为布尔值。 md5:5be00c9e8395ea93
if typePattern == "1" {
return X常量_LocalTypeBool, nil
}
s := gconv.String(fieldValue)
// mssql 是一个true|false类型的字符串。 md5:6d4dbdb95d9adfa1
if strings.EqualFold(s, "true") || strings.EqualFold(s, "false") {
return X常量_LocalTypeBool, nil
}
if gstr.X是否包含并忽略大小写(fieldType, "unsigned") {
return X常量_LocalTypeUint64Bytes, nil
}
return X常量_LocalTypeInt64Bytes, nil
case
fieldTypeBool:
return X常量_LocalTypeBool, nil
case
fieldTypeDate:
return X常量_LocalTypeDate, nil
case
fieldTypeDatetime,
fieldTypeTimestamp,
fieldTypeTimestampz:
return X常量_LocalTypeDatetime, nil
case
fieldTypeJson:
return X常量_LocalTypeJson, nil
case
fieldTypeJsonb:
return X常量_LocalTypeJsonb, nil
default:
// 自动检测字段类型,通过键匹配。 md5:138e4aeac8d26d8a
switch {
case strings.Contains(typeName, "text") || strings.Contains(typeName, "char") || strings.Contains(typeName, "character"):
return X常量_LocalTypeString, nil
case strings.Contains(typeName, "float") || strings.Contains(typeName, "double") || strings.Contains(typeName, "numeric"):
return X常量_LocalTypeFloat64, nil
case strings.Contains(typeName, "bool"):
return X常量_LocalTypeBool, nil
case strings.Contains(typeName, "binary") || strings.Contains(typeName, "blob"):
return X常量_LocalTypeBytes, nil
case strings.Contains(typeName, "int"):
if gstr.X是否包含并忽略大小写(fieldType, "unsigned") {
return X常量_LocalTypeUint, nil
}
return X常量_LocalTypeInt, nil
case strings.Contains(typeName, "time"):
return X常量_LocalTypeDatetime, nil
case strings.Contains(typeName, "date"):
return X常量_LocalTypeDatetime, nil
default:
return X常量_LocalTypeString, nil
}
}
}
// X底层ConvertValueForLocal 根据从数据库中获取的字段类型名称,将值转换为Go语言中的本地类型。
// 参数 `fieldType` 为小写格式,例如:
// `float(5,2)`,`unsigned double(5,2)`,`decimal(10,2)`,`char(45)`,`varchar(100)` 等。
// md5:7e1ede2b68158e31
func (c *X结构_Core) X底层ConvertValueForLocal(
ctx context.Context, fieldType string, fieldValue interface{},
) (interface{}, error) {
// 如果没有获取到类型,则直接返回`fieldValue`,
// 利用其原始数据类型,因为`fieldValue`是`interface{}`类型的。
// md5:62cf4d391c9da4f2
if fieldType == "" {
return fieldValue, nil
}
typeName, err := c.db.X底层CheckLocalTypeForField(ctx, fieldType, fieldValue)
if err != nil {
return nil, err
}
switch typeName {
case X常量_LocalTypeBytes:
var typeNameStr = string(typeName)
if strings.Contains(typeNameStr, "binary") || strings.Contains(typeNameStr, "blob") {
return fieldValue, nil
}
return gconv.X取字节集(fieldValue), nil
case X常量_LocalTypeInt:
return gconv.X取整数(gconv.String(fieldValue)), nil
case X常量_LocalTypeUint:
return gconv.X取正整数(gconv.String(fieldValue)), nil
case X常量_LocalTypeInt64:
return gconv.X取整数64位(gconv.String(fieldValue)), nil
case X常量_LocalTypeUint64:
return gconv.X取正整数64位(gconv.String(fieldValue)), nil
case X常量_LocalTypeInt64Bytes:
return gbinary.BeDecodeToInt64(gconv.X取字节集(fieldValue)), nil
case X常量_LocalTypeUint64Bytes:
return gbinary.BeDecodeToUint64(gconv.X取字节集(fieldValue)), nil
case X常量_LocalTypeFloat32:
return gconv.X取小数32位(gconv.String(fieldValue)), nil
case X常量_LocalTypeFloat64:
return gconv.X取小数64位(gconv.String(fieldValue)), nil
case X常量_LocalTypeBool:
s := gconv.String(fieldValue)
// mssql 是一个true|false类型的字符串。 md5:6d4dbdb95d9adfa1
if strings.EqualFold(s, "true") {
return 1, nil
}
if strings.EqualFold(s, "false") {
return 0, nil
}
return gconv.X取布尔(fieldValue), nil
case X常量_LocalTypeDate:
// Date without time.
if t, ok := fieldValue.(time.Time); ok {
return gtime.X创建并按Time(t).X取格式文本("Y-m-d"), nil
}
t, _ := gtime.X到时间(gconv.String(fieldValue))
return t.X取格式文本("Y-m-d"), nil
case X常量_LocalTypeDatetime:
if t, ok := fieldValue.(time.Time); ok {
return gtime.X创建并按Time(t), nil
}
t, _ := gtime.X到时间(gconv.String(fieldValue))
return t, nil
default:
return gconv.String(fieldValue), nil
}
}
// mappingAndFilterData 自动将映射键映射到表格字段,并删除所有不是给定表格字段的键值对。
// md5:27fc8e27d4d4a389
func (c *X结构_Core) mappingAndFilterData(ctx context.Context, schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) {
fieldsMap, err := c.db.X取表字段信息Map(ctx, c.guessPrimaryTableName(table), schema)
if err != nil {
return nil, err
}
fieldsKeyMap := make(map[string]interface{}, len(fieldsMap))
for k := range fieldsMap {
fieldsKeyMap[k] = nil
}
// 自动将数据键映射到表格字段名。 md5:bdc9aa8a688bb975
var foundKey string
for dataKey, dataValue := range data {
if _, ok := fieldsKeyMap[dataKey]; !ok {
foundKey, _ = gutil.Map查找并忽略大小写与符号(fieldsKeyMap, dataKey)
if foundKey != "" {
if _, ok = data[foundKey]; !ok {
data[foundKey] = dataValue
}
delete(data, dataKey)
}
}
}
// 数据过滤。
// 它会删除所有具有错误字段名的键值对。
// md5:24aafcb1699db80c
if filter {
for dataKey := range data {
if _, ok := fieldsMap[dataKey]; !ok {
delete(data, dataKey)
}
}
}
return data, nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/go_888/goframe.git
git@gitee.com:go_888/goframe.git
go_888
goframe
goframe框架
782a3f7170cf

搜索帮助