mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-14 18:30:02 +08:00
reactor sql generation
This commit is contained in:
1
go.mod
1
go.mod
@@ -13,6 +13,7 @@ require (
|
|||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
||||||
github.com/go-redis/redis v6.15.7+incompatible
|
github.com/go-redis/redis v6.15.7+incompatible
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
|
github.com/go-xorm/builder v0.3.4
|
||||||
github.com/gogo/protobuf v1.3.1 // indirect
|
github.com/gogo/protobuf v1.3.1 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||||
github.com/golang/mock v1.4.3
|
github.com/golang/mock v1.4.3
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -76,6 +76,10 @@ github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gG
|
|||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-xorm/builder v0.3.4 h1:FxkeGB4Cggdw3tPwutLCpfjng2jugfkg6LDMrd/KsoY=
|
||||||
|
github.com/go-xorm/builder v0.3.4/go.mod h1:KxkQkNN1DpPKTedxXyTQcmH+rXfvk4LZ9SOOBoZBAxw=
|
||||||
|
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
||||||
|
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
|||||||
97
tools/goctl/model/sql/builderx/builder.go
Normal file
97
tools/goctl/model/sql/builderx/builder.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package builderx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/go-xorm/builder"
|
||||||
|
)
|
||||||
|
|
||||||
|
const dbTag = "db"
|
||||||
|
|
||||||
|
func NewEq(in interface{}) builder.Eq {
|
||||||
|
return builder.Eq(ToMap(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGt(in interface{}) builder.Gt {
|
||||||
|
return builder.Gt(ToMap(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToMap(in interface{}) map[string]interface{} {
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
v := reflect.ValueOf(in)
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
// we only accept structs
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
|
||||||
|
}
|
||||||
|
typ := v.Type()
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
// gets us a StructField
|
||||||
|
fi := typ.Field(i)
|
||||||
|
if tagv := fi.Tag.Get(dbTag); tagv != "" {
|
||||||
|
// set key of map to value in struct field
|
||||||
|
val := v.Field(i)
|
||||||
|
zero := reflect.Zero(val.Type()).Interface()
|
||||||
|
current := val.Interface()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(current, zero) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[tagv] = current
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func FieldNames(in interface{}) []string {
|
||||||
|
out := make([]string, 0)
|
||||||
|
v := reflect.ValueOf(in)
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
// we only accept structs
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
|
||||||
|
}
|
||||||
|
typ := v.Type()
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
// gets us a StructField
|
||||||
|
fi := typ.Field(i)
|
||||||
|
if tagv := fi.Tag.Get(dbTag); tagv != "" {
|
||||||
|
out = append(out, tagv)
|
||||||
|
} else {
|
||||||
|
out = append(out, fi.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
func FieldNamesAlias(in interface{}, alias string) []string {
|
||||||
|
out := make([]string, 0)
|
||||||
|
v := reflect.ValueOf(in)
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
// we only accept structs
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
|
||||||
|
}
|
||||||
|
typ := v.Type()
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
// gets us a StructField
|
||||||
|
fi := typ.Field(i)
|
||||||
|
tagName := ""
|
||||||
|
if tagv := fi.Tag.Get(dbTag); tagv != "" {
|
||||||
|
tagName = tagv
|
||||||
|
} else {
|
||||||
|
tagName = fi.Name
|
||||||
|
}
|
||||||
|
if len(alias) > 0 {
|
||||||
|
tagName = alias + "." + tagName
|
||||||
|
}
|
||||||
|
out = append(out, tagName)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
101
tools/goctl/model/sql/builderx/builder_test.go
Normal file
101
tools/goctl/model/sql/builderx/builder_test.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package builderx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-xorm/builder"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
User struct {
|
||||||
|
// 自增id
|
||||||
|
Id string `db:"id" json:"id,omitempty"`
|
||||||
|
// 姓名
|
||||||
|
UserName string `db:"user_name" json:"userName,omitempty"`
|
||||||
|
// 1男,2女
|
||||||
|
Sex int `db:"sex" json:"sex,omitempty"`
|
||||||
|
|
||||||
|
Uuid string `db:"uuid" uuid:"uuid,omitempty"`
|
||||||
|
|
||||||
|
Age int `db:"age" json:"age"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var userFields = FieldNames(User{})
|
||||||
|
|
||||||
|
func TestFieldNames(t *testing.T) {
|
||||||
|
var u User
|
||||||
|
out := FieldNames(&u)
|
||||||
|
fmt.Println(out)
|
||||||
|
actual := []string{"id", "user_name", "sex", "uuid", "age"}
|
||||||
|
assert.Equal(t, out, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewEq(t *testing.T) {
|
||||||
|
u := &User{
|
||||||
|
Id: "123456",
|
||||||
|
UserName: "wahaha",
|
||||||
|
}
|
||||||
|
out := NewEq(u)
|
||||||
|
fmt.Println(out)
|
||||||
|
actual := builder.Eq{"id": "123456", "user_name": "wahaha"}
|
||||||
|
assert.Equal(t, out, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @see https://github.com/go-xorm/builder
|
||||||
|
func TestBuilderSql(t *testing.T) {
|
||||||
|
u := &User{
|
||||||
|
Id: "123123",
|
||||||
|
}
|
||||||
|
fields := FieldNames(u)
|
||||||
|
eq := NewEq(u)
|
||||||
|
sql, args, err := builder.Select(fields...).From("user").Where(eq).ToSQL()
|
||||||
|
fmt.Println(sql, args, err)
|
||||||
|
|
||||||
|
actualSql := "SELECT id,user_name,sex,uuid,age FROM user WHERE id=?"
|
||||||
|
actualArgs := []interface{}{"123123"}
|
||||||
|
assert.Equal(t, sql, actualSql)
|
||||||
|
assert.Equal(t, args, actualArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildSqlDefaultValue(t *testing.T) {
|
||||||
|
var eq = builder.Eq{}
|
||||||
|
eq["age"] = 0
|
||||||
|
eq["user_name"] = ""
|
||||||
|
|
||||||
|
sql, args, err := builder.Select(userFields...).From("user").Where(eq).ToSQL()
|
||||||
|
fmt.Println(sql, args, err)
|
||||||
|
|
||||||
|
actualSql := "SELECT id,user_name,sex,uuid,age FROM user WHERE age=? AND user_name=?"
|
||||||
|
actualArgs := []interface{}{0, ""}
|
||||||
|
assert.Equal(t, sql, actualSql)
|
||||||
|
assert.Equal(t, args, actualArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderSqlIn(t *testing.T) {
|
||||||
|
u := &User{
|
||||||
|
Age: 18,
|
||||||
|
}
|
||||||
|
gtU := NewGt(u)
|
||||||
|
in := builder.In("id", []string{"1", "2", "3"})
|
||||||
|
sql, args, err := builder.Select(userFields...).From("user").Where(in).And(gtU).ToSQL()
|
||||||
|
fmt.Println(sql, args, err)
|
||||||
|
|
||||||
|
actualSql := "SELECT id,user_name,sex,uuid,age FROM user WHERE id IN (?,?,?) AND age>?"
|
||||||
|
actualArgs := []interface{}{"1", "2", "3", 18}
|
||||||
|
assert.Equal(t, sql, actualSql)
|
||||||
|
assert.Equal(t, args, actualArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildSqlLike(t *testing.T) {
|
||||||
|
like := builder.Like{"name", "wang"}
|
||||||
|
sql, args, err := builder.Select(userFields...).From("user").Where(like).ToSQL()
|
||||||
|
fmt.Println(sql, args, err)
|
||||||
|
|
||||||
|
actualSql := "SELECT id,user_name,sex,uuid,age FROM user WHERE name LIKE ?"
|
||||||
|
actualArgs := []interface{}{"%wang%"}
|
||||||
|
assert.Equal(t, sql, actualSql)
|
||||||
|
assert.Equal(t, args, actualArgs)
|
||||||
|
}
|
||||||
@@ -13,7 +13,9 @@ func genDelete(table Table, withCache bool) (string, error) {
|
|||||||
keySet := collection.NewSet()
|
keySet := collection.NewSet()
|
||||||
keyVariableSet := collection.NewSet()
|
keyVariableSet := collection.NewSet()
|
||||||
for fieldName, key := range table.CacheKey {
|
for fieldName, key := range table.CacheKey {
|
||||||
|
if fieldName == table.PrimaryKey.Name.Source() {
|
||||||
keySet.AddStr(key.KeyExpression)
|
keySet.AddStr(key.KeyExpression)
|
||||||
|
}
|
||||||
if fieldName != table.PrimaryKey.Name.Source() {
|
if fieldName != table.PrimaryKey.Name.Source() {
|
||||||
keySet.AddStr(key.DataKeyExpression)
|
keySet.AddStr(key.DataKeyExpression)
|
||||||
}
|
}
|
||||||
@@ -33,7 +35,7 @@ func genDelete(table Table, withCache bool) (string, error) {
|
|||||||
"upperStartCamelObject": camel,
|
"upperStartCamelObject": camel,
|
||||||
"withCache": withCache,
|
"withCache": withCache,
|
||||||
"containsIndexCache": containsIndexCache,
|
"containsIndexCache": containsIndexCache,
|
||||||
"lowerStartCamelPrimaryKey": stringx.From(camel).LowerStart(),
|
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.Snake2Camel()).LowerStart(),
|
||||||
"dataType": table.PrimaryKey.DataType,
|
"dataType": table.PrimaryKey.DataType,
|
||||||
"keys": strings.Join(keySet.KeysStr(), "\n"),
|
"keys": strings.Join(keySet.KeysStr(), "\n"),
|
||||||
"originalPrimaryKey": table.PrimaryKey.Name.Source(),
|
"originalPrimaryKey": table.PrimaryKey.Name.Source(),
|
||||||
|
|||||||
@@ -21,12 +21,17 @@ func genFields(fields []parser.Field) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func genField(field parser.Field) (string, error) {
|
func genField(field parser.Field) (string, error) {
|
||||||
|
tag, err := genTag(field.Name.Source())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
output, err := templatex.With("types").
|
output, err := templatex.With("types").
|
||||||
Parse(template.Field).
|
Parse(template.Field).
|
||||||
Execute(map[string]string{
|
Execute(map[string]interface{}{
|
||||||
"name": field.Name.Snake2Camel(),
|
"name": field.Name.Snake2Camel(),
|
||||||
"type": field.DataType,
|
"type": field.DataType,
|
||||||
"tag": field.Name.Source(),
|
"tag": tag,
|
||||||
|
"hasComment": field.Comment != "",
|
||||||
"comment": field.Comment,
|
"comment": field.Comment,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func genFineOneByField(table Table, withCache bool) (string, error) {
|
|||||||
var list []string
|
var list []string
|
||||||
camelTableName := table.Name.Snake2Camel()
|
camelTableName := table.Name.Snake2Camel()
|
||||||
for _, field := range table.Fields {
|
for _, field := range table.Fields {
|
||||||
if field.IsPrimaryKey {
|
if field.IsPrimaryKey || !field.IsKey {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
camelFieldName := field.Name.Snake2Camel()
|
camelFieldName := field.Name.Snake2Camel()
|
||||||
@@ -25,7 +25,7 @@ func genFineOneByField(table Table, withCache bool) (string, error) {
|
|||||||
"withCache": withCache,
|
"withCache": withCache,
|
||||||
"cacheKey": table.CacheKey[field.Name.Source()].KeyExpression,
|
"cacheKey": table.CacheKey[field.Name.Source()].KeyExpression,
|
||||||
"cacheKeyVariable": table.CacheKey[field.Name.Source()].Variable,
|
"cacheKeyVariable": table.CacheKey[field.Name.Source()].Variable,
|
||||||
"primaryKeyLeft": table.CacheKey[table.Name.Source()].Left,
|
"primaryKeyLeft": table.CacheKey[table.PrimaryKey.Name.Source()].Left,
|
||||||
"lowerStartCamelObject": stringx.From(camelTableName).LowerStart(),
|
"lowerStartCamelObject": stringx.From(camelTableName).LowerStart(),
|
||||||
"lowerStartCamelField": stringx.From(camelFieldName).LowerStart(),
|
"lowerStartCamelField": stringx.From(camelFieldName).LowerStart(),
|
||||||
"upperStartCamelPrimaryKey": table.PrimaryKey.Name.Snake2Camel(),
|
"upperStartCamelPrimaryKey": table.PrimaryKey.Name.Snake2Camel(),
|
||||||
|
|||||||
@@ -104,7 +104,10 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
importsCode := genImports()
|
importsCode, err := genImports(withCache)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
var table Table
|
var table Table
|
||||||
table.Table = in
|
table.Table = in
|
||||||
table.CacheKey = m
|
table.CacheKey = m
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
package gen
|
package gen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
sqltemplate "github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
|
"github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util/templatex"
|
||||||
)
|
)
|
||||||
|
|
||||||
func genImports() string {
|
func genImports(withCache bool) (string, error) {
|
||||||
return sqltemplate.Imports
|
output, err := templatex.With("import").
|
||||||
|
Parse(template.Imports).
|
||||||
|
Execute(map[string]interface{}{
|
||||||
|
"withCache": withCache,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return output.String(), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func genCacheKeys(table parser.Table) (map[string]Key, error) {
|
|||||||
}
|
}
|
||||||
camelFieldName := field.Name.Snake2Camel()
|
camelFieldName := field.Name.Snake2Camel()
|
||||||
lowerStartCamelFieldName := stringx.From(camelFieldName).LowerStart()
|
lowerStartCamelFieldName := stringx.From(camelFieldName).LowerStart()
|
||||||
left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName)
|
left := fmt.Sprintf("cache%s%sPrefix", lowerStartCamelTableName, camelFieldName)
|
||||||
right := fmt.Sprintf("cache#%s#%s#", lowerStartCamelTableName, lowerStartCamelFieldName)
|
right := fmt.Sprintf("cache#%s#%s#", lowerStartCamelTableName, lowerStartCamelFieldName)
|
||||||
variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName)
|
variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName)
|
||||||
m[field.Name.Source()] = Key{
|
m[field.Name.Source()] = Key{
|
||||||
@@ -42,9 +42,9 @@ func genCacheKeys(table parser.Table) (map[string]Key, error) {
|
|||||||
Left: left,
|
Left: left,
|
||||||
Right: right,
|
Right: right,
|
||||||
Variable: variable,
|
Variable: variable,
|
||||||
KeyExpression: fmt.Sprintf(`%s := fmt.Sprintf("cache#user#id#%s", %s)`, variable, "%s", lowerStartCamelFieldName),
|
KeyExpression: fmt.Sprintf(`%s := fmt.Sprintf("cache#%s#%s#%s", %s)`, variable, lowerStartCamelTableName, lowerStartCamelFieldName, "%v", lowerStartCamelFieldName),
|
||||||
DataKeyExpression: fmt.Sprintf(`%s := fmt.Sprintf("cache#user#id#%s", data.%s)`, variable, "%s", lowerStartCamelFieldName),
|
DataKeyExpression: fmt.Sprintf(`%s := fmt.Sprintf("cache#%s#%s#%s", data.%s)`, variable, lowerStartCamelTableName, lowerStartCamelFieldName, "%v", camelFieldName),
|
||||||
RespKeyExpression: fmt.Sprintf(`%s := fmt.Sprintf("cache#user#id#%s", resp.%s)`, variable, "%s", lowerStartCamelFieldName),
|
RespKeyExpression: fmt.Sprintf(`%s := fmt.Sprintf("cache#%s#%s#%s", resp.%s)`, variable, lowerStartCamelTableName, lowerStartCamelFieldName, "%v", camelFieldName),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ import (
|
|||||||
"github.com/xwb1989/sqlparser"
|
"github.com/xwb1989/sqlparser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
none = iota
|
||||||
|
primary
|
||||||
|
unique
|
||||||
|
normal
|
||||||
|
spatial
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Table struct {
|
Table struct {
|
||||||
Name stringx.String
|
Name stringx.String
|
||||||
@@ -26,6 +34,7 @@ type (
|
|||||||
IsPrimaryKey bool
|
IsPrimaryKey bool
|
||||||
Comment string
|
Comment string
|
||||||
}
|
}
|
||||||
|
KeyType int
|
||||||
)
|
)
|
||||||
|
|
||||||
func Parse(ddl string) (*Table, error) {
|
func Parse(ddl string) (*Table, error) {
|
||||||
@@ -50,6 +59,7 @@ func Parse(ddl string) (*Table, error) {
|
|||||||
columns := tableSpec.Columns
|
columns := tableSpec.Columns
|
||||||
indexes := tableSpec.Indexes
|
indexes := tableSpec.Indexes
|
||||||
|
|
||||||
|
keyMap := make(map[string]KeyType)
|
||||||
for _, index := range indexes {
|
for _, index := range indexes {
|
||||||
info := index.Info
|
info := index.Info
|
||||||
if info == nil {
|
if info == nil {
|
||||||
@@ -59,13 +69,33 @@ func Parse(ddl string) (*Table, error) {
|
|||||||
if len(index.Columns) > 1 {
|
if len(index.Columns) > 1 {
|
||||||
return nil, errPrimaryKey
|
return nil, errPrimaryKey
|
||||||
}
|
}
|
||||||
break
|
keyMap[index.Columns[0].Column.String()] = primary
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// can optimize
|
||||||
|
if len(index.Columns) > 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
column := index.Columns[0]
|
||||||
|
columnName := column.Column.String()
|
||||||
|
camelColumnName := stringx.From(columnName).Snake2Camel()
|
||||||
|
// by default, createTime|updateTime findOne is not used.
|
||||||
|
if camelColumnName == "CreateTime" || camelColumnName == "UpdateTime" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info.Unique {
|
||||||
|
keyMap[columnName] = unique
|
||||||
|
} else if info.Spatial {
|
||||||
|
keyMap[columnName] = spatial
|
||||||
|
} else {
|
||||||
|
keyMap[columnName] = normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
fields []Field
|
fields []Field
|
||||||
primaryKey Primary
|
primaryKey Primary
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, column := range columns {
|
for _, column := range columns {
|
||||||
if column == nil {
|
if column == nil {
|
||||||
continue
|
continue
|
||||||
@@ -83,18 +113,19 @@ func Parse(ddl string) (*Table, error) {
|
|||||||
field.DataBaseType = column.Type.Type
|
field.DataBaseType = column.Type.Type
|
||||||
field.DataType = dataType
|
field.DataType = dataType
|
||||||
field.Comment = comment
|
field.Comment = comment
|
||||||
// see line 1194 https://github.com/xwb1989/sqlparser/blob/master/ast.go
|
key, ok := keyMap[column.Name.String()]
|
||||||
field.IsKey = column.Type.KeyOpt != 0
|
if ok {
|
||||||
field.IsPrimaryKey = column.Type.KeyOpt == 1
|
field.IsKey = true
|
||||||
fields = append(fields, field)
|
field.IsPrimaryKey = key == primary
|
||||||
// see line 1195 https://github.com/xwb1989/sqlparser/blob/master/ast.go
|
if field.IsPrimaryKey {
|
||||||
if column.Type.KeyOpt == 1 {
|
|
||||||
primaryKey.Field = field
|
primaryKey.Field = field
|
||||||
if column.Type.Autoincrement {
|
if column.Type.Autoincrement {
|
||||||
primaryKey.AutoIncrement = true
|
primaryKey.AutoIncrement = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fields = append(fields, field)
|
||||||
|
}
|
||||||
return &Table{
|
return &Table{
|
||||||
Name: stringx.From(tableName),
|
Name: stringx.From(tableName),
|
||||||
PrimaryKey: primaryKey,
|
PrimaryKey: primaryKey,
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package template
|
package template
|
||||||
|
|
||||||
var Field = `{{.name}} {{.type}} {{.tag}} {{.comment}}`
|
var Field = `{{.name}} {{.type}} {{.tag}} {{if .hasComment}}// {{.comment}}{{end}}`
|
||||||
|
|||||||
@@ -55,5 +55,15 @@ func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{
|
|||||||
default:
|
default:
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}{{else}}var resp {{.upperStartCamelObject}}
|
||||||
|
query := ` + "`" + `select ` + "`" + ` + {{.lowerStartCamelObject}}Rows + ` + "`" + ` from ` + "` + " + `m.table ` + " + `" + ` where {{.originalField}} limit 1` + "`" + `
|
||||||
|
err := m.QueryRowNoCache(&resp, query, {{.lowerStartCamelField}})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}{{end}}
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -2,13 +2,15 @@ package template
|
|||||||
|
|
||||||
var Imports = `
|
var Imports = `
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"{{if .withCache}}
|
||||||
"strings"
|
"fmt"
|
||||||
|
{{end}}"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/stores/cache"
|
"github.com/tal-tech/go-zero/core/stores/cache"
|
||||||
"github.com/tal-tech/go-zero/core/stores/sqlc"
|
"github.com/tal-tech/go-zero/core/stores/sqlc"
|
||||||
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
||||||
"github.com/tal-tech/go-zero/core/stringx"
|
"github.com/tal-tech/go-zero/core/stringx"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ var Vars = `
|
|||||||
var (
|
var (
|
||||||
{{.lowerStartCamelObject}}FieldNames = builderx.FieldNames(&{{.upperStartCamelObject}}{})
|
{{.lowerStartCamelObject}}FieldNames = builderx.FieldNames(&{{.upperStartCamelObject}}{})
|
||||||
{{.lowerStartCamelObject}}Rows = strings.Join({{.lowerStartCamelObject}}FieldNames, ",")
|
{{.lowerStartCamelObject}}Rows = strings.Join({{.lowerStartCamelObject}}FieldNames, ",")
|
||||||
{{.lowerStartCamelObject}}RowsExpectAutoSet = strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{.if autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "create_time", "update_time"), ",")
|
{{.lowerStartCamelObject}}RowsExpectAutoSet = strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "create_time", "update_time"), ",")
|
||||||
{{.lowerStartCamelObject}}RowsWithPlaceHolder = strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "create_time", "update_time"), "=?,") + "=?"
|
{{.lowerStartCamelObject}}RowsWithPlaceHolder = strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "create_time", "update_time"), "=?,") + "=?"
|
||||||
|
|
||||||
{{.cacheKeys}}
|
{{.cacheKeys}}
|
||||||
|
|||||||
149
tools/goctl/model/sql/test/modelwithcache/testmodel.go
Normal file
149
tools/goctl/model/sql/test/modelwithcache/testmodel.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/stores/cache"
|
||||||
|
"github.com/tal-tech/go-zero/core/stores/sqlc"
|
||||||
|
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
||||||
|
"github.com/tal-tech/go-zero/core/stringx"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
userCamelFieldNames = builderx.FieldNames(&UserCamel{})
|
||||||
|
userCamelRows = strings.Join(userCamelFieldNames, ",")
|
||||||
|
userCamelRowsExpectAutoSet = strings.Join(stringx.Remove(userCamelFieldNames, "create_time", "update_time"), ",")
|
||||||
|
userCamelRowsWithPlaceHolder = strings.Join(stringx.Remove(userCamelFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
|
||||||
|
|
||||||
|
cacheuserCamelIdPrefix = "cache#userCamel#id#"
|
||||||
|
cacheuserCamelNamePrefix = "cache#userCamel#name#"
|
||||||
|
cacheuserCamelMobilePrefix = "cache#userCamel#mobile#"
|
||||||
|
|
||||||
|
ErrNotFound = sqlx.ErrNotFound
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
UserCamelModel struct {
|
||||||
|
sqlc.CachedConn
|
||||||
|
table string
|
||||||
|
}
|
||||||
|
|
||||||
|
UserCamel struct {
|
||||||
|
Id int64 `db:"id"`
|
||||||
|
Name string `db:"name"` // 用户名称
|
||||||
|
Password string `db:"password"` // 用户密码
|
||||||
|
Mobile string `db:"mobile"` // 手机号
|
||||||
|
Gender string `db:"gender"` // 男|女|未公开
|
||||||
|
Nickname string `db:"nickname"` // 用户昵称
|
||||||
|
CreateTime time.Time `db:"createTime"`
|
||||||
|
UpdateTime time.Time `db:"updateTime"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewUserCamelModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserCamelModel {
|
||||||
|
return &UserCamelModel{
|
||||||
|
CachedConn: sqlc.NewConn(conn, c),
|
||||||
|
table: table,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserCamelModel) Insert(data UserCamel) error {
|
||||||
|
query := `insert into ` + m.table + `(` + userCamelRowsExpectAutoSet + `) value (?, ?, ?, ?, ?, ?)`
|
||||||
|
_, err := m.ExecNoCache(query, data.Id, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserCamelModel) FindOne(id int64) (*UserCamel, error) {
|
||||||
|
userCamelIdKey := fmt.Sprintf("cache#userCamel#id#%v", id)
|
||||||
|
var resp UserCamel
|
||||||
|
err := m.QueryRow(&resp, userCamelIdKey, func(conn sqlx.SqlConn, v interface{}) error {
|
||||||
|
query := `select ` + userCamelRows + ` from ` + m.table + ` where id = ? limit 1`
|
||||||
|
return conn.QueryRow(v, query, id)
|
||||||
|
})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserCamelModel) FindOneByName(name string) (*UserCamel, error) {
|
||||||
|
userCamelNameKey := fmt.Sprintf("cache#userCamel#name#%v", name)
|
||||||
|
var resp UserCamel
|
||||||
|
err := m.QueryRowIndex(&resp, userCamelNameKey, func(primary interface{}) string {
|
||||||
|
return fmt.Sprintf("%s%v", cacheuserCamelIdPrefix, primary)
|
||||||
|
}, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||||
|
query := `select ` + userCamelRows + ` from ` + m.table + ` where name = ? limit 1`
|
||||||
|
if err := conn.QueryRow(&resp, query, name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Id, nil
|
||||||
|
}, func(conn sqlx.SqlConn, v, primary interface{}) error {
|
||||||
|
query := `select ` + userCamelRows + ` from ` + m.table + ` where id = ? limit 1`
|
||||||
|
return conn.QueryRow(v, query, primary)
|
||||||
|
})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserCamelModel) FindOneByMobile(mobile string) (*UserCamel, error) {
|
||||||
|
userCamelMobileKey := fmt.Sprintf("cache#userCamel#mobile#%v", mobile)
|
||||||
|
var resp UserCamel
|
||||||
|
err := m.QueryRowIndex(&resp, userCamelMobileKey, func(primary interface{}) string {
|
||||||
|
return fmt.Sprintf("%s%v", cacheuserCamelIdPrefix, primary)
|
||||||
|
}, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||||
|
query := `select ` + userCamelRows + ` from ` + m.table + ` where mobile = ? limit 1`
|
||||||
|
if err := conn.QueryRow(&resp, query, mobile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Id, nil
|
||||||
|
}, func(conn sqlx.SqlConn, v, primary interface{}) error {
|
||||||
|
query := `select ` + userCamelRows + ` from ` + m.table + ` where id = ? limit 1`
|
||||||
|
return conn.QueryRow(v, query, primary)
|
||||||
|
})
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return &resp, nil
|
||||||
|
case sqlc.ErrNotFound:
|
||||||
|
return nil, ErrNotFound
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserCamelModel) Update(data UserCamel) error {
|
||||||
|
userCamelIdKey := fmt.Sprintf("cache#userCamel#id#%v", data.Id)
|
||||||
|
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := `update ` + m.table + ` set ` + userCamelRowsWithPlaceHolder + ` where id = ?`
|
||||||
|
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
|
||||||
|
}, userCamelIdKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UserCamelModel) Delete(id int64) error {
|
||||||
|
data, err := m.FindOne(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
userCamelIdKey := fmt.Sprintf("cache#userCamel#id#%v", id)
|
||||||
|
userCamelNameKey := fmt.Sprintf("cache#userCamel#name#%v", data.Name)
|
||||||
|
userCamelMobileKey := fmt.Sprintf("cache#userCamel#mobile#%v", data.Mobile)
|
||||||
|
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
|
query := `delete from ` + m.table + ` where id = ?`
|
||||||
|
return conn.Exec(query, id)
|
||||||
|
}, userCamelIdKey, userCamelNameKey, userCamelMobileKey)
|
||||||
|
return err
|
||||||
|
}
|
||||||
20
tools/goctl/model/sql/test/sql_test.go
Normal file
20
tools/goctl/model/sql/test/sql_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenSqlWithCache(t *testing.T) {
|
||||||
|
generator := gen.NewDefaultGenerator("./test.sql", "./modelwithcache")
|
||||||
|
err := generator.Start(true)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenSqlWithoutCache(t *testing.T) {
|
||||||
|
generator := gen.NewDefaultGenerator("./test.sql", "./modelwithoutcache")
|
||||||
|
err := generator.Start(false)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
30
tools/goctl/model/sql/test/test.sql
Normal file
30
tools/goctl/model/sql/test/test.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
-- user_snake
|
||||||
|
CREATE TABLE `user_snake` (
|
||||||
|
`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 NULL,
|
||||||
|
`update_time` timestamp NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `name_index` (`name`),
|
||||||
|
KEY `mobile_index` (`mobile`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- userCamel (disable AUTO_INCREMENT)
|
||||||
|
CREATE TABLE `userCamel` (
|
||||||
|
`id` bigint(10) NOT NULL,
|
||||||
|
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
|
||||||
|
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
|
||||||
|
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
|
||||||
|
`gender` char(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',
|
||||||
|
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',
|
||||||
|
`createTime` timestamp NULL DEFAULT NULL,
|
||||||
|
`updateTime` timestamp NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `nameIndex` (`name`),
|
||||||
|
KEY `mobile_index` (`mobile`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
@@ -87,7 +87,7 @@ func (s String) LowerStart() string {
|
|||||||
if !unicode.IsUpper(r) && !unicode.IsLower(r) {
|
if !unicode.IsUpper(r) && !unicode.IsLower(r) {
|
||||||
return s.source
|
return s.source
|
||||||
}
|
}
|
||||||
return string(r) + s.source[1:]
|
return string(unicode.ToLower(r)) + s.source[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// it will not ignore spaces
|
// it will not ignore spaces
|
||||||
|
|||||||
Reference in New Issue
Block a user