当前位置:网站首页>Go service platform project (I) design of database tables and use of gendry Library

Go service platform project (I) design of database tables and use of gendry Library

2022-06-21 22:42:00 Love letters are not included

One 、 Database table design

In the course of the project, try to use mysql Command line to operate , But to improve efficiency , Visual tools can be used when creating tables and changing field information , Check the data and fields with the command line .  Insert picture description here

There are eight data sheets for the whole project ,user Table and advisor The table records users' and consultants' information model Field data ,orders surface 、service Table and review Table records orders separately 、 Service and order review data of three entities ,adv_coin Table and user_coin The table is used to record the gold coin running records of users and consultants . The following are a few things to pay attention to in database design :

1. Primary key Id The design of the

In terms of database theory , Each entity should be designed with a primary key Id Field , A record that uniquely identifies the entity . For example, user table settings uid, Consultant table settings aid, Service table settings sid.
about id Field , In general, it will increase by itself , And the starting point of self increment will be set . In order to distinguish the records of each entity , Usually set different self increasing starting points with strong differentiation . For example, in this project , I give uid Set the starting point of self increment to 60000,aid by 80000,sid by 50000. For projects with huge data systems , Consider multiple digit numbers id Set up .

2. The name of the field

The database fields are named with underscores , If the field is strongly related to the entity , You don't have to add an identifier in front of it . such as user The name field of the user in the table , Do not name it user_name, Direct use name that will do . However, if the field has no attribute relationship with the entity , An identifier must be added to the front , for example orders User name in the table , use user_name It would be more appropriate .

3. Non empty setting of field

In general, try to ensure that the fields in the table are not empty , Unless the data of this field can be ignored or only added in subsequent data operations .
 Insert picture description here

For example, in the order table answer Field , Because there is no answer to the question when creating the order , Only when the corresponding consultant answers the order will there be an answer , Therefore, this field cannot be set to non empty . Except in similar cases , All other fields are set to non empty .

4. Special field handling

Projects often encounter some enumerations or fields with format requirements . For example, the gender field is in model Set up in Int enumeration , So the type in the table is set to Int. For example, the average score field of the consultant shall be one decimal place and the maximum is 10, Type is available decimal(2,1).(decimal(x,y),x Represents the total number of digits ,y Identify the number of digits after the decimal point )

Two 、 Configure the database in the project

After designing the database , The next step is to configure the database in the project . In general, the configuration information of the database ( Port number 、 user name 、 password ) Write in the configuration file or constant file , However, due to the small volume of the project , I wrote it directly in the database initialization function .
First , We need to create a separate folder in the project to place the database configuration files , I'm going to call it common, Then create db.go file , Write the functions of database initialization and obtaining database pointer in it .

db.go :GO-GIN / common / db.go

package common

import (...)

var DB *sql.DB

func InitDB() *sql.DB {
    

	scanner.SetTagName("key")
	
	//  here  root:password  The user name of your database : password , demo Is the name of the connected database , 3306 Is the port number ( Default )
	db, err := sql.Open("mysql", "root:[email protected](127.0.0.1:3306)/demo?charset=utf8")
	if err != nil {
    
		fmt.Println(err)
	} else {
    
		fmt.Println(" Database connection successful !")
	}

	DB = db
	return db
}

func GetDB() *sql.DB {
    
	return DB
}

After configuration , stay main.go Add... To the file :

main.go :GO-GIN / main.go

func main() {
    
	r := gin.Default()
	
	//  Initialize database 
	common.InitDB()

	//  establish DB example 
	db := common.GetDB()

	//  Delay database shutdown 
	defer db.Close()
	
	//  Initialize route 
	r = router.InitRouter(r)
	r.Run(":8000")
}

In this way, the basic configuration of the database is completed .

3、 ... and 、gendry Use

Gendry Is a database for auxiliary operation Go package . be based on go-sql-driver/mysql, It provides a series of methods to call the standard library for you database/sql Methods in prepare parameters .( Official link jump )

Gendery It is mainly divided into 3 A separate part :

1.manager:

It is mainly used to initialize the connection pool ( That is to say sql.DB object ), Set various parameters , So it's called manager. You can set any go-sql-driver/mysql Parameters supported by the driver .

2.builder:

builder seeing the name of a thing one thinks of its function , Is to build and generate sql sentence . Handwriting sql Although intuitive and simple , But the maintainability is poor , The main thing is that hard coding is error prone .builder Is not a ORM, It just provides a simple API Help you generate sql sentence .

3.scanner:

After performing the database operation , To put The returned result set and the custom struct mapping .Scanner Provide a simple interface to bind result set and custom type through reflection .scanner The reflection will use the structure of tag, Default tagName yes ddb:“xxx”, You can also customize it .
The... Set in this project scannar mapping tag by “key”, stay InitDB() Function :

scanner.SetTagName("key")

For a field , Set up key Is the field name in the corresponding data table .scanner The function maps to struct On .

Four 、 Function encapsulation and use

