314 lines
11 KiB
Markdown
314 lines
11 KiB
Markdown
- [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`: 对于每个Model,gorm中将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 > 13</code>, refer <a href="constraints.html">Constraints</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td><-</td>
|
||
<td>set field’s write permission, <code><-:create</code> create-only field, <code><-:update</code> update-only field, <code><-:false</code> no write permission, <code><-</code> create and update permission</td>
|
||
</tr>
|
||
<tr>
|
||
<td>-></td>
|
||
<td>set field’s read permission, <code>->: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>
|
||
|
||
|