# R&D `gorm` ## Get ```bash= $ go get -u gorm.io/gorm ``` ## Issue When Running Examples ### Error ```bash= # github.com/mattn/go-sqlite3 cgo: exec gcc: exec: "gcc": executable file not found in %PATH% ``` ### Solution ```bash= choco install mingw -y ``` ## Basic ### Initiation ```go= db, err := gorm.Open(sqlite.Open("some.db"), &gorm.config{}) ``` ### Migration ```go= type A struct{ } db.AutoMigrate(&A{}) ``` ### Create ```go= a := &A{...data} db.Create(a) ``` ### Delete ```go= db.Where("field/coolumn",value).Delete(&A{}) ``` ### Update ```go= db.Where("field/coolumn",value).Update("field/column",value) ``` ### Relation - Belongs To ```go= type Parent struct { gorm.Model Name string } type Child struct { gorm.Model Name string ParentID uint Parent *Parent `gorm:"foreignKey:ParentID"` } ``` ### Preloading ```go= type A struct{ .... BID.... B *B } type B struct{ .... } a := &A{} db.Preload("B").Where(.....).Find(a) ``` ### Custom Table Name ```go= // need to implement schema.Tabler interface type A struct{ ..... } func (a *A) TableName() string{ return "A" } ``` ## From DB To Struct I couldn't find any direct information on how gorm(gorm.io) uses database to generate struct, but found a library that does the job. It generates struct from sqlite3 database attaches tagging like `gorm:""...`, also there are other functionalities. Such as json tagging, creating http server and so on... ### Install ```bash= go get -u github.com/smallnest/gen ``` This will download the executable. ### Command ```bash= gen --sqltype=sqlite3 --connstr "./test.db" --gorm --out ./example --database main ``` This will generate some files in the example directory. ### Experiment This was my original struct: ```go= type User struct { gorm.Model ID string UserName string Email string } ``` - After doing Database operations, I have a `test.db` database file. - Then I ran the command - An `example` directory was created - Inside it , there was 2 files, one `model_base.go` which I need to look deeper, it has Database Actions, Table Information, Column/Field structure .... // Sample : ```go= package model import "fmt" // Action CRUD actions type Action int32 var ( // Create action when record is created Create = Action(0) // RetrieveOne action when a record is retrieved from db RetrieveOne = Action(1) // RetrieveMany action when record(s) are retrieved from db RetrieveMany = Action(2) // Update action when record is updated in db Update = Action(3) // Delete action when record is deleted in db Delete = Action(4) // FetchDDL action when fetching ddl info from db FetchDDL = Action(5) tables map[string]*TableInfo ) func init() { tables = make(map[string]*TableInfo) tables["users"] = usersTableInfo } // String describe the action func (i Action) String() string { switch i { case Create: return "Create" case RetrieveOne: return "RetrieveOne" case RetrieveMany: return "RetrieveMany" case Update: return "Update" case Delete: return "Delete" case FetchDDL: return "FetchDDL" default: return fmt.Sprintf("unknown action: %d", int(i)) } } // Model interface methods for database structs generated type Model interface { TableName() string BeforeSave() error Prepare() Validate(action Action) error TableInfo() *TableInfo } // TableInfo describes a table in the database type TableInfo struct { Name string `json:"name"` Columns []*ColumnInfo `json:"columns"` } // ColumnInfo describes a column in the database table type ColumnInfo struct { Index int `json:"index"` GoFieldName string `json:"go_field_name"` GoFieldType string `json:"go_field_type"` JSONFieldName string `json:"json_field_name"` ProtobufFieldName string `json:"protobuf_field_name"` ProtobufType string `json:"protobuf_field_type"` ProtobufPos int `json:"protobuf_field_pos"` Comment string `json:"comment"` Notes string `json:"notes"` Name string `json:"name"` Nullable bool `json:"is_nullable"` DatabaseTypeName string `json:"database_type_name"` DatabaseTypePretty string `json:"database_type_pretty"` IsPrimaryKey bool `json:"is_primary_key"` IsAutoIncrement bool `json:"is_auto_increment"` IsArray bool `json:"is_array"` ColumnType string `json:"column_type"` ColumnLength int64 `json:"column_length"` DefaultValue string `json:"default_value"` } // GetTableInfo retrieve TableInfo for a table func GetTableInfo(name string) (*TableInfo, bool) { val, ok := tables[name] return val, ok } ``` - Another file was `users.go`, In which the Users struct can be seen as it was generated. // Sample : ```go= package model import ( "database/sql" "time" "github.com/guregu/null" "github.com/satori/go.uuid" ) var ( _ = time.Second _ = sql.LevelDefault _ = null.Bool{} _ = uuid.UUID{} ) /* DB Table Details ------------------------------------- CREATE TABLE `users` (`id` text,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`user_name` text,`email` text,PRIMARY KEY (`id`)) JSON Sample ------------------------------------- { "id": "FuFucxUKhGIeGEGBssVmIfBIt", "created_at": "2216-08-15T03:54:05.076014368+06:00", "updated_at": "2105-11-06T03:36:23.232552451+06:00", "deleted_at": "2175-04-06T13:42:14.949882557+06:00", "user_name": "NsTIHILujbHtVWZPZJGYfjepV", "email": "YPGuOkqDrSvZfSRHUgauXZLcM"} */ // Users struct is a row record of the users table in the main database type Users struct { //[ 0] id text null: false primary: true isArray: false auto: false col: text len: -1 default: [] ID string `gorm:"primary_key;column:id;type:text;" json:"id"` //[ 1] created_at datetime null: true primary: false isArray: false auto: false col: datetime len: -1 default: [] CreatedAt time.Time `gorm:"column:created_at;type:datetime;" json:"created_at"` //[ 2] updated_at datetime null: true primary: false isArray: false auto: false col: datetime len: -1 default: [] UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;" json:"updated_at"` //[ 3] deleted_at datetime null: true primary: false isArray: false auto: false col: datetime len: -1 default: [] DeletedAt time.Time `gorm:"column:deleted_at;type:datetime;" json:"deleted_at"` //[ 4] user_name text null: true primary: false isArray: false auto: false col: text len: -1 default: [] UserName sql.NullString `gorm:"column:user_name;type:text;" json:"user_name"` //[ 5] email text null: true primary: false isArray: false auto: false col: text len: -1 default: [] Email sql.NullString `gorm:"column:email;type:text;" json:"email"` } var usersTableInfo = &TableInfo{ Name: "users", Columns: []*ColumnInfo{ &ColumnInfo{ Index: 0, Name: "id", Comment: ``, Notes: ``, Nullable: false, DatabaseTypeName: "text", DatabaseTypePretty: "text", IsPrimaryKey: true, IsAutoIncrement: false, IsArray: false, ColumnType: "text", ColumnLength: -1, GoFieldName: "ID", GoFieldType: "string", JSONFieldName: "id", ProtobufFieldName: "id", ProtobufType: "string", ProtobufPos: 1, }, &ColumnInfo{ Index: 1, Name: "created_at", Comment: ``, Notes: ``, Nullable: true, DatabaseTypeName: "datetime", DatabaseTypePretty: "datetime", IsPrimaryKey: false, IsAutoIncrement: false, IsArray: false, ColumnType: "datetime", ColumnLength: -1, GoFieldName: "CreatedAt", GoFieldType: "time.Time", JSONFieldName: "created_at", ProtobufFieldName: "created_at", ProtobufType: "google.protobuf.Timestamp", ProtobufPos: 2, }, &ColumnInfo{ Index: 2, Name: "updated_at", Comment: ``, Notes: ``, Nullable: true, DatabaseTypeName: "datetime", DatabaseTypePretty: "datetime", IsPrimaryKey: false, IsAutoIncrement: false, IsArray: false, ColumnType: "datetime", ColumnLength: -1, GoFieldName: "UpdatedAt", GoFieldType: "time.Time", JSONFieldName: "updated_at", ProtobufFieldName: "updated_at", ProtobufType: "google.protobuf.Timestamp", ProtobufPos: 3, }, &ColumnInfo{ Index: 3, Name: "deleted_at", Comment: ``, Notes: ``, Nullable: true, DatabaseTypeName: "datetime", DatabaseTypePretty: "datetime", IsPrimaryKey: false, IsAutoIncrement: false, IsArray: false, ColumnType: "datetime", ColumnLength: -1, GoFieldName: "DeletedAt", GoFieldType: "time.Time", JSONFieldName: "deleted_at", ProtobufFieldName: "deleted_at", ProtobufType: "google.protobuf.Timestamp", ProtobufPos: 4, }, &ColumnInfo{ Index: 4, Name: "user_name", Comment: ``, Notes: ``, Nullable: true, DatabaseTypeName: "text", DatabaseTypePretty: "text", IsPrimaryKey: false, IsAutoIncrement: false, IsArray: false, ColumnType: "text", ColumnLength: -1, GoFieldName: "UserName", GoFieldType: "sql.NullString", JSONFieldName: "user_name", ProtobufFieldName: "user_name", ProtobufType: "string", ProtobufPos: 5, }, &ColumnInfo{ Index: 5, Name: "email", Comment: ``, Notes: ``, Nullable: true, DatabaseTypeName: "text", DatabaseTypePretty: "text", IsPrimaryKey: false, IsAutoIncrement: false, IsArray: false, ColumnType: "text", ColumnLength: -1, GoFieldName: "Email", GoFieldType: "sql.NullString", JSONFieldName: "email", ProtobufFieldName: "email", ProtobufType: "string", ProtobufPos: 6, }, }, } // TableName sets the insert table name for this struct type func (u *Users) TableName() string { return "users" } // BeforeSave invoked before saving, return an error if field is not populated. func (u *Users) BeforeSave() error { return nil } // Prepare invoked before saving, can be used to populate fields etc. func (u *Users) Prepare() { } // Validate invoked before performing action, return an error if field is not populated. func (u *Users) Validate(action Action) error { return nil } // TableInfo return table meta data func (u *Users) TableInfo() *TableInfo { return usersTableInfo } ```