Ненужные запросы, выполняемые gorm для вставки данных в таблицы базы данных - PullRequest
2 голосов
/ 22 марта 2020

Я пробую свои силы на golang и gorm для создания новой функции в моем текущем проекте.

У меня следующая структура базы данных в MySQL.

enter image description here

UserMaster Таблица находится на вершине иерархии, а ее столбец первичного ключа используется многими другими таблицами, и они сами используйте его в качестве основных столбцов. UserLogin таблица является одной из таких таблиц. Это не идеальный дизайн базы данных, но это структура базы данных, над которой я буду работать, и нет никаких шансов, что структура может быть изменена.

Ниже приведены классы моделей, которые я создал для UserMaster и UserLogin в go.

type UserMaster struct {
    UserId      string    `gorm:"column:UserId;PRIMARY_KEY";size:20`
    CreatedDate time.Time `gorm:"column:CreatedDate"`
    IsActive    bool      `gorm:"column:IsActive"`
    UserLogin   UserLogin `gorm:"foreignkey:Id"`
}

type UserLogin struct {
    Id            string    `gorm:"column:Id;PRIMARY_KEY";size:20"`
    UserName      string    `gorm:"column:UserName;unique"`
    EmailId       string    `gorm:"column:EmailId;unique"`
    PasswordHash  string    `gorm:"column:PasswordHash"`
    PasswordSalt  string    `gorm:"column:PasswordSalt"`
    LastLoginDate time.Time `gorm:"column:LastLoginDate"`
}

func (UserMaster) TableName() string {
    return "usermaster"
}

func (UserLogin) TableName() string {
    return "userlogin"
}

Я хочу sh создать строки в таблицах UserMaster и UserLogin с помощью gorm, просто сохранив полностью заполненный экземпляр UserMaster.

Ниже приведен код Я написал для этого.

func main() {
    db, err := gorm.Open("mysql", "user:password@tcp(localhost:3306)/ormsample")

    if err != nil {
        panic("Failed to connect the database.")
    }

    defer db.Close()

    createNewUser(db, "someuser009@yopmail.com", "j;dfasdasdf")
}

func createNewUser(db *gorm.DB, emailId string, password string) UserMaster {
    userId := createUserId()

    newUser := &UserMaster{}
    newUser.UserId = userId
    newUser.CreatedDate = time.Now()
    newUser.UserLogin = UserLogin{}
    newUser.UserLogin.UserName = emailId
    newUser.UserLogin.EmailId = emailId
    newUser.UserLogin.PasswordHash = password
    newUser.UserLogin.PasswordSalt = "SomeSalt"

    db.Debug().Model(&newUser).Create(&newUser)

    return *newUser
}

func createUserId() string {
    uuidWithHyphen := uuid.New()
    uuid := strings.Replace(uuidWithHyphen.String(), "-", "", -1)

    return strings.ToUpper(uuid[:12])
}

Все это работает, как и ожидалось, это означает, что он вставляет записи как в UserMaster, так и в таблицу UserLogin.

Проблема заключается в том, как Горм выполняет это работа с базой данных. Далее следуют запросы SQL, регистрируемые терминалом (поскольку я использую функцию Debug() для объекта gorm db).

INSERT INTO `usermaster` (`UserId`,`CreatedDate`,`IsActive`) 
    VALUES ('1601F7E41E29','2020-03-22 22:24:08',false) 

UPDATE `userlogin` SET `UserName` = 'someuser009@yopmail.com', 
  `EmailId` = 'someuser009@yopmail.com', `PasswordHash` = 'j;dfasdasdf', 
  `PasswordSalt` = 'SomeSalt', `LastLoginDate` = '0000-00-00 00:00:00'  
  WHERE `userlogin`.`Id` = '1601F7E41E29'

SELECT * FROM `userlogin`  WHERE `userlogin`.`Id` = '1601F7E41E29' 
 ORDER BY `userlogin`.`Id` ASC LIMIT 1

INSERT INTO `userlogin` (`Id`,`UserName`,`EmailId`,`PasswordHash`,`PasswordSalt`,`LastLoginDate`)
VALUES ('1601F7E41E29','someuser009@yopmail.com','someuser009@yopmail.com',
 'j;dfasdasdf','SomeSalt','0000-00-00 00:00:00')

Как вы заметили выше, gorm выполняет 2 запроса INSERT, 1 UPDATE и 1 SELECT. вставить в две таблицы, в то время как должно быть выполнено только 2 INSERT-запроса.

Я много искал об этом на различных форумах golang и gorm, но не нашел ничего, что могло бы объяснить это поведение.

Любая помощь / руководство / направление для решения этой проблемы будет принята с благодарностью.

1 Ответ

1 голос
/ 30 марта 2020

Gorm вызывает эту функцию перед созданием

func beforeCreateCallback(scope *Scope) {
    if !scope.HasError() {
        scope.CallMethod("BeforeSave")
    }
    if !scope.HasError() {
        scope.CallMethod("BeforeCreate")
    }
}

Здесь, если первичный ключ уже установлен, тогда gorm пытается обновить существующие данные, используя заданный первичный ключ. Вот почему дополнительный запрос выполнен. Чтобы решить эту проблему, используйте UserId как foreignkey для UserLogin, который является первичным ключом модели UserLogin, чтобы gorm попытался сохранить потомок потом потом родительский. Не устанавливайте первичный ключ, используйте крюк BeforeCreate для установки

func (user *UserLogin) BeforeCreate(scope *gorm.Scope) error {
  scope.SetColumn("Id", ), createUserId())
  return nil
}

Модели типа:

type UserMaster struct {
    UserId      uint      `gorm:"column:UserId;PRIMARY_KEY;size:20"`
    CreatedDate time.Time `gorm:"column:CreatedDate"`
    IsActive    bool      `gorm:"column:IsActive"`
    UserLogin   UserLogin `gorm:"foreignkey:UserId"`
}

type UserLogin struct {
    Id            uint      `gorm:"column:Id;PRIMARY_KEY;size:20"`
    UserName      string    `gorm:"column:UserName;"`
    EmailId       string    `gorm:"column:EmailId;"`
    PasswordHash  string    `gorm:"column:PasswordHash"`
    PasswordSalt  string    `gorm:"column:PasswordSalt"`
    LastLoginDate time.Time `gorm:"column:LastLoginDate"`
}

Использование Model(&user) и Create(&user) не требуется. Model() в основном используется перед обновлением существующих данных, что может вызвать дополнительный запрос. Только для создания вы можете попробовать

db.Debug().Create(&newUser)

Gorm с автоматическим выбором модели UserMaster из newUser

...