Files
rikako-note/Golang/gorm.md
2025-01-15 22:59:21 +08:00

314 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [GORM](#gorm)
- [QuickStart](#quickstart)
- [Declaring Models](#declaring-models)
- [约定](#约定)
- [gorm.Model](#gormmodel)
- [Advanced](#advanced)
- [Field Level Permission](#field-level-permission)
- [CreatedAt / UpdatedAt](#createdat--updatedat)
- [struct embedded](#struct-embedded)
- [field tags](#field-tags)
# GORM
## QuickStart
GORM为golang的orm框架其使用示例如下
```go
package main
import (
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
db.AutoMigrate(&Product{})
// Create
db.Create(&Product{Code: "D42", Price: 100})
// Read
var product Product
db.First(&product, 1) // find product with integer primary key
db.First(&product, "code = ?", "D42") // find product with code D42
// Update - update product's price to 200
db.Model(&product).Update("Price", 200)
// Update - update multiple fields
db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields
db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})
// Delete - delete product
db.Delete(&product, 1)
}
```
## Declaring Models
gorm中Model通过struct来进行定义struct中的fields可以为如下类型
- 基础golang类型
- 指针
- type alias
- 自定义类型
- 自定义类型需要实现`database/sql`中的Scanner和Valuer接口
如下为一个Model定义的示例
```go
type User struct {
ID uint // Standard field for the primary key
Name string // A regular string field
Email *string // A pointer to a string, allowing for null values
Age uint8 // An unsigned 8-bit integer
Birthday *time.Time // A pointer to time.Time, can be null
MemberNumber sql.NullString // Uses sql.NullString to handle nullable strings
ActivatedAt sql.NullTime // Uses sql.NullTime for nullable time fields
CreatedAt time.Time // Automatically managed by GORM for creation time
UpdatedAt time.Time // Automatically managed by GORM for update time
ignored string // fields that aren't exported are ignored
}
```
在上述Model中
- 基础类型的字段可以直接访问,例如`uint, uint8, string`
- 指针类型,例如`*time.Time, *string`等,代表该字段值可为空
- `sql/database`中的`sql.NullString``sql.NullTime`类型,也代表该字段可为空
- `CreatedAt`字段和`UpdatedAt`字段是由Gorm管理的字段当Record被创建或更新时该字段的值会自动被注入为当前时间
- `Non-exported fields`(首字母不是大写)并不会被映射
### 约定
- `Primary Key`: 对于每个Modelgorm中将name为`ID`的field看作默认primary key
- `Table names` 默认情况下gorm将struct name转换为`snake_case`,并将`snake_case``复数化`作为表名。例如,`User`struct对应的表名为`users``GormUserName`对应的表名为`gorm_user_names`
- `Column Names`: gorm会自动将field name转化为`snake_case`,用于表中字段名的映射
- `Timestamp Field`: gorm默认使用`CreatedAt``UpdatedAt`字段来跟踪数据的创建和更新日期。
上述是gorm管理实体时的默认约定但是除了默认约定外gorm也支持对实体进行自定义配置。
### gorm.Model
gorm提供了一个预先定义的实体`gorm.Model`,其中包含了常用字段:
```go
// gorm.Model definition
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
```
在自定义实体时可以将Model实体作为一个field包含在自定义实体中那么Model中定义的实体也会自动包含在自定义实体中。
上述预定义字段含义如下:
- `ID`:每行数据的唯一标识符
- `CreatedAt`:行创建时间
- `UpdatedAt`:行最后更新时间
- `DeletedAt`:用于逻辑删除
### Advanced
#### Field Level Permission
对于Exported Fields以大写开头的Field在执行CRUD时GORM默认用友所有权限。但是gorm允许通过`tag`来修改field level permission故而可以将field指定为`read-only, write-only, create-only, update-only, ignored`
> 当字段被标记为`ignored`时若使用gorm migrator创建表时`ignored fields不会被创建`。
```go
type User struct {
Name string `gorm:"<-:create"` // allow read and create
Name string `gorm:"<-:update"` // allow read and update
Name string `gorm:"<-"` // allow read and write (create and update)
Name string `gorm:"<-:false"` // allow read, disable write permission
Name string `gorm:"->"` // readonly (disable write permission unless it configured)
Name string `gorm:"->;<-:create"` // allow read and create
Name string `gorm:"->:false;<-:create"` // createonly (disabled read from db)
Name string `gorm:"-"` // ignore this field when write and read with struct
Name string `gorm:"-:all"` // ignore this field when write, read and migrate with struct
Name string `gorm:"-:migration"` // ignore this field when migrate with struct
}
```
#### CreatedAt / UpdatedAt
gorm默认使用CreatedAt/UpdatedAt来跟踪record创建和更新时间如果这些字段被定义将会在创建/更新操作时设置这些字段。
如果想要为Create/Update字段指定不同的名称可以为自定义字段指定`autoCreateTime``autoUpdateTime` tag。
如果想要为时间戳指定不同精度,可以按照如下格式进行定义:
```go
type User struct {
CreatedAt time.Time // Set to current time if it is zero on creating
UpdatedAt int // Set to current unix seconds on updating or if it is zero on creating
Updated int64 `gorm:"autoUpdateTime:nano"` // Use unix nano seconds as updating time
Updated int64 `gorm:"autoUpdateTime:milli"`// Use unix milli seconds as updating time
Created int64 `gorm:"autoCreateTime"` // Use unix seconds as creating time
}
```
#### struct embedded
在gorm中如果想要嵌套类可以使用如下方式
- 将匿名struct作为field
```golang
type Author struct {
Name string
Email string
}
type Blog struct {
Author
ID int
Upvotes int32
}
// equals
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}
```
- 将非匿名struct用`gorm:"embedded"`进行标记:
```golang
type Author struct {
Name string
Email string
}
type Blog struct {
ID int
Author Author `gorm:"embedded"`
Upvotes int32
}
// equals
type Blog struct {
ID int64
Name string
Email string
Upvotes int32
}
```
- 除了指定`gorm:"embeded"`tag外还可以为嵌套struct指定prefix
```golang
type Blog struct {
ID int
Author Author `gorm:"embedded;embeddedPrefix:author_"`
Upvotes int32
}
// equals
type Blog struct {
ID int64
AuthorName string
AuthorEmail string
Upvotes int32
}
```
#### field tags
当定义model时tag是可选的gorm支持tag如下所示。tag是大小写不敏感的更推荐使用驼峰的写法。
如果需要指定多个tags可以将多个tags根据`;`进行分隔gorm参数值的转义符为`\`字符。
<table>
<thead>
<tr>
<th>Tag Name</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>column</td>
<td>column db name</td>
</tr>
<tr>
<td>type</td>
<td>column data type, prefer to use compatible general type, e.g: bool, int, uint, float, string, time, bytes, which works for all databases, and can be used with other tags together, like <code>not null</code>, <code>size</code>, <code>autoIncrement</code>… specified database data type like <code>varbinary(8)</code> also supported, when using specified database data type, it needs to be a full database data type, for example: <code>MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT</code></td>
</tr>
<tr>
<td>serializer</td>
<td>specifies serializer for how to serialize and deserialize data into db, e.g: <code>serializer:json/gob/unixtime</code></td>
</tr>
<tr>
<td>size</td>
<td>specifies column data size/length, e.g: <code>size:256</code></td>
</tr>
<tr>
<td>primaryKey</td>
<td>specifies column as primary key</td>
</tr>
<tr>
<td>unique</td>
<td>specifies column as unique</td>
</tr>
<tr>
<td>default</td>
<td>specifies column default value</td>
</tr>
<tr>
<td>precision</td>
<td>specifies column precision</td>
</tr>
<tr>
<td>scale</td>
<td>specifies column scale</td>
</tr>
<tr>
<td>not null</td>
<td>specifies column as NOT NULL</td>
</tr>
<tr>
<td>autoIncrement</td>
<td>specifies column auto incrementable</td>
</tr>
<tr>
<td>autoIncrementIncrement</td>
<td>auto increment step, controls the interval between successive column values</td>
</tr>
<tr>
<td>embedded</td>
<td>embed the field</td>
</tr>
<tr>
<td>embeddedPrefix</td>
<td>column name prefix for embedded fields</td>
</tr>
<tr>
<td>autoCreateTime</td>
<td>track current time when creating, for <code>int</code> fields, it will track unix seconds, use value <code>nano</code>/<code>milli</code> to track unix nano/milli seconds, e.g: <code>autoCreateTime:nano</code></td>
</tr>
<tr>
<td>autoUpdateTime</td>
<td>track current time when creating/updating, for <code>int</code> fields, it will track unix seconds, use value <code>nano</code>/<code>milli</code> to track unix nano/milli seconds, e.g: <code>autoUpdateTime:milli</code></td>
</tr>
<tr>
<td>index</td>
<td>create index with options, use same name for multiple fields creates composite indexes, refer <a href="indexes.html">Indexes</a> for details</td>
</tr>
<tr>
<td>uniqueIndex</td>
<td>same as <code>index</code>, but create uniqued index</td>
</tr>
<tr>
<td>check</td>
<td>creates check constraint, eg: <code>check:age &gt; 13</code>, refer <a href="constraints.html">Constraints</a></td>
</tr>
<tr>
<td>&lt;-</td>
<td>set fields write permission, <code>&lt;-:create</code> create-only field, <code>&lt;-:update</code> update-only field, <code>&lt;-:false</code> no write permission, <code>&lt;-</code> create and update permission</td>
</tr>
<tr>
<td>-&gt;</td>
<td>set fields read permission, <code>-&gt;:false</code> no read permission</td>
</tr>
<tr>
<td>-</td>
<td>ignore this field, <code>-</code> no read/write permission, <code>-:migration</code> no migrate permission, <code>-:all</code> no read/write/migrate permission</td>
</tr>
<tr>
<td>comment</td>
<td>add comment for field when migration</td>
</tr>
</tbody></table>