diff --git a/go.mod b/go.mod index dff5e1b..003f458 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect gorm.io/driver/mysql v1.4.7 // indirect diff --git a/go.sum b/go.sum index 56268ac..8dcfa40 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= diff --git a/init.go b/init.go index 6618ed1..36004a6 100644 --- a/init.go +++ b/init.go @@ -66,4 +66,6 @@ func init() { } DB = db + // 注册序列化器 + schema.RegisterSerializer("csv", CSVSerializer{}) } diff --git a/model.go b/model.go index 9c7dc49..51d9a5e 100644 --- a/model.go +++ b/model.go @@ -1,9 +1,15 @@ package gormExample import ( + "context" + "database/sql" "fmt" + "github.com/google/uuid" "gorm.io/gorm" + "gorm.io/gorm/schema" "log" + "reflect" + "strings" "time" ) @@ -36,7 +42,7 @@ type TypeMap struct { } func Migrate() { - if err := DB.Debug().AutoMigrate(&TypeMap{}, &Post{}, &Category{}, &PostCategory{}, &Box{}); err != nil { + if err := DB.Debug().AutoMigrate(&Service{}, &IAndC{}, &FieldTag{}, &TypeMap{}, &Post{}, &Category{}, &PostCategory{}, &Box{}); err != nil { log.Fatal(err) } } @@ -52,3 +58,224 @@ func PointerDiff() { DB.First(typeMap, 1) fmt.Printf("%+v\n", typeMap) } + +type CustomTypeModel struct { + gorm.Model + + FTime time.Time + FNullTime sql.NullTime + + FString string + FNullString sql.NullString + + FUUID uuid.UUID + FNullUUID uuid.NullUUID +} + +func CustomType() { + //id := uuid.UUID{} + //id.Scan() // Scanner + //id.Value() // Valuer + + // 初始化模型 + ctm := &CustomTypeModel{} + // 迁移数据表 + DB.AutoMigrate(ctm) + + // 创建 + ctm.FTime = time.Now() // 当前时间 + ctm.FNullTime = sql.NullTime{} // 零值,Valid默认为false + ctm.FString = "" + ctm.FNullString = sql.NullString{} + + ctm.FUUID = uuid.New() + ctm.FNullUUID = uuid.NullUUID{} + + DB.Create(ctm) + + // 查询 + DB.First(ctm, ctm.ID) + + // 判定字段是否为NULL + if ctm.FString == "" { + fmt.Println("FString is NULL") + } else { + fmt.Println("FString is NOT NULL") + } + + if ctm.FNullString.Valid == false { + fmt.Println("FNullString is NULL") + } else { + fmt.Println("FNullString is NOT NULL") + } +} + +type FieldTag struct { + //Field string `gorm:"" json:"" bson:"" xml:""` // Tag + gorm.Model + + // string 类型的处理 + FStringDefault string `gorm:""` + FTypeChar string `gorm:"type:char(32)"` + FTypeVarchar string `gorm:"type:varchar(255)"` + FTypeText string `gorm:"type:text"` + FTypeBlob []byte `gorm:"type:blob"` + FTypeEnum string `gorm:"type:enum('Go', 'GORM', 'MySQL')"` + FTypeSet string `gorm:"type:set('Go', 'GORM', 'MySQL')"` + + FColNum string `gorm:"column:custom_column_name"` + // 默认是NULl + FColNotNull string `gorm:"type:varchar(255);not null;"` + FColDefault string `gorm:"type:varchar(255);not null;default:gorm middle ware;"` + FColComment string `gorm:"type:varchar(255);comment:带有注释的字段"` +} + +type IAndC struct { + // 基础索引类型 + ID uint `gorm:"primaryKey"` + Email string `gorm:"type:varchar(255);uniqueIndex"` // unique + Age uint8 `gorm:"index;check:age >= 18 AND email is not null"` + + // 复合索引 + FirstName string `gorm:"index:name"` + LastName string `gorm:"index:name"` + // 顺序关键顺序 + // 默认的 priority:10 + FirstName1 string `gorm:"index:name1,priority:2"` + LastName1 string `gorm:"index:name1,priority:1"` + + // 索引选项,前缀长度,排序方式,comment + Height float32 `gorm:"index:,sort:desc"` + AddressHash string `gorm:"index:,length:12,comment:前12个字符作为索引关键字"` +} + +func IAndCCreate() { + iac := &IAndC{} + iac.Age = 18 + if err := DB.Create(iac).Error; err != nil { + log.Fatal(err) + } + fmt.Printf("%+v\n", iac) +} + +type Service struct { + gorm.Model + + //Url string `gorm:"-"` // -:all + Url string `gorm:"-:migration; type:varchar(255)"` + Schema string + Host string `gorm:"<-:false"` + Path string `gorm:"<-:update"` + QueryString string `gorm:"->:false"` +} + +func ServiceCRUD() { + s := &Service{} + s.Schema = "http" + s.Host = "www.mashibing.com" + s.Path = "/" + s.Url = "http://www.mashibing.com/" + DB.Create(s) +} + +func PaperCrud() { + if err := DB.AutoMigrate(&Paper{}); err != nil { + log.Fatal(err) + } + + // 常规操作 + paper := &Paper{} + paper.Subject = "使用Serializer操作Tags字段" + paper.Tags = []string{"Go", "Serializer", "Gorm", "MySQL"} + // create 会执行序列化工作,serialize + if err := DB.Create(paper).Error; err != nil { + log.Fatal(err) + } + + // 查询 + newPaper := &Paper{} + // First会执行反序列化工作,unserialize + DB.First(newPaper, 5) + fmt.Printf("%+v\n", newPaper) + +} + +// 1.定义实现了序列化器接口的类型 +type CSVSerializer struct{} + +// 实现Scan,unserialize时执行 +// ctx Context对象 +// field 模型的字段对应的类型 +// dst 目标值(最终结果赋值到dst) +// dbValue 从数据库读取的值 +// 错误 +func (CSVSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) error { + // 初始化一个用来存储字段值的变量 + var fieldValue []string + // 一:解析读取到的数据表的数据 + if dbValue != nil { // 不是 NULL + // 支持解析的只有string和[]byte + // 使用类型检测进行判定 + var str string + switch v := dbValue.(type) { + case string: + str = v + case []byte: + str = string(v) + default: + return fmt.Errorf("failed to unmarshal CSV value: %#v", dbValue) + } + // 二:核心:将数据表中的字段使用逗号分割,形成 []string + fieldValue = strings.Split(str, ",") + } + + // 三,将处理好的数据,设置到dst上 + field.ReflectValueOf(ctx, dst).Set(reflect.ValueOf(fieldValue)) + return nil +} + +// 实现Value, serialize时执行 +// fieldValue 模型的的字段值 +func (CSVSerializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) { + // 将字段值转换为可存储的CSV结构 + return strings.Join(fieldValue.([]string), ","), nil +} + +type Paper struct { + gorm.Model + + Subject string + //Tags []string + // 使用 json 序列化器进行处理 + Tags []string `gorm:"serializer:json"` + // 使用自定义的编码器 + Categories []string `gorm:"serializer:csv"` +} + +// 2.注册到GORM中 +// 3.测试 +func CustomSerializer() { + // 注册序列化器 + schema.RegisterSerializer("csv", CSVSerializer{}) + + // 测试 + if err := DB.AutoMigrate(&Paper{}); err != nil { + log.Fatal(err) + } + + // 常规操作 + paper := &Paper{} + paper.Subject = "使用自定义的Serializer操作Categories字段" + paper.Tags = []string{"Go", "Serializer", "Gorm", "MySQL"} + paper.Categories = []string{"Go", "Serializer", "Gorm", "MySQL"} + // create 会执行序列化工作,serialize + if err := DB.Create(paper).Error; err != nil { + log.Fatal(err) + } + + // 查询 + newPaper := &Paper{} + // First会执行反序列化工作,unserialize + DB.First(newPaper, paper.ID) + fmt.Printf("%+v\n", newPaper) +} diff --git a/model_test.go b/model_test.go index 21db13c..651269b 100644 --- a/model_test.go +++ b/model_test.go @@ -9,3 +9,23 @@ func TestMigrate(t *testing.T) { func TestPointerDiff(t *testing.T) { PointerDiff() } + +func TestCustomType(t *testing.T) { + CustomType() +} + +func TestIAndCCreate(t *testing.T) { + IAndCCreate() +} + +func TestServiceCRUD(t *testing.T) { + ServiceCRUD() +} + +func TestPaperCrud(t *testing.T) { + PaperCrud() +} + +func TestCustomSerializer(t *testing.T) { + CustomSerializer() +}