Gin框架 -- Gorm配置及使用


1. 官方文檔

https://gorm.io/zh_CN/

2. 包下載

1. mysql的驅動

go get gorm.io/driver/mysql

2. Gorm

go get gorm.io/gorm

3. 連接

3.1 簡單連接

3. 缺點

  1. 自動生成SQL語句會消耗計算資源,這勢必會對程序性能造成一定的影響
  2. 對於復雜的數據庫操作,ORM通常難以處理,自動生成的SQL 語句在性能方面也不如手寫的原生SQL
  3. 生成SQL 語句的過程是自動進行的,不能人工干預,這是的開發人員無法定制一些特殊的SQL語句

4. Gorm 配置注冊

main.go

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"godemo/app/address"
	"godemo/app/auth"
	"godemo/app/chat"
	"godemo/app/index"
	"godemo/models"
	"godemo/routers"
	"gopkg.in/ini.v1"
	"log"
)

func main() {
	// 注冊路由
	routers.Include(address.Routers, auth.Routers,chat.Routers,index.Routers)
	r := routers.Init()


        // 設置信任的客戶端IP
	r.SetTrustedProxies([]string{"192.168.1.33"})

	// 加載模板文件
	r.LoadHTMLGlob("template/*/*")

	// 靜態文件配置
	r.Static("/static", "./static")

	// 讀取配置文件
	conf, err := ini.Load("./config/my.ini")
	println(">>>>>",conf)
	if err != nil{
		log.Fatal("配置文件加載失敗,err:>>>",err)
	}
	fmt.Println("配置文件加載成功")
	gin.SetMode(gin.DebugMode)
	// 初始化數據庫
	db := models.InitDB(conf)
	defer db.Close()

	port := conf.Section("").Key("http_host").String() +":" + conf.Section("").Key("http_port").String()
	if err = r.Run(port); err != nil{
		fmt.Println("服務端啟動錯誤:",err)
	}
}

models/initDB.go

package models

import (
	"github.com/gogf/gf/os/gfile"
	"github.com/jinzhu/gorm"
	"gopkg.in/ini.v1"
	"log"
	"os"
	"path"

	// 加載MySQL數據庫驅動
	_ "github.com/go-sql-driver/mysql"

	// 加載sqlite3數據庫驅動
	//_ "github.com/mattn/go-sqlite3"
)

// 全局db對象
var db *gorm.DB

func InitDB(conf *ini.File) *gorm.DB {
	var err error

	// 數據庫類型:mysql/sqlite3
	// db_type = "mysql"
	dbType := conf.Section("").Key("db_type").String()

	// mysql配置信息
	mysqlName := conf.Section("mysql").Key("db_name").String()       // 數據庫名稱
	mysqlUser := conf.Section("mysql").Key("db_user").String()       // 用戶名
	mysqlPwd := conf.Section("mysql").Key("db_pwd").String()         // 密碼
	mysqlHost := conf.Section("mysql").Key("db_host").String()       // 數據庫ip地址
	mysqlPort := conf.Section("mysql").Key("db_port").String()       // 數據庫端口
	mysqlCharset := conf.Section("mysql").Key("db_charset").String() // 指定字符集

	// sqlite3配置信息
	sqliteName := conf.Section("sqlite3").Key("db_name").String()

	var dataSource string
	switch dbType {
	case "mysql":
                // parseTime=true 表示自動解析為時間
		dataSource = mysqlUser + ":" + mysqlPwd + "@tcp(" + mysqlHost + ":" + mysqlPort + ")/" + mysqlName + "?charset=" + mysqlCharset + "&parseTime=true"
		db, err = gorm.Open(dbType,dataSource)
	case "sqlite3":
		dataSource = "database" + string(os.PathSeparator) + sqliteName
		if !gfile.Exists(dataSource){
			os.MkdirAll(path.Dir(dataSource),os.ModePerm)
			os.Create(dataSource)
		}
		db, err = gorm.Open(dbType,dataSource)
	}

	if err != nil{
		db.Close()
		log.Println("數據庫加載失敗",err)
	}

        // 設置數據庫操作顯示原生SQL 語句
	Db.LogMode(true)

        // 數據庫表動態遷移
	db.AutoMigrate(&auth.User{})

	// 設置數據庫連接池空閑連接數
	db.DB().SetConnMaxIdleTime(50)

	// 打開連接
	db.DB().SetMaxOpenConns(100)
        

	// 表明禁用后綴加s
	db.SingularTable(true)

        
	return db
}

config/myconf.ini配置文件

#數據庫類型:mysql/sqlite3
db_type = "mysql"

[mysql]
db_alias = "default"
db_name = "test"
db_user = "root"
db_pwd = "123"
db_host = "127.0.0.1"
db_port = 3306
db_charset = "utf8"


[sqlite3]
db_alias = "default"
db_name = "gin_test.db"

5. 簡單的CRUD

3.1 創建

Create()

user := User{Name:"jinzhu",Age:18,Birthday:time.Now()}
result := db.Create(&user)    // 通過結構體數據指針創建


user.ID  // 返回插入數據的主鍵
result.Error   // 返回 error
result.RowsAffected   // 返回插入記錄的條數

指定字段創建

db.Select("Name","Age","CreatedAt").Create(&user)

忽略指定字段創建

db.Omit("Name","Age","CreatedAt").Create(&user)

3.2 查詢

