Gorm — это фреймворк golang, который обеспечивает инкапсуляцию операций с базой данных и довольно удобен в использовании.
Однако при разработке проекта кода пишется больше, и обнаруживается, что еще остается место для повторной инкапсуляции, такой как добавление журналов ошибок или некоторых часто используемых условных запросов, запросов на подкачку и данных в одной таблице. .обновление и т.д. Кроме того, для одной и той же функциональной операции gorm также предоставляет различные методы реализации.Я немного запутался в новом обучении и не знаю, какой из них использовать.
Итак, основываясь на своем опыте и привычках кодирования в проекте, я сделал следующие расширения для вашей справки.
Подготовить
为了兼容gorm的使用方法,我使用了内嵌类型来扩展。 Он определяется следующим образом:
type DBExtension struct {
*gorm.DB
logger DBLogger
}
объекты-оболочки определяются таким образом, минимально инвазивное расширение, не только исходный метод точки непосредственно горм также может быть расширен, чтобы указать метод.
новый
Что касается новых данных, я рекомендую использоватьSaveметод, когда данные, соответствующие первичному ключу, не существуют, его эффект заключается в вставке новых данных, а когда данные, соответствующие первичному ключу, существуют, затемОбновить все поля,Скажи это снова,он обновит все поля!
Независимо от того, было ли поле изменено или является ли оно значением по умолчанию, определенным типом.
Еще раз обратите внимание: допустимо ли значение по умолчанию, обрабатывается по-разному в разных методах gorm, и нужно быть очень осторожным.
Например, если вы определяете структуру User с полем Age типа int. (Примечание. В следующих примерах эта структура определена по умолчанию.)
type User struct {
Id int `gorm:"column:id; type:int(11);primary_key"`
Name string `gorm:"column:name; type:varchar(32);"`
Age int `gorm:"column:age; type:int(11);"`
Description string `gorm:"column:description; type:varchar(512);"`
}
func (User) TableName() string {
return "test.user"
}
++ Обратите особое внимание на определение идентификатораprimary_key, 如果没有加个这个Save方法是无法正常工作的。 ++
Если во время определения возрасту не присвоено значение, возраст этих данных будет установлен на 0.
Для новых данных может и не проблема, а вот для обновления данных то это непонятный баг!
Итак, если у метода Save есть такая яма, зачем его использовать?
Проще говоря, суждение, которое не нужно отображать, заключается в том, что добавление данных и обновление данных может сделать код более кратким, а преимущества перевешивают недостатки, верно?
Код расширения выглядит следующим образом, добавляя некоторые суждения об ошибках и журналы:
type TableNameAble interface {
TableName() string
}
// Update All Fields
func (dw *DBExtension) SaveOne(value TableNameAble) error {
tableNameAble, ok := value.(TableNameAble)
if !ok {
return errors.New("value doesn't implement TableNameAble")
}
var err error
if err = dw.Save(value).Error; err != nil {
dw.logger.LogErrorc("mysql", err, fmt.Sprintf("Failed to save %s, the value is %+v", tableNameAble.TableName(), value))
}
return err
}
Используйте код следующим образом:
user1 := User{Id:1, Name:"Jeremy", Age: 30, Description: "A gopher"}
if err := dw.SaveOne(&instInfo); err != nil{
// error handling
return err
}
Когда запись не существует, выполняется инструкция Sql:
insert into test.user(id ,name, age, description) values(1, "Jeremy", 30, "A gopher")
Когда запись существует, выполняется следующий оператор:
update test.user set name = "Jeremy", age = 30, description = "A gohper" where id = 1
Написание нового таким образом также учитывает ситуацию с обновлением всех полей.Убивает ли это двух зайцев одним выстрелом?
PS: Если Id первичного ключа является автоинкрементным столбцом, нет необходимости присваивать значение Id при создании нового. Когда данные будут успешно вставлены, идентификатор данных будет автоматически обновлен до поля идентификатора, что особенно полезно в некоторых сценариях.
возобновить
Метод SaveOneПолное обновление суммыНо в большинстве случаев обновление может быть только частью поля данных, но также требуется обновить или перевернуть поле. В этой части операции горм при этом предоставляет ряд методов работы, но и наиболее запутанных.
В этом случае я обычно использую два способа справиться с этим: один — определить специальную структуру, такую как:
type UserDesc struct {
Id int `gorm:"column:id; type:int(11);primary_key"`
Description string `gorm:"column:description; type:varchar(512);"`
}
func (UserDesc) TableName() string {
return "test.user"
}
На этом этапе вы можете использовать метод SaveOne, чтобы обновить его следующим образом:
userDesc := UserDesc{Id:1, Description: "A programmer"}
if err := dw.SaveOne(&userDesc); err != nil{
// error handling
return err
}
Выполненный оператор sql:
update test.user set description = "A programmer" where id = 1
Но чаще именно совпадающие данные хотят обновляться по условиям совмещения, тогда SaveOne не может их удовлетворить. Итак, я сделал следующее расширение:
const table_name = "$Table_Name$"
type UpdateAttrs map[string]interface{}
func NewUpdateAttrs(tableName string) UpdateAttrs {
attrMap := make(map[string]interface{})
attrMap[table_name] = tableName
return attrMap
}
// Update selected Fields, if attrs is an object, it will ignore default value field; if attrs is map, it will ignore unchanged field.
func (dw *DBExtension) Update(attrs interface{}, query interface{}, args ...interface{}) error {
var (
tableNameAble TableNameAble
ok bool
tableName string
)
if tableNameAble, ok = query.(TableNameAble); ok {
tableName = tableNameAble.TableName()
}else if tableNameAble, ok = attrs.(TableNameAble); ok {
tableName = tableNameAble.TableName()
} else if attrMap, isUpdateAttrs := attrs.(UpdateAttrs); isUpdateAttrs {
tableName = attrMap[table_name].(string)
delete(attrMap, table_name)
}
if tableName == "" {
return errors.New("can't get table name from both attrs and query")
}
var err error
db := dw.Table(tableName).Where(query, args...).Update(attrs)
if err = db.Error; err != nil {
dw.logger.LogErrorc("mysql", err, fmt.Sprintf("failed to update %s, query is %+v, args are %+v, attrs is %+v", tableName, query, args, attrs))
}
if db.RowsAffected == 0 {
dw.logger.LogWarnc("mysql",nil, fmt.Sprintf("No rows is updated.For %s, query is %+v, args are %+v, attrs is %+v", tableName, query, args, attrs))
}
return err
}
Ниже я объясню, как использовать его один за другим в сочетании с операторами Sql.
Давайте возьмем пример выполнения следующего оператора:
update test.user set description = "A programmer" where id = 1
Теперь есть несколько реализаций следующим образом
- написание одного
udateAttrs := User{Description: "A programmer"}
condition := User{Id: 1}
if err := dw.Update(&udateAttrs, condition); err != nil{
// error handling
return err
}
- правописание два
udateAttrs := User{Description: "A programmer"}
if err := dw.Update(&udateAttrs, "id = ?", 1); err != nil{
// error handling
return err
}
- правописание три
udateAttrs := NewUpdateAttrs("test.user")
udateAttrs["description"] = "A programmer"
if err := dw.Update(&udateAttrs, "id = ?", 1); err != nil{
// error handling
return err
}
- правописание четыре
udateAttrs := NewUpdateAttrs("test.user")
udateAttrs["description"] = "A programmer"
condition := User{Id: 1}
if err := dw.Update(&udateAttrs, condition); err != nil{
// error handling
return err
}
На первый взгляд, четыре варианта написания очень похожи. Итак, почему существует так много способов написать это?
Это делается не для того, чтобы продемонстрировать несколько способов написания слова в обратном направлении, а потому, что родной метод обновления gorm игнорирует значение по умолчанию для параметров структуры.
Например, если вы хотите очистить описание, напишите так:
udateAttrs := User{Description: ""}
condition := User{Id: 1}
if err := dw.Update(&udateAttrs, condition); err != nil{
// error handling
return err
}
Описание не будет обновляться. Для этого требуется метод написания 3 или метод 4. Возьмем, к примеру, метод 4.
udateAttrs := NewUpdateAttrs("test.user")
udateAttrs["description"] = ""
condition := User{Id: 1}
if err := dw.Update(&udateAttrs, condition); err != nil{
// error handling
return err
}
Сделает так, как вы хотите:
update test.user set description = "" where id = 1
Сила написания двух (трех) заключается в том, что условия соответствия могут быть указаны более свободно, например:
udateAttrs := User{Description: "A programmer"}
if err := dw.Update(&udateAttrs, "id in (?) and age > ? and description != ?", []int{1,2}, 30, ""); err != nil{
// error handling
return err
}
Выполненный sql:
update test.user set description = "A programmer" where id in (1,2) and age > 30 and description != ''
Продолжение следует...
Кодовый адрес:Github:Ksloveyuan/gorm-ex