Function encapsulation is stored in a separate folder , This project is named utils. establish gen.go file , preservation gendry Encapsulation of statements .

gen.go :GO-GIN / utils / gen.go

1. Lookup function - Universal - Binding structure

import (
	"database/sql"
	"strconv"
	...
	qb "github.com/didi/gendry/builder"
	"github.com/didi/gendry/scanner"
	_ "github.com/go-sql-driver/mysql"
	"go.uber.org/zap"
)

func GenSelectOne(obj interface{
    }, table string, where map[string]interface{
    }, selectField []string) error {
    
	
	var db = common.GetDB()

	cond, vals, _ := qb.BuildSelect(table, where, selectField)

	rows, err := db.Query(cond, vals...)

	if err != nil {
    
		common.Log.Info(config.ErrSelect.Msg, zap.Error(err))
		return err
	}

	defer rows.Close()

	if err := scanner.Scan(rows, obj); err != nil {
    
		common.Log.Info(config.ErrScan.Msg, zap.Error(err))
		return err
	}

	return err
}

First, get the database object , And then use gendry-builder Of BuildSelect Function to build a query statement cond And query parameters vals, And then use mysql Native query statements for db.Query The query , Get the results rows.( Be careful : Yes rows Close the after operation rows, See the official website for specific reasons ). Last use gendry-scannar Medium Scan function , Bind the result to the incoming structure obj in .

2. Update function - Universal - Binding structure

func GenUpdateNew(obj interface{
    }, table string, where map[string]interface{
    }) error {
    
	
	var db = common.GetDB()
	
	update := map[string]interface{
    }{
    }
	
	// Will the incoming struct turn map, Used to update the 
	MapByReflect(update, obj)
	
	cond, vals, _ := qb.BuildUpdate(table, where, update)

	_, err := db.Exec(cond, vals...)

	if err != nil {
    
		common.Log.Info(config.ErrUpdate.Msg, zap.Error(err))
	}

	return err
}

In the update function, the struct Convert to a map, Then put it into the update statement to complete the update . The conversion function will be explained in detail in subsequent articles .

3. Insert the function - Universal - Binding structure

//  Insert 
func GenInsertNew(obj interface{
    }, table string) error {
    
	
	var db = common.GetDB()
	
	data := map[string]interface{
    }{
    }
	
	var insertData []map[string]interface{
    }

	MapByReflect(data, obj)

	insertData = append(insertData, data)

	cond, vals, _ := qb.BuildInsert(table, insertData)

	_, err := db.Exec(cond, vals...)

	if err != nil {
    
		common.Log.Info(config.ErrInsert.Msg, zap.Error(err))
	}

	return err
}

4. Lookup function - customized - Bind structure list

//  multi-select  -  List of consultants 
func GenSelectAdvisor(objArray *[]model.Advisor, table string, where map[string]interface{
    }, selectField []string) error {
    
	
	var db = common.GetDB()

	cond, vals, _ := qb.BuildSelect(table, where, selectField)

	rows, err := db.Query(cond, vals...)

	if err != nil {
    
		common.Log.Info(config.ErrSelect.Msg, zap.Error(err))
		return err
	}

	defer rows.Close()
	
	if err := scanner.Scan(rows, objArray); err != nil {
    
		common.Log.Info(config.ErrScan.Msg, zap.Error(err))
		return err
	}

	return err
}

When we need to query multiple entities , Just pass in a struct Array . But after many attempts , It is found that the input parameters cannot be uniformly defined as []interface, Only input function can be customized . The above query function is a customized query consultant list function , Pass in a consultant structure array and bind . Other similar functions only need to modify the structure type of the input parameter .

5. Use of functions

user.go :GO-GIN / service / user.go

import (
	"xxx/go-gin/common"
	"xxx/go-gin/config"
	"xxx/go-gin/model"
	"xxx/go-gin/utils"
	"go.uber.org/zap"
)

//  The user gets the list of consultants 
func GetAdvisorList(advisorList *[]model.Advisor) error {
    
	where := map[string]interface{
    }{
    }

	if err := utils.GenSelectAdvisor(advisorList, "advisor", where, config.AdvSelectAll); err != nil {
    
		common.Log.Info(config.ErrSelect.Msg, zap.Error(err))
		return err
	}
	return nil
}

The statement of database operation is used in Service layer , Only return one error. stay Service The layer only needs to error Print log , Then return to Controller Just layer . The specific logic will be discussed in detail in subsequent articles .

5、 ... and 、 Summary and reflection

The design of database and the writing and encapsulation of operation statements are the basis of the whole project , Many details need attention , Otherwise, I will come back later to rework and modify , Efficiency will be greatly reduced .
meanwhile , Data flow should preferably depend on struct Structure , Match the operation data with the corresponding struct Binding , Then carry out data transmission and next step processing , This comparison map Moving around is much more efficient .

原网站

版权声明
本文为[Love letters are not included]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/172/202206212049543869.html