Introduction:
Q: What do Gin and Gorm do? What's the difference?
A: Gin and Gorm are popular open source libraries in the Go programming language. However, they serve different purposes and are often used together in web development projects.
Gin is a web framework for building HTTP servers. It provides a simple and easy-to-use API for handling HTTP requests and responses, routing, middleware, and other common web applications. It features its high performance and simplicity, providing a lightweight and flexible solution to build web servers.
Gorm is an ORM (Object Relational Mapping) library for Go. It provides a simple and easy-to-use API for interacting with databases, handling database migrations, and performing common database operations such as querying, inserting, updating and deleting records. It supports a variety of database backends, including MySQL, PostgreSQL, SQLite, etc.
All in all, Gin is a web framework for handling HTTP requests and responses, routing, middleware and other network-related things, while Gorm is an ORM library for interacting with databases and performing common database operations. They are usually used together to process HTTP requests/responses and store or fetch data in a web development project.
Development environment:
- Windows 10
- VSCode
1. Gin
0. Quick Start:
package main import ( "encoding/json" "net/http" "/gin-gonic/gin" "/thinkerou/favicon" ) // Middleware (interceptor), functions: preprocessing, login authorization, verification, paging, time-consuming statistics...// func myHandler() { // return func(ctx *) { // // By customizing the middleware and setting the value, you can get the parameters here as long as you call this middleware in the subsequent processing.// ("usersesion", "userid-1") // () // Release// () // Prevent// } // } func main() { // Create a service ginServer := () (("./")) // If you add something and run it again, there is no change, please restart the browser, the browser has a cache // Load static page ("templates/*") // One is global loading, the other is loading the specified file // Load the resource file ("/static", "./static") // A corresponding page is given to the front end ("/index", func(ctx *) { (, "", { "msg": "This data is come from Go background.", }) }) // Can load static pages or test files // Get the parameters in the request // Traditional way: usl?userid=xxx&username=conqueror712 // Rustful method: /user/info/1/conqueror712 // Here are examples of traditional methods ("/user/info", func(context *) { // This format is fixed userid := ("userid") username := ("username") // Return to the front end after getting it (, { "userid": userid, "username": username, }) }) // After executing the code, you can enter http://localhost:8081/user/info?userid=111&username=666 in the browser // You can see the data returned in JSON format // Below is an example of the Rustful method ("/user/info/:userid/:username", func(context *) { userid := ("userid") username := ("username") // Still the same, return to the front end (, { "userid": userid, "username": username, }) }) // After specifying the code, you only need to http://localhost:8081/user/info/111/555 in the browser // You can see that the JSON data is returned, which is very convenient and simple // Serialization // The front-end passes JSON to the back-end ("/json", func(ctx *) { // data, _ := () var m map[string]interface{} // In Go language, object is generally represented by an empty interface, and can receive anything // By the way, above 1.18, interface can be changed directly to any _ = (data, &m) (, m) }) // Just use apipost or postman to write a json and pass it to localhost:8081/json /* json example: { "name": "Conqueror712", "age": 666, "address": "Mars" } */ // Just see the data received in the real-time response of the backend // Handling form requests These support functional programming, Go language features, and can pass functions as parameters in ("/user/add", func(ctx *) { username := ("username") password := ("password") (, { "msg": "ok", "username": username, "password": password, }) }) //Route ("/test", func(ctx *) { // Redirect -> 301 (301, "//") }) // http://localhost:8081/test // 404 (func(ctx *) { (404, "", nil) }) // Routing group is temporarily omitted // Server port, use the server port to access the address (":8081") // If you don't write it, the default is 8080, and it can also be changed.}
API usage example:/zh-cn/docs/examples/
1. Benchmarking
Q: What is the benchmark?
A: Benchmark testing, also known as performance testing or stress testing, is aTests used to measure system or component performance. Benchmark testingThe purpose is to understand the performance of a system or component under specific conditions and compare the results with other similar systems or components. Benchmarks can be used to evaluate various types of systems and components, including hardware, software, networks and overall systems.
Q: When do you need benchmarking?
A: Benchmarking usually involves running a specific workload or task on the system or component under test and measuringThroughput, latency, CPU usage, memory usageand other performance indicators. The results of the benchmark can be used to identify bottlenecks and performance issues and make informed decisions about how to optimize a system or component to improve performance.
There are many different types of benchmarks, each with its own metrics and workloads. Common benchmark test types include:
- Manual benchmarking: Use manual workloads to measure the performance of a system or component.
- Real-world benchmarking: Use real-world workloads or scenarios to measure the performance of a system or component.
- Stress testing: designed to push the system or component to the limit to determine performance issues that may not be noticeable under normal use conditions
It is important to know that benchmarking is not a one-time activity, but shouldPerform regularlyactivity to evaluate system performance and detect consumption over time.
Q: What kind of benchmark results are what we want?
A:
- The higher the total number of calls implemented in a certain period of time, the better
- Single operation takes time (ns/op), the lower the better
- Heap memory allocation (B/op), the lower the better
- The average number of memory allocations per operation (allocs/op), the lower the better
2. Gin's features and Jsoniter:
Gin v1 stable features:
- Zero allocation routing.
- Still the fastest http router and framework.
- Complete unit test support.
- Practical test.
- API freezes, new version releases will not destroy your code.
- Gin projects can be easily deployed on any cloud provider.
Gin uses encoding/json as the default json package, but you can modify it to jsoniter using tags in compilation.
$ go build -tags=jsoniter .
What is Jsoniter?
-
json-iterator
It's a fast and flexibleJSON
The parser is provided at the same timeJava
andGo
Two versions. -
json-iterator
It's the fastestJSON
parser. It can be as fast as 10 times faster than ordinary parsers - Unique
iterator api
Can traverse directlyJSON
, ultimate performance, zero memory allocation - A lot of code borrowed from dsljson and jsonparser.
Download dependencies:go get /json-iterator/go
2. GORM
0. Features and installation:
- Fully functional ORM
- Association (Has One, Has Many, Belongs To, Many To Many, polymorphic, single table inheritance)
- Create, Save, Update, Delete, Find hook method
- support
Preload
、Joins
Preloading - Transactions, Nested Transactions, Save Point, Rollback To Saved Point
- Context, precompiled mode, DryRun mode
- Batch Insert, FindInBatches, Find/Create with Map, CRUD using SQL expressions, Context Valuer
- SQL builder, Upsert, database lock, Optimizer/Index/Comment Hint, named parameters, subquery
- Composite primary key, index, constraint
- Auto Migration
- Custom Logger
- Flexible extensible plug-in API: Database Resolver (multi-database, read-write separation), Prometheus…
- Each feature has been tested
- Developer friendly
go get -u /gorm go get -u /driver/sqlite
Other supplements:
- Gorm is soft deletion to ensure the integrity of the database
3. Navicat
Create a new connection -> MySQL -> Connection name -> Password -> Double-click on the left to open -> Right-click information_schema -> New database -> Name crud-list -> Character set utf8mb4
If this is opened, the error will be reportednavicat 1045 - access denied for user 'root'@'localhost' (using password: 'YES')
, you need to check the problem of your own database itself
4. Gin+Gorm's CRUD
Connect to the database
Write the test code and run it successfully, but at this time, you cannot check whether the database has been created.
If we need to define the structure and then define the table migration, the specific code is as follows:
package main import ( "fmt" "time" // "/driver/sqlite" "/gin-gonic/gin" "/driver/mysql" "/gorm" ) func main() { // How to connect to the database? MySQL + Navicat // What needs to be changed: username, password, database name dsn := "root:BqV?eGcc_1o+@tcp(127.0.0.1:3306)/crud-list?charset=utf8mb4&parseTime=True&loc=Local" db, err := ((dsn), &{}) ("db = ", db) ("err = ", err) // Connection pool sqlDB, err := () // SetMaxIdleConns Set the maximum number of connections in the idle connection pool (10) // SetMaxOpenConns Set the maximum number of open database connections. (100) // SetConnMaxLifetime sets the maximum time for connection to be reusable. (10 * ) // 10 seconds // Structure type List struct { Name string State string Phone string Email string Address string } // Migrate (&List{}) // Interface r := () // Port number PORT := "3001" (":" + PORT) }
After definition, we run it. There is no error and 3001 is displayed on the terminal. At this time, we can go to Navicat to view the "table" under crud-list. After refreshing, we find that a list is generated, which is correct.
But at this time we have two problems:
- No primary key: add in struct
Let's solve it, Ctrl+left button can be viewed
model
- The name in the table becomes plural: see the advanced topic of the document - GORM configuration for details,
*db*, *err* *:=* ((dsn), *&*{})
Just add a paragraph to it
After the change is completed, we must first put the original table in Navicatlists
Only by deleting it can you recreate it. At this time, we will find that there are a lot of things in the form
Structure definition and optimization
For example:
`gorm:"type:varchar(20); not null" json:"name" binding:"required"`
What should be noted is:
- The variable (Name) in the structure must be capitalized, otherwise it will not be delisted and will be automatically ignored.
- Gorm Specify Type
- json means the name of json when it is received
- binding required means that it must be passed in
CRUD interface
// test("/", func(c *) { (200, { "message": "Request succeeded", }) })
After writing this paragraph, run the code, and then go to postman to create a new GET interface127.0.0.1:3001
Then send it, and the request succeeds.
The same is true for adding. After writing it, you can use the following JSON to test it:
{ "name" : "Zhang San", "state" : "On-job", "phone" : "13900000000", "email" : "6666@", "address" : "Erxian Bridge Chenghua Avenue" }
return:
{ "code": "200", "data": { "ID": 1, "CreatedAt": "2023-01-24T09:27:36.73+08:00", "UpdatedAt": "2023-01-24T09:27:36.73+08:00", "DeletedAt": null, "name": "Zhang San", "state": "On-job", "phone": "13900000000", "email": "6666@", "address": "Erxian Bridge Chenghua Avenue" }, "msg": "Added successfully" }
You can also see this data in the database at this time
The same is true for deletion. After writing and running, add a DELETE interface and enter127.0.0.1:3001/user/delete/2
After that, you can see the return (provided that there is data)
{ "code": 200, "msg": "Delete successfully" }
If the id that does not exist is deleted, it will return
{ "code": 400, "msg": "id not found, deletion failed" }
By the way, in fact, this code can be optimized. There are too many if else here. If there are errors during optimization, it will be returned directly.
The same goes for modification, example:
{ "name" : "Zhang San", "state" : "Resignation", "phone" : "13900000000", "email" : "6666@", "address" : "Erxian Bridge Chenghua Avenue" }
return:
{ "code": 200, "msg": "Modification was successful" }
There are two types of query:
- Conditional query
- Pagination query
If you query the condition, write the request directly127.0.0.1:3001/user/list/Wang Wu
return:
{ "code": "200", "data": [ { "ID": 3, "CreatedAt": "2023-01-24T10:06:25.305+08:00", "UpdatedAt": "2023-01-24T10:06:25.305+08:00", "DeletedAt": null, "name": "Wang Wu", "state": "On-job", "phone": "13100000000", "email": "8888@", "address": "Eight Immortal Bridge Chenghua Avenue" } ], "msg": "Query successful" }
All / paging query
For example, the request is:127.0.0.1:3001/user/list?pageNum=1&pageSize=2
It means to query the two on the first page
return:
{ "code": 200, "data": { "list": [ { "ID": 3, "CreatedAt": "2023-01-24T10:06:25.305+08:00", "UpdatedAt": "2023-01-24T10:06:25.305+08:00", "DeletedAt": null, "name": "Wang Wu", "state": "On-job", "phone": "13100000000", "email": "8888@", "address": "Eight Immortal Bridge Chenghua Avenue" } ], "pageNum": 1, "pageSize": 2, "total": 2 }, "msg": "Query successful" }
If the request is:127.0.0.1:3001/user/list
return:
{ "code": 200, "data": { "list": [ { "ID": 1, "CreatedAt": "2023-01-24T09:27:36.73+08:00", "UpdatedAt": "2023-01-24T09:55:20.351+08:00", "DeletedAt": null, "name": "Zhang San", "state": "Resignation", "phone": "13900000000", "email": "6666@", "address": "Erxian Bridge Chenghua Avenue" }, { "ID": 3, "CreatedAt": "2023-01-24T10:06:25.305+08:00", "UpdatedAt": "2023-01-24T10:06:25.305+08:00", "DeletedAt": null, "name": "Wang Wu", "state": "On-job", "phone": "13100000000", "email": "8888@", "address": "Eight Immortal Bridge Chenghua Avenue" } ], "pageNum": 0, "pageSize": 0, "total": 2 }, "msg": "Query successful" }
The complete code is as follows:
package main import ( "fmt" "strconv" "time" // "/driver/sqlite" "/gin-gonic/gin" "/driver/mysql" "/gorm" "/gorm/schema" ) func main() { // How to connect to the database? MySQL + Navicat // What needs to be changed: username, password, database name dsn := "root:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" db, err := ((dsn), &{ NamingStrategy: { SingularTable: true, }, }) ("db = ", db) ("err = ", err) // Connection pool sqlDB, err := () // SetMaxIdleConns Set the maximum number of connections in the idle connection pool (10) // SetMaxOpenConns Set the maximum number of open database connections. (100) // SetConnMaxLifetime sets the maximum time for connection to be reusable. (10 * ) // 10 seconds // Structure type List struct { // Primary key Name string `gorm:"type:varchar(20); not null" json:"name" binding:"required"` State string `gorm:"type:varchar(20); not null" json:"state" binding:"required"` Phone string `gorm:"type:varchar(20); not null" json:"phone" binding:"required"` Email string `gorm:"type:varchar(40); not null" json:"email" binding:"required"` Address string `gorm:"type:varchar(200); not null" json:"address" binding:"required"` } // Migrate (&List{}) // Interface r := () // test // ("/", func(c *) { // (200, { // "message": "Request succeeded", // }) // }) // Business code agreement: 200 correct, 400 error // Add ("/user/add", func(ctx *) { // Define a variable pointing to a structure var data List // Binding method err := (&data) // Determine whether there is any error in the binding if err != nil { (200, { "msg": "Add failed", "data": {}, "code": "400", }) } else { // Database operation (&data) // Create a data (200, { "msg": "Added successfully", "data": data, "code": "200", }) } }) // Delete // 1. Find the corresponding entry for the corresponding id // 2. Determine whether the id exists // 3. Delete or return id from the database // Restful encoding specification ("/user/delete/:id", func(ctx *) { var data []List // Receive id id := ("id") // If there is a key-value pair form, use Query() // Determine whether the id exists ("id = ? ", id).Find(&data) if len(data) == 0 { (200, { "msg": "id not found, deletion failed", "code": 400, }) } else { // Operation database deletion (the corresponding item to delete id) // ("id = ? ", id).Delete(&data) <- Actually, it doesn't need to be written like this, because the data you found is the data to be deleted. (&data) (200, { "msg": "Delete successfully", "code": 200, }) } }) // change ("/user/update/:id", func(ctx *) { // 1. Find the entry corresponding to the corresponding id // 2. Determine whether the id exists // 3. Modify the corresponding entry or return id not found var data List id := ("id") // ("id = ?", id).Find(&data) can be written like this or as follows // You can also add the Count function after Where to find out the number of digits corresponding to this condition ("id").Where("id = ? ", id).Find(&data) if == 0 { (200, { "msg": "User ID not found", "code": 400, }) } else { // Bind it err := (&data) if err != nil { (200, { "msg": "Modification failed", "code": 400, }) } else { // db modify database content ("id = ?", id).Updates(&data) (200, { "msg": "Modification was successful", "code": 200, }) } } }) // check // The first type: conditional query, ("/user/list/:name", func(ctx *) { // Get path parameters name := ("name") var dataList []List // Query the database ("name = ? ", name).Find(&dataList) // Determine whether the data has been queried if len(dataList) == 0 { (200, { "msg": "No data was found", "code": "400", "data": {}, }) } else { (200, { "msg": "Query successful", "code": "200", "data": dataList, }) } }) // The second type: all query / pagination query ("/user/list", func(ctx *) { var dataList []List // Query all data or query paginated data pageSize, _ := (("pageSize")) pageNum, _ := (("pageNum")) // Determine whether paging is required if pageSize == 0 { pageSize = -1 } if pageNum == 0 { pageNum = -1 } offsetVal := (pageNum - 1) * pageSize // Fixed writing method Just remember it if pageNum == -1 && pageSize == -1 { offsetVal = -1 } // Return a total number var total int64 // Query the database (dataList).Count(&total).Limit(pageSize).Offset(offsetVal).Find(&dataList) if len(dataList) == 0 { (200, { "msg": "No data was found", "code": 400, "data": {}, }) } else { (200, { "msg": "Query successful", "code": 200, "data": { "list": dataList, "total": total, "pageNum": pageNum, "pageSize": pageSize, }, }) } }) // Port number PORT := "3001" (":" + PORT) }
This is all about this article about Gin+Gorm CRUD. For more related Gin Gorm CRUD content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!