ORM
全名为Object-Relational Mapping 物件关系对应,也就是透过物件导向的方式将关系资料库的资料映射至物件的技术。
那ORM有三大核心准则分别是
ORM的使用也带来了以下优点:
同时ORM也隐含着以下缺点:
GORM是专门为Gin这个Framework所设计的ORM Library,主要用於操作资料库。这边我们会基於过往的MVC架构将其依不同功能分成dao、model以及service三层。
这边我们专门放连结database所用到的function与变数等。
dao/postgresql.go
package dao
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
var (
SqlSession *gorm.DB
)
func Initialize(dbConfig string) (*gorm.DB, error) {
var err error
SqlSession, err = gorm.Open(postgres.Open(dbConfig), &gorm.Config{})
return SqlSession, err
}
main.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"ironman-2021/app/config"
"ironman-2021/app/dao"
"ironman-2021/app/model"
"os"
)
func main() {
envErr := godotenv.Load()
if envErr != nil {
panic(envErr)
}
port := os.Getenv("PORT")
dbConfig := os.Getenv("DB_CONFIG")
db, ormErr := dao.Initialize(dbConfig)
if ormErr != nil {
panic(ormErr)
}
migrateErr := db.AutoMigrate(&model.User{})
if migrateErr != nil {
return
}
server := gin.Default()
server.GET("/hc", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "health check",
})
})
config.RouteUsers(server)
err := server.Run(":" + port)
if err != nil {
panic(err)
}
}
godotenv
得到环境变数DB_CONFIG
Initialize()
来连接上资料库这边则负责堆放需要在database所创建的table资料
model/user.go
package model
import (
"gorm.io/gorm"
"time"
)
type User struct {
gorm.Model
ID int64 `gorm:"primary_key;auto_increment" json:"id"`
Account string `gorm:"size:100;not null;unique" json:"account"`
Password string `gorm:"size:100;not null;" json:"password"`
Email string `gorm:"size:100;not null;unique" json:"email"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP" json:"updated_at"`
}
我们create了一个名为User的gorm.Model
,然後上面的primary key、field如程序码所述,这边就不在多做赘述。
这里则是关於orm相关的变数以及function,这边也是主要对资料库进行资料CRUD的地方。
service/user.go
package service
import (
"fmt"
"ironman-2021/app/dao"
"ironman-2021/app/model"
)
var UserFields = []string{"id", "account", "email"}
func SelectOneUsers(id int64) (*model.User, error) {
userOne:=&model.User{}
err := dao.SqlSession.Select(UserFields).Where("id=?", id).First(&userOne).Error
if err != nil {
return nil, err
} else {
return userOne, nil
}
}
func RegisterOneUser(account string, password string, email string) error {
if !CheckOneUser(account) {
return fmt.Errorf("User exists.")
}
user := model.User{
Account: account,
Password: password,
Email: email,
}
insertErr := dao.SqlSession.Model(&model.User{}).Create(&user).Error
return insertErr
}
func CheckOneUser(account string) bool {
result := false
var user model.User
dbResult := dao.SqlSession.Where("account = ?", account).Find(&user)
if dbResult.Error != nil {
fmt.Printf("Get User Info Failed:%v\n", dbResult.Error)
} else {
result = true
}
return result
}
UserFields
为我们SelectOneUsers()
所要调用的User table中的fieldSelectOneUsers()
为利用id query User table的orm functionRegisterOneUser()
为透过account, password以及email去创建一笔新资料在User table的orm functionCheckOneUser()
则为检查该account是否已经存在於User table的orm function最後则是这次新增的controller directory,这边主要是用来撰写API逻辑的地方,举例来说一次的GET DATA API,我们会将其切割为routing, business logic, orm query三大部分,controller这层就是用来实现business logic的地方
controller/user.go
package controller
import (
"github.com/gin-gonic/gin"
"ironman-2021/app/service"
"net/http"
"strconv"
)
type UsersController struct {}
func NewUsersController() UsersController {
return UsersController{}
}
func QueryUsersController() UsersController {
return UsersController{}
}
type Register struct {
Account string `json:"account" binding:"required" example:"account"`
Password string `json:"password" binding:"required" example:"password"`
Email string `json:"email" binding:"required" example:"[email protected]"`
}
func (u UsersController) CreateUser (c *gin.Context){
var form Register
bindErr := c.BindJSON(&form)
if bindErr == nil {
err := service.RegisterOneUser(form.Account, form.Password, form.Email)
if err == nil {
c.JSON(http.StatusOK, gin.H{
"status": 1,
"msg": "success Register",
"data": nil,
})
} else {
c.JSON(http.StatusInternalServerError, gin.H{
"status": -1,
"msg": "Register Failed" + err.Error(),
"data": nil,
})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{
"status": -1,
"msg": "Failed to parse register data" + bindErr.Error(),
"data": nil,
})
}
}
func (u UsersController) GetUser (c *gin.Context){
id := c.Params.ByName("id")
userId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"status": -1,
"msg": "Failed to parse params" + err.Error(),
"data": nil,
})
}
userOne, err := service.SelectOneUsers(userId)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{
"status": -1,
"msg": "User not found" + err.Error(),
"data": nil,
})
} else {
c.JSON(http.StatusOK, gin.H{
"status": 0,
"msg": "Successfully get user data",
"user": &userOne,
})
}
}
由於这次范例并没写什麽商业逻辑,就只做简单的call service function进行data的create与query而已,故不在赘述!
这章节我们从分割程序码结构、如何使用使用orm并串接上API做了一次范例给大家看,这次的code也会放在以下连结,有兴趣者可以参考!
https://github.com/Neskem/Ironman-2021/tree/Day-18
>>: Day 18: LeetCode 322. Coin Change
Tasks: Initialize Cloud Monitoring. Navigation men...
今天来了解在 TypeScript 中使用 Class,Class Member 包含了: Fie...
Football Betting - Making Sense of the Odds Footba...
昨天说的是单一档案上传,如果要多个档案上传的话... 建立新栏位 资料表先新增可以储存一个阵列的栏位...
重复影像 background-repeat 这个属性可以重复图像在背景 如果是使用小图做素材 可以...