GORM 最佳实践(1) | 青训营笔记
- 数据序列化与 SQL 表达式
- 批量数据操作
- 代码复用、分库分表、Sharding
- 混沌工程/压测
- Logger/Trace
- Migrator
- Gen 代码生成/Raw SQL
- 安全
数据序列化与 SQL 表达式 - SQL 表达式更新创建
方法1 : 通过 gorm.Expr 使用 SQL 表达式
db.Model(User{}).Create(map[string]interface{}){
"Name":"hzhan",
"Location":gorm.Expr("ST_PointFromText(?)","POINT(100 100)"),
})
db.Model(&product).Update("price",gorm.Expr("price * ? + ?", 2, 100))
db.Model(User{})创建了一个新的 User 对象并将其写入到数据库中。Create() 方法的第一个参数是指定要创建记录的数据模型类型。第二个参数是一个 map,表示要写入数据库的记录的属性值。其中 "Name" 是一个字符串属性,值为 "hzhan"。"Location" 属性使用了一个SQL 表达式来计算它的值。这个表达式使用了 PostGIS 中的 ST_PointFromText() 函数将字符串 "POINT(100 100)" 转换为点坐标值。
db.Model(&product)更新了一个已存在的 product 对象的 "price" 属性的值。Model() 方法的参数是指定要操作的记录的数据模型类型或指针。Update() 方法的第一个参数是一个字符串,表示要更新的属性名。第二个参数是一个 SQL 表达式,它使用了一个乘法运算和一个加法运算来计算新的 "price" 值。这个表达式中的 "?" 是一个占位符,它会被后面的参数依次替换。第一个替换参数是乘数 "2",第二个替换参数是加数 "100"。
方法2 : 使用 GOPMValuer 使用 SQL 表达式 / SubQuery
type Location struct {
X, Y int
}
func(loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
return gorm.Expr("ST_PointFromText(?)", fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y))
}
db.Create(User{Name: "hzhan", Location: Location{X: 100, Y: 100}})
db.Model(&User{ID: 1}.Uptates(User{Name: "jinzhu", Location: Location{X: 100, Y: 100}}))
定义了一个结构体Location,表示一个位置,包含了X坐标和Y坐标。
定义了一个方法GormValue,用于将一个Location类型的值转换为SQL语句的表达式,该表达式表示一个点的坐标。该方法使用了ST_PointFromText函数将点的坐标转换为SQL的点类型。
使用db.Create方法创建了一个名为"hzhan"的用户,并设置其位置为(100, 100)。
使用db.Model方法对ID为1的用户进行更新操作,将其名称更新为"hzhan",位置更新为(100,100)。
方法3 : 通过 *gorm.DB 使用 SubQuery
subQuery := db.Model(&Company{}).Select("name").Where("companies.id = users.company_id")
db.Model(&user).Updates(map[string]interface{}{}{"company_name": subQuery})
定义一个 subQuery 变量来表示一个子查询,该查询使用 db.Model 方法从 Company 模型中选择名称(name)字段,并使用 Where 方法指定了条件,即只选择 id 等于用户所属公司 id 的记录。这个子查询将会在后续中作为更新数据的值使用。
接下来,使用 db.Model 方法指定要更新的用户模型(&user),并使用 Updates 方法更新数据。Updates 方法的参数是一个键值对 map,键为要更新的字段("company_name"),值为需要更新到的值。这个值通过 subQuery 子查询获取,实现了将公司名称更新为用户所属公司的名称。
数据序列化与 SQL 表达式 - SQL 表达式查询
方法1 : 使用 gorm.Expr
db.Where("location = ?", gorm.Expr("ST_PointFromText(?)","POINT(100 100)")).First(&user)
在db(数据库)对象上调用Where()方法。这个方法允许在数据库上进行有条件的查询。在这种情况下,条件被指定为一个字符串("location = ?"),这意味着数据库中的位置列应该等于字符串后面的那个问号的值。接下来的值是由GORM的Expr()函数生成的。这个函数接受包含SQL表达式和应该插入这个表达式的值的字符串。在这种情况下,表达式是"ST_PointFromText(?)",这是一个PostGIS函数,用于从WKT(well-known text)字符串创建一个点对象。将插入表达式的值是"POINT(100 100)",表示二维平面上坐标为(100,100)的点。使用First()方法执行查询并获取符合条件的第一个用户。这个用户被存储在user变量中。
方法2 : Struct 定义 GormValuer
func(loc Location) GormValue(ctx context.Context, db *gorm.DB)clause.Expr{
return gorm.Expr("ST_PointFromText(?)", fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y))
}
db.Where("location = ?", Location{X: 100, Y: 100}).First(&user)
db.Where()方法指定了查询条件,即通过location字段查询到某个具体的位置坐标。
"location = ?"是查询条件的SQL语句,其中?以后的参数将被替换成下一参数的具体值。这里的Location{X: 100, Y: 100}就是下一参数的具体值。
Location{X: 100, Y: 100}是一个自定义的类型,表示一个二维坐标系中的位置坐标。这个自定义类型中,包含了X和Y两个字段。
First(&user)意味着只查询符合条件的第一个用户数据,并将查询结果填入user变量中。
func(loc Location) GormValue(ctx context.Context, db *gorm.DB)clause.Expr{}是一个自定义函数,用于将Location类型的值转换成数据库中适用的表达式。ST_PointFromText()和POINT()都是PostGIS的内置函数,用于处理空间位置坐标。其中,格式化字符串"POINT(%d %d)"用来将X和Y两个字段的值转换成数据库所需要的形式。
方法3 : 自定义查询 SQL 实现接口 clause.Expression
type Expression interface{
Build(builder Builder)
}
db.Find(&user, dataypes.JSONQuery("attributes").HasKey("role"))
db.Clauses(datatpyes.JSONQuery("attridutes").HasKey("org", "name")).Find(&user)
datatypes.JSONQuery("attributes").HasKey("role"):构建了一个 JSON 查询条件,查询属性中是否包含键为 "role" 的字段。
db.Find(\&user, ...):使用 JSON 查询条件进行查询,将查询结果赋值给 user 对象。
datatypes.JSONQuery("attributes").HasKey("org", "name"):构建了一个 JSON 查询条件,查询属性中是否同时包含键为 "org" 和 "name" 的字段。
db.Clauses(datatypes.JSONQuery("attributes").HasKey("org", "name")).Find(\&user):使用 JSON 查询条件进行查询,将查询结果赋值给 user 对象。此处使用了 Clauses 方法将查询条件传递给 GORM。
方法4 : SubQuery
db.Where("name in (?)",db.Model(&User{}).Select("name").Where("id > 10")).Find(&user)
使用db.Where()函数指定查询条件,其中name in (?)表示查询名字在某一集合中的所有用户。问号部分用于后面给定的具体条件。
在问号的位置,使用了db.Model()函数指定了查询对象为User模型。然后使用.Select()函数,将name属性选出来。这样做是为了获取所有用户的名字集合。在.Select()函数指定的name属性上,使用.Where()函数指定一个额外的条件,即id > 10。这样做是为了过滤一些user对象,只有id大于10的才会被用于查询名字集合。最后将得到的名字集合作为参数,传给db.Where()函数。这样可以得到所有满足条件的用户对象,并将它们存储到已定义的user变量中。