1. Basic Concepts
Object Relational Mapping (ORM).
The database will provide an official client-side driver, but will need to handle the SQL and structure conversion itself.
Using the ORM framework lets us avoid transformations and write boring, redundant code. In theory, the ORM framework can get you out of SQL, but in practice you still need to know SQL to use ORM.
I am personally very averse to using ORM frameworks for two reasons.
I. Without freedom, I can’t control my database as I want.
Second, poor performance, than the official client drive directly write SQL 3-5 times lower efficiency.
However, ORM also has many advantages, and it can avoid slow SQL to some extent for novices.
Several articles have also discussed the pros and cons of ORM. For example, orM_is_AN_antipattern.
In general, whether or not to use the ORM framework depends on the developer organization of a project.
Old hands want freedom, new hands need rules. There are so many novices in the world that old hands have to make some concessions.
Gorm is an ORM framework developed with Golang, which has become one of the most popular ORM frameworks in Golang Web development. This article will explain the COMMON APIS used in GORM to help you quickly learn gorM.
Besides GORM, you have other options, such as SQLX and SQLC.
MySQL > connect to MySQL
Gorm can connect to a variety of databases, requiring only different drivers. Currently, only four databases are supported: MySQL, PostgreSQL, SQlite, and SQL Server. However, other databases can be accessed through customized methods.
The following uses mySQL as an example. You need to install two packages.
import (
"gorm.io/driver/mysql" // gorm mysql driver package
"gorm.io/gorm"// gorm
)
Copy the code
Connection code.
// MySQL configuration information
username := "root" / / account
password := "xxxxxx" / / password
host := "127.0.0.1" / / address
port := 3306 / / port
DBname := "gorm1" // Database name
timeout := "10s" // Connection timed out, 10 seconds
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s? charset=utf8&parseTime=True&loc=Local&timeout=%s", username, password, host, port, DBname, timeout)
/ / Open connections
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
iferr ! =nil {
panic("failed to connect mysql.")}Copy the code
Declare the model
Each table corresponds to a model (structure).
Let’s say you have a goods table in your database.
CREATE TABLE'GORm1'. 'no title' (' id ')int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
`price` decimal(10.2) NULL DEFAULT NULL.PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
Copy the code
That would correspond to something like this.
type Goods struct {
Id int
Name string
Price int
}
Copy the code
convention
Gorm makes a lot of conventions and works with the idea of convention over configuration.
For example, it looks for the table name based on the complex number of the structure, uses ID as the primary key, and represents the creation, update, and delete times based on CreateAt, UpdateAt, and DeletedAt.
Gorm provides a Model structure that you can embed in your own structure, omitting the above fields.
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
Copy the code
Embedded in the goods structure.
type Goods struct {
gorm.Model
Id int
Name string
Price int
}
Copy the code
This allows you to omit the creation ID, CreatedAt, UpdatedAt, and DeletedAt fields each time you create a different structure.
Field tag
When you create the model, you can tag a field to define some of its properties.
For example, to create a Post structure, we want the Title to be mapped to T, set the maximum length to 256, and the field to be unique.
type Post struct {
Title string `gorm:"column:t, size:256, unique:true"`
}
Copy the code
Equivalent to the following SQL.
CREATE TABLE `posts` (`t, size:256.unique:true` longtext)
Copy the code
For more information, see the table below.
Tag name | instructions |
---|---|
column | Specify the DB column name |
type | Column data type. The compatible general type is recommended. For example, all databases support bool, int, Uint, float, string, time, bytes and can be used together with other tags.not null ,size .autoIncrement … likevarbinary(8) Specifying database data types in this way is also supported. When using the specified database data type, it needs to be the full database data type, such as:MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT |
size | Specify the column size, for example:size:256 |
primaryKey | Specifies the column primary key |
unique | Specifying a unique column |
default | Specifies the default value for the column |
precision | Specifies the precision of the column |
scale | Specify column size |
not null | Specifies the column NOT NULL |
autoIncrement | Specifies an automatic growth column |
embedded | Nested fields |
embeddedPrefix | The column name prefix of the embedded field |
autoCreateTime | Tracing the current time at creation time, forint Field, which tracks the number of timestamp seconds that you can usenano /milli To track nanosecond and millisecond timestamps, for example:autoCreateTime:nano |
autoUpdateTime | Track the current time when creating/updating, forint Field, which tracks the number of timestamp seconds that you can usenano /milli To track nanosecond and millisecond timestamps, for example:autoUpdateTime:milli |
index | Create an index based on the parameter. Create a compound index if multiple fields use the same nameThe indexFor more details |
uniqueIndex | withindex Same, but create a unique index |
check | Create check constraints, for examplecheck:age > 13 To see theThe constraintFor more details |
<- | Set the field write permission,<-:create Only the creation,<-:update Update only,<-:false No write permission,<- Create and update permissions |
-> | Set the field read permission,->:false No read permission |
– | Ignore this field,- No read and write permission |
Automatic migration
Gorm can automatically create tables based on the specified structure when the tables of the database are not initialized.
The db.AutoMigrate method is used to automatically create the User table according to the User structure. If the table already exists, this method does nothing.
type User struct {
gorm.Model
UserName string
Password string
}
db.AutoMigrate(&User{})
Copy the code
The table building rules adjust user to plural and automatically add several fields in gorm.model. Because many databases are case insensitive, if using camelCase style naming, in the migration of the database will encounter many problems, so the database field naming style is using underscorecase style naming, GORM will automatically help us convert.
Equivalent to the following SQL.
CREATE TABLE'GORm1'. 'no title' (' id ')bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT,
`created_at` datetime(3) NULL DEFAULT NULL,
`updated_at` datetime(3) NULL DEFAULT NULL,
`deleted_at` datetime(3) NULL DEFAULT NULL,
`user_name` longtext CHARACTER SET utf8 COLLATE utf8_bin NULL,
`password` longtext CHARACTER SET utf8 COLLATE utf8_bin NULL.PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_users_deleted_at`(`deleted_at`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
Copy the code
Create Craete Insert
Create using the db.create method, passing in a pointer to the structure.
user := User{UserName: "l", Password: "ddd"}
result := db.Create(&user)
Copy the code
Equivalent to the following SQL.
INSERT INTO
`users` ( `created_at`.`updated_at`.`deleted_at`.`user_name`.`password` )
VALUES
(
'the 2020-12-03 17:19:00. 249'.'the 2020-12-03 17:19:00. 249',
NULL,
'l'.'ddd')
Copy the code
Gorm automatically maintains creATED_AT, UPDATed_AD, and deleted_AT fields.
Commonly used data returned after insertion
Here are some common insert data.
fmt.Println("ID:", user.ID) // Insert the primary key
fmt.Println("error:", result.Error) // Return error
fmt.Println("rowsAffected:", result.RowsAffected) // The number of inserts
Copy the code
Only the specified fields are inserted
Select the specified field by Select.
user := User{UserName: "lzq", Password: "ccc"}
result := db.Select("UserName").Create(&user)
Copy the code
Equivalent to the following SQL.
INSERT INTO `users` (`user_name`) VALUES ('lzq')
Copy the code
_ Note: CreATED_AT, UPDATed_AD, and deleted_AT are not automatically maintained with select.
The specified field is not inserted
Use a Omit method to filter fields.
result := db.Omit("UserName").Create(&user)
Copy the code
Bulk insert
When batch inserts are needed, pass in a slice.
users := []User{
{UserName: "lzq", Password: "aaa"},
{UserName: "qqq", Password: "bbb"},
{UserName: "gry", Password: "ccc"},
}
db.Create(&users)
Copy the code
Equivalent to the following SQL.
INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
( 'the 2020-12-03 18:08:47. 478'.'the 2020-12-03 18:08:47. 478'.NULL.'lzq'.'aaa'), ('the 2020-12-03 18:08:47. 478'.'the 2020-12-03 18:08:47. 478'.NULL.'qqq'.'bbb'), ('the 2020-12-03 18:08:47. 478'.'the 2020-12-03 18:08:47. 478'.NULL.'gry'.'ccc')
Copy the code
Batch insertion
In some cases, the number of Users may be very large, and batches of batches can be inserted using the CreateInBatches method.
Suppose you have six pieces of user data and you want to insert two at a time, which will result in three SQL executions.
users := []User{
{UserName: "lzq", Password: "aaa"},
{UserName: "qqq", Password: "bbb"},
{UserName: "gry", Password: "ccc"},
{UserName: "lzq", Password: "aaa"},
{UserName: "qqq", Password: "bbb"},
{UserName: "gry", Password: "ccc"},
}
db.CreateInBatches(&users, 2)
Copy the code
This is equivalent to executing the following three SQL statements in sequence.
INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
( 'the 2020-12-03 18:15:20. 602'.'the 2020-12-03 18:15:20. 602'.NULL.'lzq'.'aaa'), ('the 2020-12-03 18:15:20. 602'.'the 2020-12-03 18:15:20. 602'.NULL.'qqq'.'bbb')
Copy the code
INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
( 'the 2020-12-03 18:15:20. 616'.'the 2020-12-03 18:15:20. 616'.NULL.'gry'.'ccc'), ('the 2020-12-03 18:15:20. 616'.'the 2020-12-03 18:15:20. 616'.NULL.'lzq'.'aaa')
Copy the code
INSERT INTO `users` ( `created_at`, `updated_at`, `deleted_at`, `user_name`, `password` )
VALUES
( 'the 2020-12-03 18:15:20. 621'.'the 2020-12-03 18:15:20. 621'.NULL.'qqq'.'bbb'), ('the 2020-12-03 18:15:20. 621'.'the 2020-12-03 18:15:20. 621'.NULL.'gry'.'ccc'
)
Copy the code
The interior of the CreateInBatches method is sliced using for rather than Goroutine.
Read Selete
Querying a Single object
Gorm provides First, Take, and Last methods. They are all implemented by LIMIT 1, primary key ascending, unsorted, and primary key descending, respectively.
user := User{}
// Get the first record (primary key ascending order)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
// Get a record without specifying the sort field
db.Take(&user)
// SELECT * FROM users LIMIT 1;
// Get the last record (primary key descending order)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
Copy the code
If no object is found, an ErrRecordNotFound error is returned.
result := db.First(&user)
errors.Is(result.Error, gorm.ErrRecordNotFound)
result.RowsAffected
Copy the code
Query by primary key
Set the second argument, which is treated as ID, in the First/Take/Last functions. The value can be an int or string.
db.First(&user, 10)
db.First(&user, "10")
Copy the code
When selecting variables of type string, be aware of SQL injection issues.
Querying multiple objects (lists)
Use the Find method to query multiple objects.
users := []User{}
result := db.Find(&users)
Copy the code
The return value is mapped to the Users slice.
You can still get the exception and the affected line number by accessing the Error and RowsAffected fields on the return value.
result.Error
result.RowsAffected
Copy the code
Set the search condition Where
Gorm provides a universal Where method, can implement =, <>, IN, LIKE, AND, >, <, BETWEEN, etc., using? As placeholders.
db.Where("name = ?"."l").First(&user)
// SELECT * FROM users WHERE user_name = 'l' ORDER BY id LIMIT 1;
// Get all matched records
db.Where("name <> ?"."l").Find(&users)
// SELECT * FROM users WHERE user_name <> 'l';
// IN
db.Where("name IN ?"And []string{"lzq"."qqq"}).Find(&users)
// SELECT * FROM users WHERE user_name IN ('lzq','qqq');
// LIKE
db.Where("name LIKE ?"."%l%").Find(&users)
// SELECT * FROM users WHERE user_name LIKE '%l%';
// AND
db.Where("name = ? AND age = ?"."lzq"."aaa").Find(&users)
// SELECT * FROM users WHERE user_name = 'lzq' AND password = aaa;
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2020-11-01 00:00:00' AND '2020-11-08 00:00:00';
Copy the code
Where a method for quickly setting conditions
When passing structs, maps, and slices, it is easier to set conditions.
db.Where(&User{UserName:"lzq", Password:"aaa"}).Find(&user)
db.Where(map[string]interface{} {"user_name": "lzq"."password": "aaa"}).Find(&user)
Copy the code
Structures and maps have almost equal effects.
SELECT
*
FROM
`users`
WHERE
`users`.`user_name` = 'lzq'
AND `users`.`password` = 'aaa'
AND `users`.`deleted_at` IS NULL
Copy the code
The only difference is that zero-valued fields in structs are not queried. Such as 0, “”, false.
Slicing is the query primary key.
db.Where([]int{10.11}).Find(&user)
Copy the code
Equivalent to the following SQL.
SELECT
*
FROM
`users`
WHERE
`users`.`id` IN ( 10.11 )
AND `users`.`deleted_at` IS NULL
Copy the code
For all queries, GORM sets tabel.deleted_at to NULL by default.
In addition to the Where method, there are inline queries, but using both styles is not recommended.
db.Find(&user, "user_name = ?"."lzq")
// SELECT * FROM users WHERE user_name = "lzq";
Copy the code
Other queries Not & Or
Gorm also provides Not and Or methods, but it’s Not recommended because Where can do both, and memorizing additional apis is a mental burden.
db.Where("password = ?"."aaa").Not("user_name"."l").Or("id > ?".10).Find(&users)
Copy the code
Equivalent to the following SQL.
SELECT * FROM `users` WHERE (( PASSWORD = 'aaa' )
AND `user_name` <> 108
OR id > 10
)
AND `users`.`deleted_at` IS NULL
Copy the code
Select the specific field Select
Use the Select method.
db.Select("password").Where(&User{UserName:"lzq"}).Find(&user)
Copy the code
Equivalent to the following SQL.
SELECT
`password`
FROM
`users`
WHERE
`users`.`user_name` = 'lzq'
AND `users`.`deleted_at` IS NULL
Copy the code
Other operating
The sorting Order
db.Order("user_name desc, password").Find(&users)
Copy the code
Equivalent to the following SQL.
SELECT
*
FROM
`users`
WHERE
`users`.`deleted_at` IS NULL
ORDER BY
user_name DESC,
PASSWORD
Copy the code
Paging Limit Offset
Limit and Offset can be used individually or in combination.
db.Limit(3).Find(&users)
db.Offset(3).Find(&users)
db.Limit(2).Offset(3).Find(&users)
Copy the code
Equivalent to the following SQL.
SELECT
*
FROM
`users`
WHERE
`users`.`deleted_at` IS NULL
LIMIT 2 OFFSET 3
Copy the code
Group Group Having
Count the number of duplicate user names based on username.
result := []map[string]interface{}{}
db.Model(&User{}).
Select("user_name, SUM( id ) AS nums").
Group("user_name").
Find(&result)
Copy the code
Equivalent to the following SQL.
SELECT
user_name,
SUM( id ) AS nums
FROM
users
GROUP BY
user_name;
Copy the code
To heavy Distinct
result := []string{}
db.Model(&User{}).
Distinct("user_name").
Find(&result)
Copy the code
Equivalent to the following SQL.
SELECT DISTINCT
user_name
FROM
users
Copy the code
Even the table Join
It is not recommended to use joins in business, but to use multiple queries to associate multiple tables.
Update data
Update all fields
Update all fields, even zero values, with the Save method.
db.First(&user)
user.UserName = ""
db.Save(&user)
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `created_at` = 'the 2020-12-03 15:12:08. 548',
`updated_at` = 'the 2020-12-04 09:17:40. 891',
`deleted_at` = NULL,
`user_name` = ' ',
`password` = 'ddd'
WHERE
`id` = 1
Copy the code
Update a single
Update a single column using the Model and Update methods.
You can use the structure as the selection criteria and select only the ID.
user.ID = 12
db.Model(&user).Update("user_name"."lzq")
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `user_name` = 'lzq',
`updated_at` = 'the 2020-12-04 09:16:45. 263'
WHERE
`id` = 12
Copy the code
You can also set an empty structure in the Model, using the Where method to select your own conditions.
db.Model(&User{}).Where("user_name"."gry").Update("user_name"."gry2")
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `user_name` = 'gry2',
`updated_at` = 'the 2020-12-04 09:21:17. 043'
WHERE
`user_name` = 'gry'
Copy the code
You can also combine selection criteria.
user.ID = 20
db.Model(&user).Where("username"."gry").Update("password"."123")
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `password` = '123',
`updated_at` = 'the 2020-12-04 09:25:30. 872'
WHERE
`username` = 'gry'
AND `id` = 20
Copy the code
Update more columns
Update multiple columns using the Updates method. Support for struct and map updates. When the update condition is struct, the zero value is not updated. If a column is guaranteed to be updated, use Select to Select that column.
Update selected field Selete Omit
Use Select and Omit methods.
Batch update
If the ID is not set in the Model, the default is batch updates.
Delete data
To delete a single
Delete a single piece of data using the Delete method. The ID must be specified; otherwise, the system will delete the data in batches.
user.ID = 20
db.Delete(&user)
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `deleted_at` = 'the 2020-12-04 09:45:32. 389'
WHERE
`users`.`id` = 20
AND `users`.`deleted_at` IS NULL
Copy the code
Set delete Conditions
Use the Where method to set the condition.
db.Where("user_name"."lzq").Delete(&user)
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `deleted_at` = 'the 2020-12-04 09:47:30. 544'
WHERE
`user_name` = 'lzq'
AND `users`.`deleted_at` IS NULL
Copy the code
Delete by primary key
The second argument can be int, string. You need to pay attention to SQL injection when using strings.
db.Delete(&User{}, 20)
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `deleted_at` = 'the 2020-12-04 09:49:05. 161'
WHERE
`users`.`id` = 20
AND `users`.`deleted_at` IS NULL
Copy the code
You can also use slices []int, []string for batch deletion by ID.
db.Delete(&User{}, []string{"21"."22"."23"})
Copy the code
Equivalent to the following SQL.
UPDATE `users`
SET `deleted_at` = 'the 2020-12-04 09:50:38. 46'
WHERE
`users`.`id` IN ( '21'.'22'.'23' )
AND `users`.`deleted_at` IS NULL
Copy the code
Batch delete
Empty structures are batch deleted.
Soft delete (logical delete)
If the structure contains the gorm.DeletedAt field, the soft delete capability is automatically acquired.
When all Delete methods are called, they automatically become update statements.
UPDATE users SET deleted_at="The 2020-12-04 09:40"WHERE id = 31;
Copy the code
The soft deleted data is automatically ignored during query.
SELECT * FROM users WHERE user_name = 'gry' AND deleted_at IS NULL;
Copy the code
Example Query soft delete data
Use the Unscoped method to find soft deleted data.
db.Unscoped().Where("user_name = gry").Find(&users)
Copy the code
Permanent delete (hard delete Physical delete)
Permanently delete data using the Unscoped method.
user.ID = 14
db.Unscoped().Delete(&user)
Copy the code
Native SQL
In addition to the encapsulation method above, GORM also provides the ability to execute native SQL.
Execute the SQL and map the results to variables
Use the Raw method and Scan method.
A single piece of data can be queried and scanned and mapped to a structure or map.
db.
Raw("SELECT id, record_id, user_name, password FROM users WHERE id = ?".25).
Scan(&user)
Copy the code
You can also map to other types.
var userCount int
db.
Raw("SELECT count(id) FROM users").
Scan(&userCount)
Copy the code
If the returned result does not match the type of the mapping variable passed in, the value of the variable will not change.
Only SQL is executed without results
Use the Exec method to execute SQL.
db.Exec("UPDATE users SET password=? WHERE id = ?"."abcdefg".22)
Copy the code
The Hook Hook
Gorm provides Hook functionality. Some logic can be executed automatically before and after creation, query, update, and delete.
create
Gorm provides four create hooks, BeforeCreate, AfterCreate and BeforeSave, AfterSave.
Suppose you now want to add a RecordID and generate a 16-bit UUID each time you create it.
type User struct {
gorm.Model
RecordID string
UserName string
Password string
}
Copy the code
In addition, you want to print the generated UUID before storage and the created ID after storage.
This is done by adding BeforeCreate and AfterCreate methods to the User model structure.
func (u *User) BeforeCreate(tx *gorm.DB) error {
u.RecordID = uuid.New().String()
fmt.Println("When creating User, UUID is:", u.RecordID)
return nil
}
func (u *User) AfterCreate(tx *gorm.DB) error {
fmt.Println("User created, ID = :", u.ID)
return nil
}
Copy the code
update
Update hooks are BeforeUpdate, AfterUpdate and BeforeSave, AfterSave, used the same as create.
The query
The Hook for a query is AfterFind, and the usage is consistent with the creation.
delete
Delete hooks are BeforeDelete and AfterDelete, which are used the same as create.
All hooks except query hooks run on transactions, which are rolled back when an error is returned in a function.
Xi. Business
Transactions guarantee transaction consistency, but reduce some performance. Gorm creation, modification, and deletion are all performed in transactions.
If not, you can disable transactions at initialization, which can improve performance by about 30%.
Globally closed transaction
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
SkipDefaultTransaction: true,})Copy the code
The session level closes the transaction
tx := db.Session(&Session{SkipDefaultTransaction: true})
// Use tx objects to continue SQL execution
tx.First(&user)
Copy the code
Execute SQL in a transaction
Suppose you now want to add a company table to store company information and create a Company_Users table to associate information about users and companies.
// Create a structure
type Company struct {
gorm.Model
RecordID string
Name string
}
type CompanyUser struct {
gorm.Model
RecordID string
UserID string
CompanyID string
}
// Automatic migration
db.AutoMigrate(&Company{})
db.AutoMigrate(&CompanyUser{})
// Create a company
company := Company{Name: "gxt"}
company.RecordID = uuid.New().String()
db.Save(&company)
// Execute in transaction
db.Transaction(func(tx *gorm.DB) error {
// Create a user
u := User{UserName: "ztg", Password: "333"}
result := tx.Create(&u)
iferr := result.Error; err ! =nil {
return err
}
// Query the company information
company2 := Company{}
tx.First(&company2, company.ID)
// Associate the user with the company
result = tx.Create(&CompanyUser{UserID: u.RecordID, CompanyID: company2.RecordID})
iferr := result.Error; err ! =nil {
return err
}
return nil
})
Copy the code
Xii. Journal
Gorm implements a Logger by default, which outputs only slow SQL.
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Info, // Log level
Colorful: false.// Disable color printing},)Copy the code
The log level can be set to Silent, Error, Warn, and Info.
Global Mode on
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
Copy the code
Enable session mode
tx := db.Session(&Session{Logger: newLogger})
Copy the code
Custom Logger
Gorm provides an Interface Interface that you can implement to customize loggers.
type Interface interface {
LogMode(LogLevel) Interface
Info(context.Context, string.interface{})
Warn(context.Context, string.interface{})
Error(context.Context, string.interface{})
Trace(ctx context.Context, begin time.Time, fc func(a) (string.int64).err error)
}
Copy the code