数据库初始化 GORM

72 阅读4分钟

GORM 是 Golang 的一个 orm 框架。简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用 ORM框架可以让我们更方便的操作数据库。 GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server

安装

go get -u gorm.io/gorm

# GORM 官方支持 sqlite、mysql、postgres、sqlserver
go get -u gorm.io/driver/mysql 

config.yaml 配置项

配置连接数据库

mysql:
  host: 127.0.0.1
  port: 3306
  user: root
  password: 123456
  database: demo
  charset: utf8mb4
type MySQLInfo struct {
	Host     string `mapstructure:"host"`
	Port     int    `mapstructure:"port"`
	User     string `mapstructure:"user"`
	Password string `mapstructure:"password"`
	Database string `mapstructure:"database"`
	Charset  string `mapstructure:"charset"`
}

初始化数据库 db.go

// main.go
// 初始化数据库
config.InitializeDB()
config.TableAutoMigrate()
package config

import (
	"leo-gin/models"
	"strconv"

	"go.uber.org/zap"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

var DB *gorm.DB

func InitializeDB() *gorm.DB {
	dbConfig := config.Conf.MySQL
	if dbConfig.Database == "" {
		return nil
	}
	dsn := dbConfig.User + ":" + dbConfig.Password + "@tcp(" + dbConfig.Host + ":" + strconv.Itoa(dbConfig.Port) + ")/" + dbConfig.Database + "?charset=" + dbConfig.Charset + "&parseTime=True&loc=Local"
	mysqlConfig := mysql.Config{
		DSN:                       dsn,   // DSN data source name
		DefaultStringSize:         191,   // string 类型字段的默认长度
		DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
		DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
		SkipInitializeWithVersion: false, // 根据版本自动配置
	}
	if db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true, // 禁用自动创建外键约束
	}); err != nil {
		logger.Log.Error("mysql connect failed, err:", zap.Any("err", err))
		return nil
	} else {
		sqlDB, _ := db.DB()
		sqlDB.SetMaxIdleConns(10)  // 空闲连接池中连接的最大数量
		sqlDB.SetMaxOpenConns(100) // 打开数据库连接的最大数量
		DB = db
		logger.Log.Info("mysql connect success")
		return db
	}
}

func TableAutoMigrate() {
  if DB == nil {
		return
	}
	err := DB.AutoMigrate(&models.User{})
	if err != nil {
		logger.Log.Error("AutoMigrate failed, err:", zap.Any("err", err))
	} else {
		logger.Log.Info("AutoMigrate success")
	}
}

定义数据库模型 创建user model

  1. 结构体的名称必须首字母大写 ,并和数据库表名称对应。例如:表名称为 user 结构体名称定义成 User,表名称为 article_cate 结构体名称定义成 ArticleCate
  2. 结构体中的字段名称首字母必须大写,并和数据库表中的字段一一对应。例如:结构体中的 Id 和数据库中的 id 对应,Username 和数据库中的 username 对应,AddTime 和数据库中的 add_time 字段对应
  3. 默认情况表名是结构体名称的复数形式。如果我们的结构体名称定义成 User,表示这个模型默认操作的是 users 表
  4. 使用结构体中的自定义方法 TableName 改变结构体的默认表名称
// 自增ID主键
type ID struct {
	ID uint `json:"id" gorm:"primaryKey"`
}

// 创建、更新时间
type Timestamps struct {
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

// 软删除
type SoftDeletes struct {
	DeletedAt gorm.DeletedAt `json:"deleted_at" gorm:"index"`
}

type User struct {
	ID
	Name     string `json:"name" gorm:"not null;comment:用户名称"`
	Mobile   string `json:"mobile" gorm:"not null;index;comment:用户手机号"`
	Password string `json:"password" gorm:"not null;default:'';comment:用户密码"`
	Timestamps
	SoftDeletes
}

写入数据

func Register(c *gin.Context) {
	var req RegisterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	user := models.User{
		Name: req.Name,
		Mobile:  req.Mobile,
    Password: req.Password,
	}
	if err := database.DB.Create(&user).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "用户保存失败"})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"message": "注册成功",
		"user_id": user.ID,
	})
}

删除数据

r.DELETE("/usr/delete/:id", func(c *gin.Context) {
    var data []models.User
    id := c.Param("id")
    database.DB.Where("id=?", id).Find(&data)
    if len(data) == 0 { // if not found id
        c.JSON(200, gin.H{
            "message": "delete failed,not found id",
            "code":    "400",
        })
    } else {
        database.DB.Where("id=?", id).Delete(&data)
        c.JSON(200, gin.H{
            "message": "delete succeeded",
            "code":    "200",
        })
    }
})

修改数据

r.PUT("/usr/update/:id", func(c *gin.Context) {
    var data models.User
    id := c.Param("id")
    database.DB.Select("id").Where("id=?", id).Find(&data)
    if data.ID == 0 { // if not found id
        c.JSON(200, gin.H{
            "message": "Put failed,not found id",
            "code":    "400",
        })
    } else { // ID is already
        err := c.ShouldBindJSON(&data)
        if err != nil {
            // put failed
            c.JSON(200, gin.H{
                "message": "put failed",
                "code":    "400",
            })
        } else {
            database.DB.Where("id = ?", "id").Updates(&data)
            c.JSON(http.StatusOK, gin.H{
                "message": "put succeeded",
                "code":    "200",
            })
        }
    }
})

查询数据

r.GET("/usr/list/:name", func(c *gin.Context) {
    name := c.Param("name")
    var data []models.User
    database.DB.Where("name=?", name).Find(&data)
    if len(data) == 0 {
        c.JSON(200, gin.H{
            "message": "not found",
            "code":    "400",
            "data":    gin.H{},
        })
    } else {
        c.JSON(200, gin.H{
            "message": "get successfully",
            "code":    "200",
            "data":    data,
        })
    }
})

分页数据

r.GET("/usr/list", func(c *gin.Context) {
    var datalist []models.User
    pageSize, _ := strconv.Atoi(c.Query("pageSize"))
    pageNum, _ := strconv.Atoi(c.Query("pageNum"))
​
    if pageSize == 0 {
        pageSize = -1
    }
    if pageNum == 0 {
        pageNum = -1
    }
​
    // get all data
    var total int64
    database.DB.Model(datalist).Count(&total).Limit(pageSize).Offset(pageNum).Find(&datalist)
    if len(datalist) == 0 {
        c.JSON(200, gin.H{
            "message": "get failed,no data",
            "code":    "400",
            "data":    gin.H{},
        })
    } else {
        c.JSON(200, gin.H{ //return datalist json response
            "message": "get succeed",
            "code":    "200",
            "data": gin.H{
                "list":     datalist,
                "total":    total,
                "pageNum":  pageNum,
                "pageSize": pageSize,
            },
        })
    }
})

gorm表关联查询

一对多

type Comment struct {
	gorm.Model
	Content    string
	ParentId   uint   	// 父评论ID
	UserID     uint   	// 用户ID
	ReplyName  string 	// 回复者名字
	UserName   string
	UserAvatar string
	SocialId   uint  `gorm:"foreignkey:Social;association_jointable_foreignkey:social_id"` //社区帖子ID
	Children   []Comment `gorm:"foreignkey:ParentId"`
}

多对多

type User struct {
	gorm.Model
	Relations 	   []User `gorm:"many2many:relation; association_jointable_foreignkey:relation_id"`
	UserName       string
	Email          string 	//`gorm:"unique"`
	Avatar         string 	`gorm:"size:1000"`
	Phone 		   string
	BanTime 	   int
	OpenID 		   string 	`gorm:"unique"`
}