[Golang] Gorm usage summary

[Golang] Gorm usage summary

Gorm usage summary

data

Good Gorm Chinese document jasperxu.com/gorm-zh This article is based on this information to organize, summarize the most basic Gorm entry use content, and continue to add.

installation

go get -u github.com/jinzhu/gorm copy the code

Database configuration

//Database configuration information func options () { //Globally disable table name pluralization //If set to true, the default table name of `User` is `user`, and the table name set with `TableName` will not be affected DB.SingularTable ( true ) //The automatic migration mode will be updated to the latest. //Warning: Automatic migration will only create tables, lack of columns and indexes, and will not change the types of existing columns or delete unused columns to protect data. DB.AutoMigrate(&User{}) //Print log, the default is false DB.LogMode( true ) //Connection pool DB.DB().SetMaxIdleConns( 10 ) DB.DB().SetMaxOpenConns( 100 ) } Copy code

Database Connectivity

sqlite3

use

sqlite
Database to quickly connect

package main Import ( "encoding/JSON" "FMT" "github.com/jinzhu/gorm" //This is gorm database package driving _ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/mysql" ) func initSqlite3Db () { //initialize var err error DB, err = gorm.Open( "sqlite3" , "test.db" ) //Check for errors if err != nil { panic (err) } } Copy code

mysql

func initMysqlDb () { //initialize var err error DB, err = gorm.Open( "mysql" , "root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8" ) //Check for errors if err != nil { panic (err) } } Copy code

Model definition

tag:gorm

by

tag
mark
gorm
To create constraints and attribute configurations for fields in the database

type User struct { Id int64 `gorm:"primary_key;AUTO_INCREMENT"` //Set as primary_key primary key, AUTO_INCREMENT increments UserId string `gorm:"index:idx_user_id;not null` //Set ordinary index, index name is idx_user_id, not null cannot be empty UserName string `gorm:"type:varchar(64);not null"` //Set to varchar type, length is 64, not null cannot be empty Age int Phone int `gorm:"unique_index:uk_phone"` //Set unique Index, the index name is uk_phone CreateTime time.Time `gorm:"not null"` UpdateTime time.Time `gorm:"not null"` UserRemark string `gorm:"column:remark;default:'default'"` IgnoreMe int `gorm:"-"` //ignore this field } Copy code
gorm definitionDatabase field constraints
gorm:"primary_key"
The field is set as the primary key
gorm:"AUTO_INCREMENT"
The field is set to self-increment
gorm:"size:20
The field length is set to 20
gorm:"index:idx_user_id
Field setting common index, the name is idx_user_id
gorm:"not null
Set the field to be non-empty
gorm:"type:varchar(64)"
Set the field to varchar type and the length is 64
gorm:"column:remark"
Set the database field name to remark
gorm:"-"
Ignore this field and do not create it in the table
gorm:"default:'default'"
Set the default value of the field

Note: It is not necessarily the most complete example, and will be added one after another

Table Name

in

gorm
In, the table name is created in plural, when the following options are set before the table is created, the plural table name can be disabled

//disable global table names plural //If set to true, `User` default table named` user`, using the name `TableName` table settings are not affected DB.SingularTable ( to true ) Copy the code

Or in each

model
Declared in the structure
TableName
Method, if it exists
gorm
This method will be preferred to configure the table name

type User struct {} //The default table name is `users` //Set User's table name to `user` func (User) TableName () string { return "user" } Copy code

Basic operation

Add

NewRecord primary key check & Create

//Create func CreateUser () { user := &User{ UserId: "USER_001" , UserName: "zhangsan" , Age: 10 , Phone: 19901020305 , CreateTime: time.Now(), UpdateTime: time.Now(), UserRemark: "Remarks" , } //1, NewRecord will check whether the primary key exists boolBefore := DB.NewRecord(user) //If the primary key exists, return true; otherwise, return false. Return true here, because the primary key ID is automatically generated. The ID is empty and does not exist. fmt.Println( "boolBefore => " , boolBefore) //2, Create err := DB.Create(user).Error if err != nil { fmt.Printf( "CreateUser error %v/n" , err) } //This will return the primary key ID fmt.Println( "user => " , toJson(user)) //3, NewRecord will check whether the primary key exists boolAfter := DB.NewRecord(user) //If the primary key exists, return true; otherwise, return false. Return false here, because the primary key ID already exists. fmt.Println( "boolAfter => " , boolAfter) //Here is the complete data return, including the primary key ID fmt.Println( "user = > " , toJson(user)) } Copy code

The print log is as follows:

boolBefore => true user => { "Id" :1, "UserId" : "USER_001" , "UserName" : "zhangsan" , "Age" :10, "Phone" :19901020301, "CreateTime" : "2021-06 -01T13:33:02.237956+08:00" , "UpdateTime" : "2021-06-01T13:33:02.237956+08:00" , "UserRemark" : "Default" , "IgnoreMe" :0} boolAfter => false user => { "Id" :1, "UserId" : "USER_001" , "UserName" : "zhangsan" , "Age" :10, "Phone" :19901020301, "CreateTime" : "2021-06 -01T13: 33 is: 02.237956 + 08: 00 " , " UpdateTime " : " 2021-06-01T13: 33 is: 02.237956 + 08: 00 " , " UserRemark " : " default " , " IgnoreMe " : 0} copy the code

The table data is as follows:

SQLite > SELECT * from User ; . 1 | USER_001 | zhangsan | 10 | 19,901,020,301 | 2021 -06 -01 13 is : 33 is : 02.237956 + 08 : 00 | 2021 -06 -01 13 is : 33 is : 02.237956 + 08 : 00 | default copy the code

Inquire

First: Query the first record

func FindFirstRecord () { result := &User{} //SELECT * FROM user ORDER BY id LIMIT 1; err:=DB.First(result).Error if err !=nil { fmt.Printf( "FindFirstRecord error %v" ,err) return } fmt.Printf(toJson(result)) } Copy code

The log output is as follows:

{ "Id" :1, "UserId" : "USER_001" , "UserName" : "zhangsan" , "Age" :10, "Phone" :19901020301, "CreateTime" : "2021-06-01T13:33:02.237956+ 08:00 " , " UpdateTime " : " 2021-06-01T13: 33 is: 02.237956 + 08: 00 " , " UserRemark " : " default " , " IgnoreMe " : 0} copy the code

Last: Query the last record

func FindLastRecord () { result := &User{} //SELECT * FROM users ORDER BY id DESC LIMIT 1 err:=DB.Last(result).Error if err != nil { fmt.Printf( "FindLastRecord error %v" ,err) return } fmt.Printf(toJson(result)) } Copy code

The log output is as follows:

{ "Id" :2, "UserId" : "USER_001" , "UserName" : "zhangsan" , "Age" :10, "Phone" :19901020302, "CreateTime" : "2021-06-01T13:57:56.807786+ 08:00" , "UpdateTime" : "2021-06-01T13:57:56.807786+08:00" , "UserRemark" : "Default" , "IgnoreMe" :0} Copy code

First( , pk): query records based on the primary key

func FindRecordByPK () { result := &User{} //SELECT * FROM users WHERE id = 2 err:=DB.First(&User{}, 2 ).Find(result).Error if err != nil { fmt.Printf( "FindRecordByPK error %v" ,err) return } fmt.Printf(toJson(result)) } Copy code

The log output is as follows:

{ "Id" :2, "UserId" : "USER_001" , "UserName" : "zhangsan" , "Age" :10, "Phone" :19901020302, "CreateTime" : "2021-06-01T13:57:56.807786+ 08:00" , "UpdateTime" : "2021-06-01T13:57:56.807786+08:00" , "UserRemark" : "Default" , "IgnoreMe" :0} Copy code

Where( ) Condition query condition

Regarding the conditional query is relatively simple, please refer to jasperxu.com/gorm-zh/cru...

FirstOrInit() & Attrs() & Assign()

Get the first matching record, or use the given conditions to initialize a new record to return.

func FirstOrInit () { user := &User{ UserId: "USER_001" , UserName: "User" , Age: 10 , Phone: 19901020305 , CreateTime: time.Now(), UpdateTime: time.Now(), } //Query SELECT * FROM users WHERE user_name="guanjian" The record exists err := DB.Attrs(&User{UserName: "attrs" }). //Here, assign assign (&User{UserRemark: "mandatory" regardless of whether it is found or not Modify " }). //If there is no such record, fill it, and the initial filling logic is Attrs> Where ( If there is no Attrs, use the Where condition) //If there is, return the first matching record found FirstOrInit(user, User {UserName: "guanjian" }).Error if err != nil { fmt.Printf( "FirstOrInit error %v" , err) return } fmt.Printf(toJson(user)) } Copy code

FirstOrInit
The method will not fall into the library if the result is not found , but will initialize
FirstOrInit(obj)
middle
obj
The value of the object, and initialize the value in the Where condition together

FirstOrCreate() & Attrs() & Assign()

with

FirstOrInit
Similar, the difference is that it will drop the warehouse operation. Get the first matching record, or use the given conditions to create a new warehouse.

func FirstOrCreate () { user := &User{ UserId: "USER_001" , UserName: "guanjian" , Age: 10 , Phone: 19901020310 , CreateTime: time.Now(), UpdateTime: time.Now(), } //Query SELECT * FROM users WHERE id = 10 // If there is no such record, the user will be initialized according to the user to fill it //If there is, return the first matching record found err := DB.FirstOrCreate(user, User {Id: 10 }).Error if err != nil { fmt.Printf( "FirstOrInit error %v" , err) return } fmt.Printf(toJson(user)) } Copy code

FirstOrCreate
If the method fails to find the result, it will be dropped and initialized
FirstOrCreate(obj)
middle
obj
The value of the object, and initialize the value in the Where condition together

Select query field definition

Here only query

user_name,age
Field return, the rest of the fields will not be queried to return the value

func Select () { user := &User{} err := DB.Select( "user_name,age" ).Find(user).Error if err != nil { fmt.Printf( "Select error %v" , err) return } fmt.Println( "Only query user_name, age fields" , toJson(user)) } Copy code

Scan

func Scan () { user := &User{} err := DB.Table( "user" ).Select([] string { "user_name" , "age" }).Scan(user).Error if err != nil { fmt.Printf( "Scan error %v" , err) return } fmt.Println( "user => " , toJson(user)) } Copy code

Scopes dynamic condition addition

The realization method is like

func(db *gorm.DB) * gorm.DB
You can match the method
Scopes
Add dynamic conditions to make coding clearer and friendly

//Match the create_time query condition func WithCreateTimeLessThanNow (db *gorm.DB) * gorm . DB { return db.Where( "create_time <?" , time.Now()) } //Here matching age query condition func WithAgeGreaterThan0 (db *gorm.DB) * gorm . DB { return db.Where( "age> ?" , 0 ) } func Scopes () { user := &User{} //Dynamic splicing of query conditions err := DB.Scopes(WithAgeGreaterThan0,WithCreateTimeLessThanNow).Find(user).Error if err != nil { fmt.Printf( "Scopes error %v" , err) return } fmt.Println( "user => " , toJson(user)) } Copy code

modify

Save

Save the existing data, trigger the update logic (update); save the non-existent data, directly insert (insert)

//Save the existing data and trigger the update logic func SaveExist () { user := &User{ Id: 1 , } //Query data with id=9999, it does not exist user.UserName = "saveNewUserName" //Update operation according to the primary key err := DB.Save(user).Error if err != nil { fmt.Printf( "Save error %v" , err) return } fmt.Println( "save user => " , toJson(user)) } //Save non-existent data, insert func directly SaveNoExist () { user := &User{ Id: 9999 , Phone: 19902029492 , } //Query data with id=9999, it does not exist user.UserName = "saveNewUserName" //Update operation according to the primary key err := DB.Save(user).Error if err != nil { fmt.Printf( "Save error %v" , err) return } fmt.Println( "save user => " , toJson(user)) } Copy code

Update & Updates & Omit

func Update () { user := &User{ Id: 9999 , } err := DB.Model(user).Update( "user_name" , "this is 9999" ).Error if err != nil { fmt.Printf( "Update error %v" , err) return } } Copy code

The effect of updating the field is as follows:

use

Updates
Method can batch update multiple fields

func Updates () { //Query field whereUser := &User{ Id: 9999 , } //Batch update fields updateUser := &User{ UserName: "This is Updates" , Age: 9292 , UserRemark: "Updates" , } err := DB.Model(whereUser).Updates(updateUser).Error if err != nil { fmt.Printf( "Updates error %v" , err) return } } Copy code

The effect of batch update fields is as follows:

use

Omit
Method can ignore the update of the field

func UpdateOmit () { //Query field whereUser := &User{ Id: 1 , } //Batch update fields updateUser := &User{ UserName: "This is Updates" , Age: 6666 , UserRemark: "1 -> Update" , } err := DB.Model(whereUser). //Ignore the update of the user_name field Omit( "user_name" ). Updates(updateUser).Error if err != nil { fmt.Printf( "Updates error %v" , err) return } } Copy code

The above update operation will execute the model

BeforeUpdate, AfterUpdate
Method, update its UpdatedAt timestamp, save its Associations when updating, if you don t want to call them, you can use UpdateColumn, UpdateColumns

non-zero value update problem In order to avoid misoperation or dirty data to update the entire table, you need to configure
WHERE
Mandatory conditions to limit

use

RowsAffected
Can return the number of rows currently updated

func RowsAffected () { //Query field whereUser := &User{ Id: 9999 , } //Batch update fields updateUser := &User{ UserName: "This is Updates" , Age: 9292 , UserRemark: "Updates" , } //RowsAffected returns the number of rows currently updated count := DB.Model(whereUser).Updates(updateUser).RowsAffected fmt.Printf( "Updates count %v" , count) } Copy code

delete

Generally use database logical deletion, this part can refer to jasperxu.com/gorm-zh/cru...

Callback use (Callback)

It will be called during creation, update, query, and deletion. If any callback returns an error, gorm will stop future operations and roll back all changes.

Callback default definition

in

gorm
You can see the default in the source code
callback
Register, focus on
callback_create.go, callback_delete.go, callback_query.go, callback_update.go
Can, in fact, the whole
gorm
The execution process is based on
callback
To achieve the entire execution process.

The following example
callback_create.go
File
init()
Method, in order from top to bottom, registered
callback
Function, each
callbackName
(Such as: gorm:create) can be used as the insertion point of the callback, with
Before, After
Can realize front insertion, rear insertion, etc., other
callback
Similar to not repeat it here

//Define callbacks for creating func init () { DefaultCallback.Create().Register( "gorm:begin_transaction" , beginTransactionCallback) DefaultCallback.Create().Register( "gorm:before_create" , beforeCreateCallback) DefaultCallback.Create().Register( "gorm:save_before_associations" , saveBeforeAssociationsCallback) DefaultCallback.Create().Register( "gorm:update_time_stamp" , updateTimeStampForCreateCallback) DefaultCallback.Create().Register( "gorm:create" , createCallback) DefaultCallback.Create().Register( "gorm:force_reload_after_create" , forceReloadAfterCreateCallback) DefaultCallback.Create().Register( "gorm:save_after_associations" , saveAfterAssociationsCallback) DefaultCallback.Create().Register( "gorm:after_create" , afterCreateCallback) DefaultCallback.Create().Register( "gorm:commit_or_rollback_transaction" , commitOrRollbackTransactionCallback) } Copy code

Callback transaction guarantee

View

gorm
Source code, such as CRUD methods such as
Create(), Update(), Find(), Delete()
The callback method and other calls are made internally. The internal code of the callback is as follows:

func (scope *Scope) callCallbacks (funcs []* func (s *Scope) ) * Scope { //Exception capture here defer func () { if err := recover (); err != nil { if db, ok := scope.db.db.(sqlTx); ok { //If an exception occurs, the transaction will be rolled back db.Rollback() } panic (err) } }() for _, f := range funcs { (*f)(scope) if scope.skipLeft { break } } return scope } Copy code

Let's look at one

callback
An example of a panic exception and database rollback is as follows:

func Callback () { //register in gorm:create node to execute the function DB.Callback().Create(). //Execute After( "gorm:create" ) after the CreateUser() function method, that is, DB is executed . Register( "gorm:AfterCreate" , func (scope *gorm.Scope) { fmt.Println( "AfterCreate" ) //If an exception is thrown here, it will be captured by the recover method of callbacks panic ( "AfterCreate error" ) }) //Since the above callback has been panic, it will be rolled back here CreateUser() } Copy code

Exception handling

Yes

RecordNotFound(), Error, GetErrors()
To deal with record non-existence, exceptions, multiple exceptions, etc.

func Error () { //RecordNotFound bool := DB.First(&User{Id: 9999 }).RecordNotFound() fmt.Println( "RecordNotFound => " , bool ) //Error err := DB.Create(&User{}).Error fmt.Println( "err => " , err) errs := DB.Create(&User{}).GetErrors() fmt.Println( "errs => " , errs) } Copy code

Affairs

Note that once you are in a transaction, use

tx
As a database handle, if you use
gorm.DB
Operation is not subject to transaction control, so you need to pay attention here!

func Tx () { //Open transaction tx := DB.Begin() //Execute method 1 will be executed successfully err1 := tx.Create(&User{ UserId: "USER_002" , UserName: "zhangsan" , Age: 0 , Phone: 17701020304 , CreateTime: time.Now(), UpdateTime: time.Now(), UserRemark: "" , IgnoreMe: 0 , }).Error if err1 != nil { tx.Rollback() fmt.Println( "=== Create 1 error ===" ) } //Execute method 2 will fail to execute, there is Phone unique primary key constraint err2 := tx.Create(&User{ UserId: "USER_002" , UserName: "zhangsan" , Age: 0 , Phone: 17701020304 , CreateTime: time.Now(), UpdateTime: time.Now(), UserRemark: "" , IgnoreMe: 0 , }).Error if err2 != nil { tx.Rollback() fmt.Println( "=== Create 2 error ===" ) } tx.Commit() } Copy code

Log

//Enable Logger, display detailed log db.LogMode( true ) //Disable the logger, do not display any logs db.LogMode( false ) //debugging a single operation, this operation log display detailed db.Debug (). The Where ( "name =?" , "Jinzhu" ) .First (the User & {}) copy the code

reference

jasperxu.com/gorm-zh/