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. 缺點
- 自動生成
SQL
語句會消耗計算資源,這勢必會對程序性能造成一定的影響 - 對於復雜的數據庫操作,
ORM
通常難以處理,自動生成的SQL
語句在性能方面也不如手寫的原生SQL
- 生成
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. 數據庫的表已經建成后,如果添加新的字段,並設置此字段默認值時,默認值設置不會生效