diff --git a/tools/goctl/model/sql/README.MD b/tools/goctl/model/sql/README.MD index 97ec1226..46af9ee7 100644 --- a/tools/goctl/model/sql/README.MD +++ b/tools/goctl/model/sql/README.MD @@ -78,8 +78,8 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m Password string `db:"password"` // 用户密码 Mobile string `db:"mobile"` // 手机号 Gender string `db:"gender"` // 男|女|未公开 - Nickname string `db:"nickname"` // 用户昵称 - CreateTime time.Time `db:"create_time"` + Nickname sql.NullString `db:"nickname"` // 用户昵称 + CreateTime sql.NullTime `db:"create_time"` UpdateTime time.Time `db:"update_time"` } ) @@ -347,3 +347,33 @@ OPTIONS: 目前,我认为除了基本的CURD外,其他的代码均属于业务型代码,这个我觉得开发人员根据业务需要进行编写更好。 +# 类型转换规则 +| mysql dataType | golang dataType | golang dataType(if null&&default null) | +|----------------|-----------------|----------------------------------------| +| bool | int64 | sql.NullInt64 | +| boolean | int64 | sql.NullInt64 | +| tinyint | int64 | sql.NullInt64 | +| smallint | int64 | sql.NullInt64 | +| mediumint | int64 | sql.NullInt64 | +| int | int64 | sql.NullInt64 | +| integer | int64 | sql.NullInt64 | +| bigint | int64 | sql.NullInt64 | +| float | float64 | sql.NullFloat64 | +| double | float64 | sql.NullFloat64 | +| decimal | float64 | sql.NullFloat64 | +| date | time.Time | sql.NullTime | +| datetime | time.Time | sql.NullTime | +| timestamp | time.Time | sql.NullTime | +| time | string | sql.NullString | +| year | time.Time | sql.NullInt64 | +| char | string | sql.NullString | +| varchar | string | sql.NullString | +| binary | string | sql.NullString | +| varbinary | string | sql.NullString | +| tinytext | string | sql.NullString | +| text | string | sql.NullString | +| mediumtext | string | sql.NullString | +| longtext | string | sql.NullString | +| enum | string | sql.NullString | +| set | string | sql.NullString | +| json | string | sql.NullString | \ No newline at end of file diff --git a/tools/goctl/model/sql/converter/types.go b/tools/goctl/model/sql/converter/types.go index a1b1a175..c052d0ec 100644 --- a/tools/goctl/model/sql/converter/types.go +++ b/tools/goctl/model/sql/converter/types.go @@ -41,12 +41,34 @@ var ( } ) -func ConvertDataType(dataBaseType string) (goDataType string, err error) { +func ConvertDataType(dataBaseType string, isDefaultNull bool) (string, error) { tp, ok := commonMysqlDataTypeMap[strings.ToLower(dataBaseType)] if !ok { - err = fmt.Errorf("unexpected database type: %s", dataBaseType) - return + return "", fmt.Errorf("unexpected database type: %s", dataBaseType) + } + + return mayConvertNullType(tp, isDefaultNull), nil +} + +func mayConvertNullType(goDataType string, isDefaultNull bool) string { + if !isDefaultNull { + return goDataType + } + + switch goDataType { + case "int64": + return "sql.NullInt64" + case "int32": + return "sql.NullInt32" + case "float64": + return "sql.NullFloat64" + case "bool": + return "sql.NullBool" + case "string": + return "sql.NullString" + case "time.Time": + return "sql.NullTime" + default: + return goDataType } - goDataType = tp - return } diff --git a/tools/goctl/model/sql/converter/types_test.go b/tools/goctl/model/sql/converter/types_test.go index 09ac6175..8c2122dd 100644 --- a/tools/goctl/model/sql/converter/types_test.go +++ b/tools/goctl/model/sql/converter/types_test.go @@ -7,14 +7,22 @@ import ( ) func TestConvertDataType(t *testing.T) { - v, err := ConvertDataType("tinyint") + v, err := ConvertDataType("tinyint", false) assert.Nil(t, err) assert.Equal(t, "int64", v) - v, err = ConvertDataType("timestamp") + v, err = ConvertDataType("tinyint", true) + assert.Nil(t, err) + assert.Equal(t, "sql.NullInt64", v) + + v, err = ConvertDataType("timestamp", false) assert.Nil(t, err) assert.Equal(t, "time.Time", v) - _, err = ConvertDataType("float32") + v, err = ConvertDataType("timestamp", true) + assert.Nil(t, err) + assert.Equal(t, "sql.NullTime", v) + + _, err = ConvertDataType("float32", false) assert.NotNil(t, err) } diff --git a/tools/goctl/model/sql/example/sql/user.sql b/tools/goctl/model/sql/example/sql/user.sql index 0fef6180..e6640c46 100644 --- a/tools/goctl/model/sql/example/sql/user.sql +++ b/tools/goctl/model/sql/example/sql/user.sql @@ -15,3 +15,12 @@ CREATE TABLE `user` ( UNIQUE KEY `mobile_index` (`mobile`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +CREATE TABLE `student` ( + `id` bigint NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '', + `age` tinyint DEFAULT NULL, + `score` float(10,0) DEFAULT NULL, + `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; \ No newline at end of file diff --git a/tools/goctl/model/sql/example/sql/user_1.sql b/tools/goctl/model/sql/example/sql/user_1.sql deleted file mode 100644 index 0ecc666a..00000000 --- a/tools/goctl/model/sql/example/sql/user_1.sql +++ /dev/null @@ -1,15 +0,0 @@ --- 用户表 -- -CREATE TABLE `user1` ( - `id` bigint(10) NOT NULL AUTO_INCREMENT, - `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称', - `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码', - `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号', - `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开', - `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称', - `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, - `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `name_index` (`name`), - UNIQUE KEY `mobile_index` (`mobile`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; - diff --git a/tools/goctl/model/sql/model/informationschemamodel.go b/tools/goctl/model/sql/model/informationschemamodel.go index fbbc9718..c566a17a 100644 --- a/tools/goctl/model/sql/model/informationschemamodel.go +++ b/tools/goctl/model/sql/model/informationschemamodel.go @@ -9,11 +9,13 @@ type ( conn sqlx.SqlConn } Column struct { - Name string `db:"COLUMN_NAME"` - DataType string `db:"DATA_TYPE"` - Key string `db:"COLUMN_KEY"` - Extra string `db:"EXTRA"` - Comment string `db:"COLUMN_COMMENT"` + Name string `db:"COLUMN_NAME"` + DataType string `db:"DATA_TYPE"` + Key string `db:"COLUMN_KEY"` + Extra string `db:"EXTRA"` + Comment string `db:"COLUMN_COMMENT"` + ColumnDefault interface{} `db:"COLUMN_DEFAULT"` + IsNullAble string `db:"IS_NULLABLE"` } ) @@ -33,7 +35,7 @@ func (m *InformationSchemaModel) GetAllTables(database string) ([]string, error) } func (m *InformationSchemaModel) FindByTableName(db, table string) ([]*Column, error) { - querySql := `select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,EXTRA,COLUMN_COMMENT from COLUMNS where TABLE_SCHEMA = ? and TABLE_NAME = ?` + querySql := `select COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,DATA_TYPE,COLUMN_KEY,EXTRA,COLUMN_COMMENT from COLUMNS where TABLE_SCHEMA = ? and TABLE_NAME = ?` var reply []*Column err := m.conn.QueryRows(&reply, querySql, db, table) return reply, err diff --git a/tools/goctl/model/sql/parser/parser.go b/tools/goctl/model/sql/parser/parser.go index ea38c75f..450c6047 100644 --- a/tools/goctl/model/sql/parser/parser.go +++ b/tools/goctl/model/sql/parser/parser.go @@ -112,7 +112,17 @@ func Parse(ddl string) (*Table, error) { if column.Type.Comment != nil { comment = string(column.Type.Comment.Val) } - dataType, err := converter.ConvertDataType(column.Type.Type) + var isDefaultNull = true + if column.Type.NotNull { + isDefaultNull = false + } else { + if column.Type.Default == nil { + isDefaultNull = false + } else if string(column.Type.Default.Val) != "null" { + isDefaultNull = false + } + } + dataType, err := converter.ConvertDataType(column.Type.Type, isDefaultNull) if err != nil { return nil, err } @@ -170,7 +180,8 @@ func ConvertColumn(db, table string, in []*model.Column) (*Table, error) { } primaryColumn := primaryColumns[0] - primaryFt, err := converter.ConvertDataType(primaryColumn.DataType) + isDefaultNull := primaryColumn.ColumnDefault == nil && primaryColumn.IsNullAble == "YES" + primaryFt, err := converter.ConvertDataType(primaryColumn.DataType, isDefaultNull) if err != nil { return nil, err } @@ -189,7 +200,8 @@ func ConvertColumn(db, table string, in []*model.Column) (*Table, error) { } for key, columns := range keyMap { for _, item := range columns { - dt, err := converter.ConvertDataType(item.DataType) + isColumnDefaultNull := item.ColumnDefault == nil && item.IsNullAble == "YES" + dt, err := converter.ConvertDataType(item.DataType, isColumnDefaultNull) if err != nil { return nil, err } diff --git a/tools/goctl/model/sql/parser/parser_test.go b/tools/goctl/model/sql/parser/parser_test.go index b342e79b..e8fd7689 100644 --- a/tools/goctl/model/sql/parser/parser_test.go +++ b/tools/goctl/model/sql/parser/parser_test.go @@ -79,6 +79,7 @@ func TestConvertColumn(t *testing.T) { for _, item := range table.Fields { if item.Name.Source() == "mobile" { assert.True(t, item.IsUniqueKey) + break } } }