3.2.1 Find()

Find() 參數被調用會將查詢到的內容賦值給結構體,
Where() 不會,只是個查詢條件,需要配合Find(),First(),Last()等語句將查詢結構輸出

基本使用

var user models.User

finiedUser := db.Find(&user,"id = ?",1)

參數說明:

1. &user 為表結構體地址,
2. "id = ?" 為查詢條件
3. 1   為查詢條件的值

finiedUser.RowsAffected    // 影響了多少行
finiedUser.Error  // 錯誤信息

# 使用Find 查詢不到數據不會報錯,
# 使用first 查詢不到數據會報  ErrRecordNotFound 錯誤

var (
	// ErrRecordNotFound returns a "record not found error". Occurs only when attempting to query the database with a struct; querying with a slice won't return this error
	ErrRecordNotFound = errors.New("record not found")
	// ErrInvalidSQL occurs when you attempt a query with invalid SQL
	ErrInvalidSQL = errors.New("invalid SQL")
	// ErrInvalidTransaction occurs when you are trying to `Commit` or `Rollback`
	ErrInvalidTransaction = errors.New("no valid transaction")
	// ErrCantStartTransaction can't start transaction when you are trying to start one with `Begin`
	ErrCantStartTransaction = errors.New("can't start transaction")
	// ErrUnaddressable unaddressable value
	ErrUnaddressable = errors.New("using unaddressable value")
)

gorm.ErrRecordNotFound 

user結構體被賦值前后

# User被查詢結果賦值前,為初始零值 
&{{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC <nil>} 0   }


# User被查詢結果賦值后 
&{{8 2021-12-27 08:50:05 +0000 UTC 2021-12-28 07:45:18 +0000 UTC <nil>} 0 xiaohong7  1234567}

3.2.2 where()

Find() 參數被調用會將查詢到的內容賦值給結構體,
Where() 不會,只是個查詢條件,需要配合Find(),First(),Last()等語句將查詢結構輸出

基本使用

var user models.User

db.Where("id = ?",1).Find(&user)

6. 簡單練習

4.1 用戶更改密碼

前端Json數據

{"id":8,"name":"xiaohong7","password":"xiaobai","newpassword":"1234567"}

后端邏輯處理

// 定義一個結構體,接收前端傳輸的更多的參數
type ChangeUser struct {
	NewPassword string `gorm:"column:newpassword" json:"newpassword" binding:"required"`
	models.User
}

func ChangePwdHandler(c *gin.Context) {
    var user models.User
    // 接收前端傳輸的Json數據,並一一對應
    var changeUser ChangeUser
    
    if err := c.ShouldBindJSON(&changeUser); err != nil {
        log.Println("序列化錯誤!!!!",err)
        utils.Json(c,consts.BADREQUESTCODE,consts.BADREQUESTERROR,&defaultUser)
        return
    }
    
    // 查詢需要修改密碼的用戶是否在用戶表中,
    finiedUser := models.Db.Find(&user,"id = ?",changeUser.Id)
    if finiedUser.RowsAffected != 0{
        // 查詢到了, 更新舊密碼為新密碼
        updatedUser := finiedUser.Select("PassWord").Update(&models.User{PassWord: changeUser.NewPassword})
        utils.Json(c,consts.OKCODE,consts.OK,updatedUser)
    }else{
        // 沒查詢到

	utils.Json(c,consts.NOTFINEDCODE,consts.NOTFINEDERROR,&defaultUser)
    }

  
}

4.2 時間類型的格式化

type MyTime struct {
	time.Time
}
func (m *MyTime) MarshalJSON() ([]byte,error) {
	formatted := fmt.Sprintf(`"%s"`,m.Format("2006-01-02 15:04:05"))
	fmt.Println("正在解析>>>>",formatted)
	return []byte(formatted),nil
}
func (t MyTime) Value() (driver.Value, error) {
	var zeroTime time.Time
	if t.Time.UnixNano() == zeroTime.UnixNano() {
		return nil, nil
	}
	return t.Time, nil
}

func (t *MyTime) Scan(v interface{}) error {
	value, ok := v.(time.Time)
	if ok {
		*t = MyTime{Time: value}
		return nil
	}
	return fmt.Errorf("can not convert %v to timestamp", v)
}

// 代替gorm.Model
type BaseModel struct {
	CreatedAt MyTime `gorm:"column:created_at" json:"created_at"`
	UpdatedAt MyTime `gorm:"column:updated_at" json:"updated_at"`
	DeletedAt MyTime `gorm:"autoUpdateTime:nano"`
}

4.3 根據不同的結構體返回不同的數據

// 定義需要返回的數據的結構

type FinedUser struct {
    ID       int    `gorm:"column:id;parimary_key" json:"id"`
    Name     string `gorm:"column:name;unique;omitempty" json:"name;omitempty" binding:"required"`
    Accounts string `gorm:"column:accounts;omitempty" json:"accounts;omitempty"`
    Level string `gorm:"column:level;omitempty" json:"level;omitempty"`
    BaseUserModel
}

// 查詢語句,通過Scan指定,會根據FinedUser 結構體,返回對應結構體的數據
objDb = Db.Table("user").Where("name = ? AND password = ?", user.Name,user.PassWord).Scan(&finiedUser2)

問題集錦

1. 數據庫的表已經建成后,如果添加新的字段,並設置此字段默認值時,默認值設置不會生效


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM