From e267e77c19fbca255bce4a5838a80cd2e5b8dcd0 Mon Sep 17 00:00:00 2001 From: Royal Date: Tue, 26 Nov 2024 16:58:39 +0800 Subject: [PATCH 1/3] Add New Feature: AutoMigrate Automatically Generates Corresponding Table Structure Based on Model Struct Fields --- database/gdb/gdb_model_create_table.go | 279 +++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 database/gdb/gdb_model_create_table.go diff --git a/database/gdb/gdb_model_create_table.go b/database/gdb/gdb_model_create_table.go new file mode 100644 index 00000000000..413ec80fd9a --- /dev/null +++ b/database/gdb/gdb_model_create_table.go @@ -0,0 +1,279 @@ +package gdb + +import ( + "context" + "fmt" + "github.com/gogf/gf/v2/text/gstr" + "log" + "reflect" +) + +// 不同库对应 golang字段类型与数据库类型映射 +var typeMapping = map[string]map[reflect.Kind]string{ + "mysql": { + reflect.Int: "INT", + reflect.Int8: "TINYINT", + reflect.Int16: "SMALLINT", + reflect.Int32: "INT", + reflect.Int64: "BIGINT", + reflect.Uint: "INT", + reflect.Uint8: "TINYINT", + reflect.Uint16: "SMALLINT", + reflect.Uint32: "INT", + reflect.Uint64: "BIGINT", + reflect.Float32: "FLOAT", + reflect.Float64: "DOUBLE", + reflect.String: "VARCHAR(255)", + reflect.Bool: "BOOLEAN", + reflect.Ptr: "DATETIME", // 假设是指向时间类型的指针 + }, + "mariadb": { + reflect.Int: "INT", + reflect.Int8: "TINYINT", + reflect.Int16: "SMALLINT", + reflect.Int32: "INT", + reflect.Int64: "BIGINT", + reflect.Uint: "INT", + reflect.Uint8: "TINYINT", + reflect.Uint16: "SMALLINT", + reflect.Uint32: "INT", + reflect.Uint64: "BIGINT", + reflect.Float32: "FLOAT", + reflect.Float64: "DOUBLE", // MariaDB 支持 DOUBLE + reflect.String: "VARCHAR(255)", + reflect.Bool: "BOOLEAN", + reflect.Ptr: "DATETIME", // 假设是指向时间类型的指针 + }, + "tidb": { + reflect.Int: "INT", + reflect.Int8: "TINYINT", + reflect.Int16: "SMALLINT", + reflect.Int32: "INT", + reflect.Int64: "BIGINT", + reflect.Uint: "INT", + reflect.Uint8: "TINYINT", + reflect.Uint16: "SMALLINT", + reflect.Uint32: "INT", + reflect.Uint64: "BIGINT", + reflect.Float32: "FLOAT", + reflect.Float64: "DOUBLE", // TiDB 支持 DOUBLE + reflect.String: "VARCHAR(255)", + reflect.Bool: "BOOLEAN", + reflect.Ptr: "DATETIME", // 假设是指向时间类型的指针 + }, + "mssql": { + reflect.Int: "INT", + reflect.Int8: "TINYINT", + reflect.Int16: "SMALLINT", + reflect.Int32: "INT", + reflect.Int64: "BIGINT", + reflect.Uint: "INT", + reflect.Uint8: "TINYINT", + reflect.Uint16: "SMALLINT", + reflect.Uint32: "INT", + reflect.Uint64: "BIGINT", + reflect.Float32: "FLOAT", + reflect.Float64: "FLOAT", // MSSQL 支持 FLOAT + reflect.String: "NVARCHAR(255)", + reflect.Bool: "BIT", + reflect.Ptr: "DATETIME2", // 假设是指向时间类型的指针 + }, + "sqlite": { + reflect.Int: "INTEGER", + reflect.Int8: "INTEGER", + reflect.Int16: "INTEGER", + reflect.Int32: "INTEGER", + reflect.Int64: "INTEGER", + reflect.Uint: "INTEGER", + reflect.Uint8: "INTEGER", + reflect.Uint16: "INTEGER", + reflect.Uint32: "INTEGER", + reflect.Uint64: "INTEGER", + reflect.Float32: "REAL", + reflect.Float64: "REAL", // SQLite 支持 REAL + reflect.String: "TEXT", + reflect.Bool: "INTEGER", + reflect.Ptr: "DATETIME", // 假设是指向时间类型的指针 + }, + "oracle": { + reflect.Int: "NUMBER", + reflect.Int8: "NUMBER", + reflect.Int16: "NUMBER", + reflect.Int32: "NUMBER", + reflect.Int64: "NUMBER", + reflect.Uint: "NUMBER", + reflect.Uint8: "NUMBER", + reflect.Uint16: "NUMBER", + reflect.Uint32: "NUMBER", + reflect.Uint64: "NUMBER", + reflect.Float32: "FLOAT", + reflect.Float64: "NUMBER", // Oracle 支持 NUMBER + reflect.String: "VARCHAR2(255)", + reflect.Bool: "NUMBER(1)", + reflect.Ptr: "TIMESTAMP", // 假设是指向时间类型的指针 + }, + "dm": { + reflect.Int: "INTEGER", + reflect.Int8: "TINYINT", + reflect.Int16: "SMALLINT", + reflect.Int32: "INTEGER", + reflect.Int64: "BIGINT", + reflect.Uint: "INTEGER", + reflect.Uint8: "TINYINT", + reflect.Uint16: "SMALLINT", + reflect.Uint32: "INTEGER", + reflect.Uint64: "BIGINT", + reflect.Float32: "FLOAT", + reflect.Float64: "DOUBLE", // DM 支持 DOUBLE + reflect.String: "VARCHAR(255)", + reflect.Bool: "BOOLEAN", + reflect.Ptr: "TIMESTAMP", // 假设是指向时间类型的指针 + }, + "duckdb": { + reflect.Int: "INTEGER", + reflect.Int8: "TINYINT", + reflect.Int16: "SMALLINT", + reflect.Int32: "INTEGER", + reflect.Int64: "BIGINT", + reflect.Uint: "INTEGER", + reflect.Uint8: "TINYINT", + reflect.Uint16: "SMALLINT", + reflect.Uint32: "INTEGER", + reflect.Uint64: "BIGINT", + reflect.Float32: "REAL", + reflect.Float64: "DOUBLE", + reflect.String: "VARCHAR(255)", + reflect.Bool: "BOOLEAN", + reflect.Ptr: "TIMESTAMP", // 假设是指向时间类型的指针 + }, + "pgsql": { + reflect.Int: "INTEGER", + reflect.Int8: "SMALLINT", + reflect.Int16: "SMALLINT", + reflect.Int32: "INTEGER", + reflect.Int64: "BIGINT", + reflect.Uint: "INTEGER", + reflect.Uint8: "SMALLINT", + reflect.Uint16: "SMALLINT", + reflect.Uint32: "INTEGER", + reflect.Uint64: "BIGINT", + reflect.Float32: "REAL", + reflect.Float64: "DOUBLE PRECISION", + reflect.String: "VARCHAR(255)", + reflect.Bool: "BOOLEAN", + reflect.Ptr: "TIMESTAMPTZ", // 假设是指向时间类型的指针 + }, + "clickhouse": { + reflect.Int: "Int32", + reflect.Int8: "Int8", + reflect.Int16: "Int16", + reflect.Int32: "Int32", + reflect.Int64: "Int64", + reflect.Uint: "UInt32", + reflect.Uint8: "UInt8", + reflect.Uint16: "UInt16", + reflect.Uint32: "UInt32", + reflect.Uint64: "UInt64", + reflect.Float32: "Float32", + reflect.Float64: "Float64", + reflect.String: "String", + reflect.Bool: "UInt8", + reflect.Ptr: "DateTime", // 假设是指向时间类型的指针 + }, +} + +// 自增字段语法映射 +var autoIncrementMapping = map[string]string{ + "mysql": "AUTO_INCREMENT", + "mariadb": "AUTO_INCREMENT", + "tidb": "AUTO_INCREMENT", + "mssql": "IDENTITY(1,1)", + "sqlite": "AUTOINCREMENT", + "oracle": "GENERATED BY DEFAULT AS IDENTITY", + "dm": "AUTO_INCREMENT", + "duckdb": "", // 内存库,不支持传统意义上的自增 + "pgsql": "SERIAL", //最新版本psql 使用BIGSERIAL + "clickhouse": "AUTO_INCREMENT", +} + +// AutoMigrate 自动迁移模型到数据库。 +// 该方法根据模型的结构体字段自动生成对应的数据库表结构。 +// 参数: +// +// ctx - 上下文,用于传递请求范围的信息。 +// model - 需要进行迁移的模型结构体。 +// +// 返回值: +// +// 如果迁移过程中发生错误,则返回错误。 +func (m *Model) AutoMigrate(ctx context.Context, model interface{}) error { + // 获取结构体的字段信息 + t := reflect.TypeOf(model) + if t.Kind() != reflect.Struct { + return fmt.Errorf("model must be a struct") + } + // 获取数据库类型 + dbType := m.db.GetCore().config.Type + + //获取字段类型映射 + typeMap, ok := typeMapping[dbType] + if !ok { + return fmt.Errorf("unsupported database type: %s", dbType) + } + + //获取自增字段语法映射 + autoIncrement, ok := autoIncrementMapping[dbType] + if !ok { + return fmt.Errorf("unsupported database type for auto increment: %s", dbType) + } + + var columns []string + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + // 将字段名称转换为小写并用下划线分隔 + fieldName := gstr.CaseSnake(field.Name) + //如果存在 orm 中的配置 则取orm中配置的字段名 + ormTag := field.Tag.Get("orm") + if ormTag != "" { + fieldName = ormTag + } + + //使用默认的字段类型映射 + fieldType, ok := typeMap[field.Type.Kind()] + if !ok { + return fmt.Errorf("unsupported field type: %s", field.Type.Kind()) + } + + // 构建列定义 + columnDef := fmt.Sprintf(" %s %s", m.db.GetCore().QuoteString(fieldName), fieldType) + + // 添加默认选项 + if field.Type.Kind() == reflect.String { + columnDef += " NOT NULL DEFAULT ''" + } else if field.Type.Kind() == reflect.Bool { + columnDef += " NOT NULL DEFAULT FALSE" + } + + columns = append(columns, columnDef) + } + + //默认给 id 字段设置为主键,orm标签目前是配置的字段,后续考虑通过配置orm标签扩展主键自增 + for i, column := range columns { + if gstr.Contains(column, "id") { + columns[i] = column + " PRIMARY KEY UNIQUE " + autoIncrement + break + } + } + + // 生成创建表的 SQL 语句 + sql := fmt.Sprintf("CREATE TABLE %s (%s)", m.db.GetCore().QuotePrefixTableName(m.tables), gstr.Join(columns, ", ")) + + _, err := m.db.Exec(ctx, sql) + if err != nil { + return err + } else { + log.Printf("生成表:%s---成功", m.tables) + } + + return nil +} From 200ddb1044df45ad0bf8b2250ec5a419636e2f8c Mon Sep 17 00:00:00 2001 From: Royal Date: Tue, 26 Nov 2024 17:02:47 +0800 Subject: [PATCH 2/3] Add New Feature: AutoMigrate Automatically Generates Corresponding Table Structure Based on Model Struct Fields --- database/gdb/gdb_model_create_table.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_model_create_table.go b/database/gdb/gdb_model_create_table.go index 413ec80fd9a..d77da5f93fa 100644 --- a/database/gdb/gdb_model_create_table.go +++ b/database/gdb/gdb_model_create_table.go @@ -3,9 +3,10 @@ package gdb import ( "context" "fmt" - "github.com/gogf/gf/v2/text/gstr" "log" "reflect" + + "github.com/gogf/gf/v2/text/gstr" ) // 不同库对应 golang字段类型与数据库类型映射 From 2c34879115f63ad5cb47cb9621dba8502164c95e Mon Sep 17 00:00:00 2001 From: Royal Date: Tue, 26 Nov 2024 17:23:46 +0800 Subject: [PATCH 3/3] commentFormatting: put a space between `//` and comment text --- database/gdb/gdb_model_create_table.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/database/gdb/gdb_model_create_table.go b/database/gdb/gdb_model_create_table.go index d77da5f93fa..f8b91e0b706 100644 --- a/database/gdb/gdb_model_create_table.go +++ b/database/gdb/gdb_model_create_table.go @@ -193,7 +193,7 @@ var autoIncrementMapping = map[string]string{ "oracle": "GENERATED BY DEFAULT AS IDENTITY", "dm": "AUTO_INCREMENT", "duckdb": "", // 内存库,不支持传统意义上的自增 - "pgsql": "SERIAL", //最新版本psql 使用BIGSERIAL + "pgsql": "SERIAL", // 最新版本pgsql 使用BIGSERIAL "clickhouse": "AUTO_INCREMENT", } @@ -216,13 +216,13 @@ func (m *Model) AutoMigrate(ctx context.Context, model interface{}) error { // 获取数据库类型 dbType := m.db.GetCore().config.Type - //获取字段类型映射 + // 获取字段类型映射 typeMap, ok := typeMapping[dbType] if !ok { return fmt.Errorf("unsupported database type: %s", dbType) } - //获取自增字段语法映射 + // 获取自增字段语法映射 autoIncrement, ok := autoIncrementMapping[dbType] if !ok { return fmt.Errorf("unsupported database type for auto increment: %s", dbType) @@ -233,13 +233,13 @@ func (m *Model) AutoMigrate(ctx context.Context, model interface{}) error { field := t.Field(i) // 将字段名称转换为小写并用下划线分隔 fieldName := gstr.CaseSnake(field.Name) - //如果存在 orm 中的配置 则取orm中配置的字段名 + // 如果存在 orm 中的配置 则取orm中配置的字段名 ormTag := field.Tag.Get("orm") if ormTag != "" { fieldName = ormTag } - //使用默认的字段类型映射 + // 使用默认的字段类型映射 fieldType, ok := typeMap[field.Type.Kind()] if !ok { return fmt.Errorf("unsupported field type: %s", field.Type.Kind()) @@ -258,7 +258,7 @@ func (m *Model) AutoMigrate(ctx context.Context, model interface{}) error { columns = append(columns, columnDef) } - //默认给 id 字段设置为主键,orm标签目前是配置的字段,后续考虑通过配置orm标签扩展主键自增 + // 默认给 id 字段设置为主键,orm标签目前是配置的字段,后续考虑通过配置orm标签扩展主键自增 for i, column := range columns { if gstr.Contains(column, "id") { columns[i] = column + " PRIMARY KEY UNIQUE " + autoIncrement