直接回答:GORM 保存对象时会连带自动创建关联的子对象,如果不想要这种行为,就得在 BeforeCreate 钩子里自己判断处理,或者关掉关联的自动保存。

关于多对多,插入对象,子对象会自动创建的问题

,将子对象的BeforeCreate钩子里进行一次,判断,如果id>0那么就查询下数据库,将数据库的值给到对象,如果查询不到就报错

1
2
3
4
5
6
7
8
func (entity *User) BeforeCreate(tx *gorm.DB) (err error) {
if entity.ID > 0 {
tx.First(entity)
return err
}
entity.ID = Model.GetId()
return err
}

实体对象在数据库不能为空,则需要类型为引用类型,否则可能传入空字符串或者0值

可以为引用类型的目的是为了能设置为0,false,空字符串,如果数据库里允许有零值,就应该用引用类型,比如姓名,如果不是引用类型,更新的话也不会将数据库改为空

Model()函数什么时候用

  1. 对于查询来说,一般使用Find,First就够了。当查询链中没有用到Find,First等函数时,这时就无法指定要查询的表了,此时就要用Model来指定表。

  2. 更新的函数的参数是字段值,不能确定是哪个表,这时候也需要用到Model

使用Model还能指定查询条件,但是这个条件只能是id,不能使用其他条件

table和select不能和Find一起用,因为Find本身就带有指向表和查找表中字段的sql语句

Join Preload适用于一对一的关系,例如:has one, belongs to

在product模型中关联image表的时候使用belongsTo,因为此时外键img_id字段,在做为主表的product中。

在image模型中关联product表的时候使用hasOne,因为此时外键img_id字段,在做为从表的product中。

简单说,使用belongsTo还是hasOne,就看外键是处于当前模型的主表还是从表中。 ———————————————— 版权声明:本文为CSDN博主「小呆瓜与小胖妞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_42854221/article/details/82918028

关于预加载Preload 结果是嵌套

Find谁就是把谁都取出来,然后看Preload的谁在find的结果里

1
2
3
4
5
type DataNode struct {
WarnPlaneList121 []*WarnPlane `gorm:"many2many:DataNode_warnPlane;"`
}
db := DB.GetDB().Model(&Entity.DataNode{})
db = db.Preload("WarnPlaneList121") // 这里应该是对象,是上面那个Model(&Entity.DataNode{})中DataNode的对象

关于关联Association 结果是列表

取出一个user,看都哪个languages属于这个user,跟上面那个的区别就是这个是列表,上面的是嵌套

类似于右链接,Preload类似于左链接

1
db.Model(&user).Association("Languages").Find(&languages)

子查询

1
2
3

sub := db.Table("table2").Select("ID").Where("col1 = ?", 'val1').SubQuery()
err := db.Table("table1").Where("col2 NOT IN ?", sub).Find(&table1Type).Error

立即执行方法

Create,First,Find,Take,Save,UpdateXXX,Delete,Scan,Row,Rows

上面的方法会立即生成sql语句,并进入数据库进行操作

默认值

您可以通过标签 default 为字段定义默认值,如:

1
2
3
4
5
type User struct {
ID int64
Name string `gorm:"default:galeone"`
Age int64 `gorm:"default:18"`
}

插入记录到数据库时,默认值 会被用于 填充值为 零值 的字段

注意 对于声明了默认值的字段,像 0、''、false 等零值是不会保存到数据库。您需要使用指针类型或 Scanner/Valuer 来避免这个问题,例如:

1
2
3
4
5
6
type User struct {
gorm.Model
Name string
Age *int `gorm:"default:18"`
Active sql.NullBool `gorm:"default:true"`
}

注意 若要数据库有默认、虚拟/生成的值,你必须为字段设置 default 标签。若要在迁移时跳过默认值定义,你可以使用 default:(-),例如:

1
2
3
4
5
6
7
type User struct {
ID string `gorm:"default:uuid_generate_v3()"` // db func
FirstName string
LastName string
Age uint8
FullName string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
}

使用虚拟/生成的值时,你可能需要禁用它的创建、更新权限,查看 字段级权限 获取详情

创建时

字段不写,或者是0值,那么会在sql语句中排除掉,使用默认值(默认的默认值就是0值),默认值可以自定义

查询时

1
2
3
4
5
6
7
8
// 有效,因为通过 `db.Model()` 指定了 model
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

// 无效
result := map[string]interface{}{}
db.Table("users").First(&result) //这里的table只是指定了表,但是没指定模型,也就是struct

注意 当使用struct作为条件查询时,GORM 只会查询非零值字段。这意味着如果您的字段值为 0、''、false 或其他 零值,该字段不会被用于构建查询条件,用where构建字符串可以避免

更新时

注意 当通过 struct 更新时,GORM 只会更新非零字段。 如果您想确保指定字段被更新,你应该使用 Select 更新选定字段,或使用 map 来完成更新操作

更新选定字段

如果您想要在更新时选定、忽略某些字段,您可以使用 Select、Omit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 使用 Map 进行 Select
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;

db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// 使用 Struct 进行 Select(会 select 零值的字段)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})// Updates里如果不是User,是其他地定义的会如何
// UPDATE users SET name='new_name', age=0 WHERE id=111;

// Select 所有字段(查询包括零值字段的所有字段)
db.Model(&user).Select("*").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

// Select 除 Role 外的所有字段(包括零值字段的所有字段)
db.Model(&user).Select("*").Omit("Role").Update(User{Name: "jinzhu", Role: "admin", Age: 0})

更新为null

**数字**想要某项更新为null需要用Select选中需要更新的字段,传个nil就可以更新为空了,因为nil不确定是传了nil还是没传所以为nil,所以需要单独的接口去处理更新为null的问题

整体,不论数字还是字符串,应该在接收的时候,全部采用指针,这样才能知道是传了0值还是没传值

在数据库更新阶段只更新非nil的字段,结构体更新的时候需要用指针,这样才能将0值更新进去

实体对象字段为指针,如果修改的时候实体不传值,就会被更新为空,实体对象中的nil要慎用

更新阶段非必填要用引用接收,需要知道是传了还是没传,只要传的都要更新进去

查询阶段,非必填字段接收用引用,如果不是nil就用where构建

修改默认id类型

1
2
3
4
5
6
7
8
func (u *YS) BeforeCreate(*gorm.DB) (err error) {
// 如果已经有id了就不再重新赋值id了,否则会产生新的一条数据
if u.ID > 0 {
return err
}
u.ID = Model.GetId()
return err
}

这里用的钩子,在创建的时候通过雪花算法获取的id

每个设备的每个通道会有多个需要判断的事件

订阅中收到设备id,就组合通道从下面表里去查询,预算,查不到写入日志“某设备的某通道没有预警方案”

运算的时候出错也写入日志“某设备某通道的预警方案id运算时候出错”

预警报警key-->

设备id+通道名称:预警方案列表

1
docker run -d --name=webdav-aliyundriver --restart=always -p 49157:8080  -v /etc/localtime:/etc/localtime -v /volume2/docker/Aliyun/:/etc/aliyun-driver/ -e TZ="Asia/Shanghai" -e ALIYUNDRIVE_REFRESH_TOKEN="替换成你自己的 token" -e ALIYUNDRIVE_AUTH_PASSWORD="******" -e JAVA_OPTS="-Xmx1g" zx5253/webdav-aliyundriver

这篇笔记整理自我自己的实践记录,如果做法有出入,或者你踩过别的坑,欢迎到留言板一起聊聊。

站内搜索

没有找到内容!