1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/gorm-gen

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

GORM/GEN

Обзор

Более безопасный ORM на основе GORM, ориентированный на удобство разработки.

Содержание

  • GORM/GEN;
  • Обзор;
  • Содержание;
  • Установка;
  • Быстрый старт;
  • Примеры использования API.

Установка

Инструкции по установке и использованию пакета можно найти в документации.

Быстрый старт

Для начала работы с пакетом выполните следующие шаги:

  1. Установите пакет с помощью команды go get или добавьте его в файл go.mod.
  2. Импортируйте пакет в свой код.
  3. Создайте модель данных с помощью генератора кода.
  4. Настройте подключение к базе данных.
  5. Начните использовать CRUD-операции и другие функции пакета.

Проектная директория

Структура проектной директории может выглядеть следующим образом:

project_directory
├── main.go
└── model
    └── user.gen.go

В файле main.go будет находиться основной код приложения, а в файле user.gen.go — сгенерированная модель данных.

Примеры использования API

API пакета предоставляет различные функции для работы с данными. В этом разделе приведены примеры использования некоторых из них.

Генератор

Генератор кода позволяет создавать модели данных на основе структуры базы данных. Для этого необходимо выполнить следующие действия:

  1. Создать структуру данных, соответствующую таблице в базе данных.
  2. Вызвать функцию генератора с этой структурой в качестве аргумента.

Пример создания модели данных для таблицы users:

type User struct {
    ID        int64
    Name      string
    Email     string
    Password  string
}

// Генерируем модель данных
userModel := gen.Generate("users")

После выполнения этих действий будет создан файл user.gen.go с моделью данных для таблицы users.

Создание поля

Функция CreateField позволяет создать поле в модели данных. Она принимает следующие аргументы:

  • name — имя поля;
  • type — тип поля;
  • nullable — флаг, указывающий, может ли поле быть NULL;
  • default — значение по умолчанию для поля.

Пример создания поля ID в модели данных:

userModel.CreateField("ID", int64Type, false, 0)

CRUD API

CRUD-операции позволяют выполнять основные операции с данными: создание, чтение, обновление и удаление. Пакет предоставляет следующие функции для выполнения этих операций:

  • Create — создание новой записи;
  • Query — получение одной или нескольких записей;
  • Update — обновление существующей записи;
  • Delete — удаление записи.

Эти функции принимают в качестве аргументов модель данных и параметры операции. Пример использования функции Create для создания новой записи в таблице users:

err := userModel.Create(&User{
    Name: "John Doe",
    Email: "john@doe.com",
    Password: "password",
})
if err != nil {
    fmt.Println(err)
    return
}

Ассоциации

Пакет также предоставляет функции для работы с ассоциациями между моделями данных. Эти функции позволяют связывать модели данных друг с другом и выполнять операции над связанными записями.

Отношение

Отношение между моделями данных устанавливается с помощью функции Relate. Эта функция принимает в качестве аргументов две модели данных и параметры отношения. Пример установления отношения «один ко многим» между моделями User и Post:

postModel.Relate(userModel, "author", "UserID")

Этот код устанавливает отношение «автор» между моделью User и таблицей posts в базе данных. Поле author в таблице posts будет ссылаться на поле UserID в модели User.

Операция

Операции над связанными записями выполняются с помощью функций FindAssociations, AppendAssociations и Replace. Эти функции принимают в качестве аргументов модель данных, операцию и параметры операции. Ассоциации #replace-associations

  • Delete Associations #delete-associations
  • Clear Associations #clear-associations
  • Count Associations #count-associations
  • Delete with Select #delete-with-select

Предварительная загрузка #preloading

  • Preload #preload
  • Preload All #preload-all
  • Preload with conditions #preload-with-conditions
  • Nested Preloading #nested-preloading

Обновление #update

  • Update single column #update-single-column
  • Updates multiple columns #updates-multiple-columns
  • Update selected fields #update-selected-fields

Удаление #delete

  • Delete record #delete-record
  • Delete with primary key #delete-with-primary-key
  • Batch Delete #batch-delete
  • Soft Delete #soft-delete
  • Find soft deleted records #find-soft-deleted-records
  • Delete permanently #delete-permanently

DIY method #diy-method

  • Method interface #method-interface

    • Syntax of template #syntax-of-template
      • placeholder #placeholder
      • template #template
      • If clause #if-clause
      • Where clause #where-clause
      • Set clause #set-clause
    • Method interface example #method-interface-example
  • Smart select fields

Advanced Topics #advanced-topics

  • Hints #hints

Binary

Maintainers

Contributing

License

Установка

Чтобы установить пакет Gen, вам необходимо сначала установить Go и настроить рабочее пространство Go.

  1. Сначала установите Go (требуется версия 1.14+), затем вы можете использовать следующую команду Go для установки Gen.
go get -u gorm.io/gen
  1. Импортируйте его в свой код:
import "gorm.io/gen"

Быстрый старт

Внимание: все варианты использования в этом документе генерируются в режиме WithContext. А если вы генерируете код в режиме WithoutContext, пожалуйста, удалите WithContext(ctx) перед вызовом любого метода запроса, это поможет вам сделать код более лаконичным.

# предположим, что следующий код находится в файле generate.go
$ cat generate.go
package main

import "gorm.io/gen"

// сгенерируйте код
func main() {
    // укажите выходной каталог (по умолчанию: "./query")
    // ### если вы хотите запросить без ограничения контекста, установите режим gen.WithoutContext ###
    g := gen.NewGenerator(gen.Config{
        OutPath: "../dal/query",
        /* Mode: gen.WithoutContext|gen.WithDefaultQuery*/
        //если вы хотите, чтобы свойство генерации поля с нулевым значением было типом указателя, установите FieldNullable true
        /* FieldNullable: true,*/
        //если вы хотите генерировать теги индекса из базы данных, установите FieldWithIndexTag true
        /* FieldWithIndexTag: true,*/
        //если вы хотите генерировать теги типа из базы данных, установите FieldWithTypeTag true
        /* FieldWithTypeTag: true,*/
        //если вам нужны модульные тесты для кода запроса, установите WithUnitTest true
        /* WithUnitTest: true, */
    })
  
    // повторно используйте соединение с базой данных в Project или создайте здесь соединение
    // если вы хотите использовать GenerateModel/GenerateModelAs, UseDB является обязательным, иначе произойдет паника
    // db, _ := gorm.Open(mysql.Open("root:@(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=True&loc=Local"))
    g.UseDB(db)
  
    // примените основные операции crud api к структурам или моделям таблиц, которые указаны именем таблицы с функцией
    // GenerateModel/GenerateModelAs. И генератор сгенерирует код моделей таблиц при вызове Excute.
    g.ApplyBasic(model.User{}, g.GenerateModel("company"), g.GenerateModelAs("people", "Person", gen.FieldIgnore("address")))
    
    // применить diy интерфейсы к структурам или табличным моделям
    g.ApplyInterface(func(method model.Method) {}, model.User{}, g.GenerateModel("company"))

    // выполнить действие генерации кода
    g.Execute() ### CRUD API

Здесь представлен базовый тип структуры `user` и тип структуры `DB`.

```go
// сгенерированный код
// сгенерированный код
// сгенерированный код
package query

import "gorm.io/gen"

// struct map to table `users` 
type user struct {
    gen.DO
    ID       field.Uint
    Name     field.String
    Age      field.Int
    Address  field.Field
    Birthday field.Time
}

// struct collection
type DB struct {
    db       *gorm.DB
    User     *user
}

Создание

Создание записи
// u refer to query.user
user := model.User{Name: "Modi", Age: 18, Birthday: time.Now()}

u := query.Use(db).User
err := u.WithContext(ctx).Create(&user) // передать указатель данных в Create

err // возвращает ошибку
Создание записи с выбранными полями

Создайте запись и присвойте значение указанным полям.

u := query.Use(db).User
u.WithContext(ctx).Select(u.Name, u.Age).Create(&user)
// INSERT INTO `users` (`name`,`age`) VALUES ("modi", 18)

Создайте запись и игнорируйте значения для полей, переданных для пропуска.

u := query.Use(db).User
u.WithContext(ctx).Omit(u.Name, u.Age).Create(&user)
// INSERT INTO `users` (`Address`, `Birthday`) VALUES ("2021-08-17 20:54:12.000", 18)
Пакетная вставка

Чтобы эффективно вставить большое количество записей, передайте срез методу Create. GORM сгенерирует один оператор SQL для вставки всех данных и заполнит значения первичных ключей.

var users = []model.User{{Name: "modi"}, {Name: "zhangqiang"}, {Name: "songyuan"}}
query.Use(db).User.WithContext(ctx).Create(&users)

for _, user := range users {
    user.ID // 1,2,3
}

Вы можете указать размер пакета при создании с помощью CreateInBatches, например:

var users = []User{{Name: "modi_1"}, ...., {Name: "modi_10000"}}

// размер пакета 100
query.Use(db).User.WithContext(ctx).CreateInBatches(users, 100)

Это будет работать, если вы установите CreateBatchSize в gorm.Config / gorm.Session

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
    CreateBatchSize: 1000,
})
// ИЛИ
db = db.Session(&gorm.Session{CreateBatchSize: 1000})

u := query.NewUser(db)

var users = []User{{Name: "modi_1"}, ...., {Name: "modi_5000"}}

u.WithContext(ctx).Create(&users)
// INSERT INTO users xxx (5 пакетов)

Запрос

Получение одного объекта

Сгенерированный код предоставляет методы First, Take, Last для извлечения одного объекта из базы данных, он добавляет условие LIMIT 1 при запросе к базе данных и вернёт ошибку ErrRecordNotFound, если запись не найдена.

u := query.Use(db).User

// Получить первую запись, упорядоченную по первичному ключу
user, err := u.WithContext(ctx).First()
// SELECT * FROM users ORDER BY id LIMIT 1;

// Получить одну запись без указания порядка
user, err := u.WithContext(ctx).Take()
// SELECT * FROM users LIMIT 1;

// Получить последнюю запись, упорядоченную по первичному ключу по убыванию
user, err := u.WithContext(ctx).Last()
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

// проверить ошибку ErrRecordNotFound
errors.Is(err, gorm.ErrRecordNotFound)
Извлечение объектов с первичным ключом
u := query.Use(db).User

user, err := u.WithContext(ctx).Where(u.ID.Eq(10)).First()
// SELECT * FROM users WHERE id = 10;

users, err := u.WithContext(ctx).Where(u.ID.In(1,2,3)).Find()
// SELECT * FROM users WHERE id IN (1,2,3);

Если первичный ключ является строкой (например, как uuid), запрос будет записан следующим образом:

user, err := u.WithContext(ctx).Where(u.ID.Eq("1b74413f-f3b8-409f-ac47-e8c062e3472a")).First()
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
Извлечение всех объектов
u := query.Use(db).User

// Получить все записи
users, err := u.WithContext(ctx).Find()
// SELECT * FROM users;
Условия
Строковые условия
u := query.Use(db).User

// Получить первое совпадение **Limit & Offset**

Limit указывает максимальное количество записей для извлечения.
Offset указывает количество записей, которые нужно пропустить перед началом возврата записей.

```go
u := query.Use(db).User
urers, err := u.WithContext(ctx).Limit(3).Find()
// SELECT * FROM users LIMIT 3;

// Отменить условие limit с помощью -1
users, err := u.WithContext(ctx).Limit(10).Limit(-1).Find()
// SELECT * FROM users;

users, err := u.WithContext(ctx).Offset(3).Find()
// SELECT * FROM users OFFSET 3;

users, err := u.WithContext(ctx).Limit(10).Offset(5).Find()
// SELECT * FROM users OFFSET 5 LIMIT 10;

// Отменить условие offset с помощью -1
users, err := u.WithContext(ctx).Offset(10).Offset(-1).Find()
// SELECT * FROM users;

Group By & Having

u := query.Use(db).User
type Result struct {
    Date  time.Time
    Total int
}

var result Result

err := u.WithContext(ctx).Select(u.Name, u.Age.Sum().As("total")).Where(u.Name.Like("%modi%")).Group(u.Name).Scan(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "%modi%" GROUP BY `name`

err := u.WithContext(ctx).Select(u.Name, u.Age.Sum().As("total")).Group(u.Name).Having(u.Name.Eq("group")).Scan(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"

rows, err := u.WithContext(ctx).Select(u.Birthday.As("date"), u.Age.Sum().As("total")).Group(u.Birthday).Rows()
for rows.Next() {
  ...
}

o := query.Use(db).Order

rows, err := o.WithContext(ctx).Select(o.CreateAt.Date().As("date"), o.Amount.Sum().As("total")).Group(o.CreateAt.Date()).Having(u.Amount.Sum().Gt(100)).Rows()
for rows.Next() {
  ...
}

var results []Result

o.WithContext(ctx).Select(o.CreateAt.Date().As("date"), o.WithContext(ctx).Amount.Sum().As("total")).Group(o.CreateAt.Date()).Having(u.Amount.Sum().Gt(100)).Scan(&results)

Distinct

Выбор различных значений из модели.

u := query.Use(db).User

users, err := u.WithContext(ctx).Distinct(u.Name, u.Age).Order(u.Name, u.Age.Desc()).Find()

Distinct работает с Pluck и Count.

Joins

Указание условий соединения.

u := query.Use(db).User
e := query.Use(db).Email
c := query.Use(db).CreditCard

type Result struct {
    Name  string
    Email string
}

var result Result

err := u.WithContext(ctx).Select(u.Name, e.Email).LeftJoin(e, e.UserID.EqCol(u.ID)).Scan(&result)
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id

rows, err := u.WithContext(ctx).Select(u.Name, e.Email).LeftJoin(e, e.UserID.EqCol(u.ID)).Rows()
for rows.Next() {
  ...
}

var results []Result

err := u.WithContext(ctx).Select(u.Name, e.Email).LeftJoin(e, e.UserID.EqCol(u.ID)).Scan(&results)

// Несколько соединений с параметром
users := u.WithContext(ctx).Join(e, e.UserID.EqCol(u.id), e.Email.Eq("modi@example.org")).Join(c, c.UserID.EqCol(u.ID)).Where(c.Number.Eq("411111111111")).Find()

SubQuery

Подзапрос может быть вложен в запрос, GEN может генерировать подзапрос при использовании объекта Dao в качестве параметра.

o := query.Use(db).Order
u := query.Use(db).User

orders, err := o.WithContext(ctx).Where(u.Columns(o.Amount).Gt(o.WithContext(ctx).Select(o.Amount.Avg())).Find()
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");

subQuery := u.WithContext(ctx).Select(u.Age.Avg()).Where(u.Name.Like("name%"))
users, err := u.WithContext(ctx).Select(u.Age.Avg().As("avgage")).Group(u.Name).Having(u.Columns(u.Age.Avg()).Gt(subQuery).Find()
// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")

From SubQuery

GORM позволяет использовать подзапрос в предложении FROM с методом Table, например:

u := query.Use(db).User
p := query.Use(db).Pet

users, err := gen.Table(u.WithContext(ctx).Select(u.Name, u.Age).As("u")).Where(u.Age.Eq(18)).Find()
// SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18

subQuery1 := u.WithContext(ctx).Select(u.Name)
subQuery2 := p.WithContext(ctx).Select(p.Name)
users, err := gen.Table(subQuery1.As("u"), subQuery2.As("p")).Find() **Первый фрагмент**

// Found user with `name` = `modi`, update it with Assign attributes
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).Assign(u.Age.Value(20)).FirstOrInit()
// SELECT * FROM USERS WHERE name = modi' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "modi", Age: 20}

**Второй фрагмент**

###### FirstOrCreate

Get first matched record or create a new one with given conditions

```go
u := query.Use(db).User

// User not found, create a new record with give conditions
user, err := u.WithContext(ctx).Where(u.Name.Eq("non_existing")).FirstOrCreate()
// INSERT INTO "users" (name) VALUES ("non_existing");
// user -> User{ID: 112, Name: "non_existing"}

// Found user with `name` = `modi`
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).FirstOrCreate()
// user -> User{ID: 111, Name: "modi", "Age": 18}

Create struct with more attributes if record not found, those Attrs won’t be used to build SQL query

u := query.Use(db).User

// User not found, create it with give conditions and Attrs
user, err := u.WithContext(ctx).Where(u.Name.Eq("non_existing")).Attrs(u.Age.Value(20)).FirstOrCreate()
// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
// user -> User{ID: 112, Name: "non_existing", Age: 20}

// Found user with `name` = `modi`, attributes will be ignored
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).Attrs(u.Age.Value(20)).FirstOrCreate()
// SELECT * FROM users WHERE name = 'modi' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "modi", Age: 18}

Assign attributes to the record regardless it is found or not and save them back to the database.

u := query.Use(db).User

// User not found, initialize it with give conditions and Assign attributes
user, err := u.WithContext(ctx).Where(u.Name.Eq("non_existing")).Assign(u.Age.Value(20)).FirstOrCreate()
// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
// user -> User{ID: 112, Name: "non_existing", Age: 20}

// Found user with `name` = `modi`, update it with Assign attributes
user, err := u.WithContext(ctx).Where(u.Name.Eq("modi")).Assign(u.Age.Value(20)).FirstOrCreate(&user)
// SELECT * FROM users WHERE name = 'modi' ORDER BY id LIMIT 1;
// UPDATE users SET age=20 WHERE id = 111;
// user -> User{ID: 111, Name: "modi", Age: 20}

Третий фрагмент

Association

GEN will auto-save associations as GORM do. The relationships (BelongsTo/HasOne/HasMany/Many2Many) reuse GORM's tag. This feature only support exist model for now.

Relation

There are 4 kind of relationship.

const (
    HasOne    RelationshipType = RelationshipType(schema.HasOne)    // HasOneRel has one relationship
    HasMany   RelationshipType = RelationshipType(schema.HasMany)   // HasManyRel has many relationships
    BelongsTo RelationshipType = RelationshipType(schema.BelongsTo) // BelongsToRel belongs to relationship
    Many2Many RelationshipType = RelationshipType(schema.Many2Many) // Many2ManyRel many to many relationship
)
Relate to exist model
package model

// exist model
type Customer struct {
    gorm.Model
    CreditCards []CreditCard `gorm:"foreignKey:CustomerRefer"`
}

type CreditCard struct {
    gorm.Model
    Number        string
    CustomerRefer uint
}

GEN will detect model's associations:

// specify model
g.ApplyBasic(model.Customer{}, model.CreditCard{})

// assoications will be detected and converted to code 
package query

type customer struct {
    ...
    CreditCards customerHasManyCreditCards
}

type creditCard struct{
    ...
}
Relate to table in database

The association have to be speified by gen.FieldRelate

card := g.GenerateModel("credit_cards")
customer := g.GenerateModel("customers", gen.FieldRelate(field.HasMany, "CreditCards", b, 
    &field.RelateConfig{
        // RelateSlice: true,
        GORMTag: "foreignKey:CustomerRefer",
    }),
)

g.ApplyBasic(card, custormer)

GEN will generate models with associated field:

...
``` **Клиенты**

```type Customer struct {
    ID          int64          `gorm:"column:id;type:bigint(20) unsigned;primaryKey" json:"id"`
    CreatedAt   time.Time      `gorm:"column:created_at;type:datetime(3)" json:"created_at"`
    UpdatedAt   time.Time      `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`
    DeletedAt   gorm.DeletedAt `gorm:"column:deleted_at;type:datetime(3)" json:"deleted_at"`
    CreditCards []CreditCard   `gorm:"foreignKey:CustomerRefer" json:"credit_cards"`
}

Кредитные карты

    ID            int64          `gorm:"column:id;type:bigint(20) unsigned;primaryKey" json:"id"`
    CreatedAt     time.Time      `gorm:"column:created_at;type:datetime(3)" json:"created_at"`
    UpdatedAt     time.Time      `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`
    DeletedAt     gorm.DeletedAt `gorm:"column:deleted_at;type:datetime(3)" json:"deleted_at"`
    CustomerRefer int64          `gorm:"column:customer_refer;type:bigint(20) unsigned" json:"customer_refer"`
}```

Если связанная модель уже существует, gen.FieldRelateModel может помочь вам построить ассоциации между ними.

```go
customer := g.GenerateModel("customers", gen.FieldRelateModel(field.HasMany, "CreditCards", model.CreditCard{}, 
    &field.RelateConfig{
        // RelateSlice: true,
        GORMTag: "foreignKey:CustomerRefer",
    }),
)

g.ApplyBasic(custormer)
Relate Config
type RelateConfig struct {
    // specify field's type
    RelatePointer      bool // ex: CreditCard  *CreditCard
    RelateSlice        bool // ex: CreditCards []CreditCard
    RelateSlicePointer bool // ex: CreditCards []*CreditCard

    JSONTag      string // related field's JSON tag
    GORMTag      string // related field's GORM tag
    NewTag       string // related field's new tag
    OverwriteTag string // related field's tag
}
Операция
Пропуск автоматического создания/обновления
user := model.User{
  Name:            "modi",
  BillingAddress:  Address{Address1: "Billing Address - Address 1"},
  ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
  Emails:          []Email{
    {Email: "modi@example.com"},
    {Email: "modi-2@example.com"},
  },
  Languages:       []Language{
    {Name: "ZH"},
    {Name: "EN"},
  },
}

u := query.Use(db).User

u.WithContext(ctx).Select(u.Name).Create(&user)
// INSERT INTO "users" (name) VALUES ("jinzhu", 1, 2);

u.WithContext(ctx).Omit(u.BillingAddress.Field()).Create(&user)
// Пропустить создание BillingAddress при создании пользователя

u.WithContext(ctx).Omit(u.BillingAddress.Field("Address1")).Create(&user)
// Пропустить создание Address1 при создании BillingAddress для пользователя

u.WithContext(ctx).Omit(field.AssociationFields).Create(&user)
// Пропустить все ассоциации при создании пользователя```

Метод Field объединит серьёзное имя поля с «.», например: u.BillingAddress.Field («Address1», «Street») равно BillingAddress.Address1.Street.

###### Поиск ассоциаций

Найти подходящие ассоциации

```go
u := query.Use(db).User

languages, err = u.Languages.Model(&user).Find()

Найти ассоциации с условиями

q := query.Use(db)
u := q.User

languages, err = u.Languages.Where(q.Language.Name.In([]string{"ZH","EN"})).Model(&user).Find()
Добавление ассоциаций

Добавить новые ассоциации для «многие ко многим», «имеет много», заменить текущую ассоциацию для «имеет один», «принадлежит»

u := query.Use(db).User

u.Languages.Model(&user).Append(&languageZH, &languageEN)

u.Languages.Model(&user).Append(&Language{Name: "DE"})

u.CreditCards.Model(&user).Append(&CreditCard{Number: "411111111111"})
Замена ассоциаций

Заменить текущие ассоциации новыми

u.Languages.Model(&user).Replace(&languageZH, &languageEN)
Удаление ассоциаций

Удалить связь между источником и аргументами, если она существует, удалить только ссылку, не удалять эти объекты из БД.

u := query.Use(db).User

u.Languages.Model(&user).Delete(&languageZH, &languageEN)

u.Languages.Model(&user).Delete([]*Language{&languageZH, &languageEN}...)
``` **Обновления**

u.WithContext(ctx).Where(u.Activate.Is(true)).Update(u.Name, "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;

u.WithContext(ctx).Where(u.Activate.Is(true)).Update(u.Age, u.Age.Add(1))
// или
u.WithContext(ctx).Where(u.Activate.Is(true)).UpdateSimple(u.Age.Add(1))
// UPDATE users SET age=age+1, updated_at='2013-11-17 21:34:10' WHERE active=true;

u.WithContext(ctx).Where(u.Activate.Is(true)).UpdateSimple(u.Age.Zero())
// UPDATE users SET age=0, updated_at='2013-11-17 21:34:10' WHERE active=true;

**Обновляет несколько столбцов**

`Updates` поддерживает обновление с помощью `struct` или `map[string]interface{}`, при обновлении с помощью `struct` по умолчанию будут обновляться только ненулевые поля.

```go
u := query.Use(db).User

// Обновление атрибутов с помощью map
u.WithContext(ctx).Where(u.ID.Eq(111)).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// Обновляет атрибуты с помощью struct
u.WithContext(ctx).Where(u.ID.Eq(111)).Updates(model.User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

// Обновить с выражением
u.WithContext(ctx).Where(u.ID.Eq(111)).UpdateSimple(u.Age.Add(1), u.Number.Add(1))
// UPDATE users SET age=age+1,number=number+1, updated_at='2013-11-17 21:34:10' WHERE id=111;

u.WithContext(ctx).Where(u.Activate.Is(true)).UpdateSimple(u.Age.Value(17), u.Number.Zero(), u.Birthday.Null())
// UPDATE users SET age=17, number=0, birthday=NULL, updated_at='2013-11-17 21:34:10' WHERE active=true;

Примечание: При обновлении с использованием struct, GEN будет обновлять только ненулевые поля, вы можете использовать map для обновления атрибутов или использовать Select, чтобы указать поля для обновления.

Обновление выбранных полей

Если вы хотите обновить выбранные поля или игнорировать некоторые поля при обновлении, вы можете использовать Select и Omit.

u := query.Use(db).User

// Select с Map
// ID пользователя равен 111:
u.WithContext(ctx).Select(u.Name).Where(u.ID.Eq(111)).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;

u.WithContext(ctx).Omit(u.Name).Where(u.ID.Eq(111)).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;

result, err := u.WithContext(ctx).Where(u.ID.Eq(111)).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

result.RowsAffected // количество затронутых строк
err                 // ошибка

Удалить

Удалить запись

e := query.Use(db).Email

// Email ID равен 10
e.WithContext(ctx).Where(e.ID.Eq(10)).Delete()
// DELETE from emails where id = 10;

// Удалить с дополнительными условиями
e.WithContext(ctx).Where(e.ID.Eq(10), e.Name.Eq("modi")).Delete()
// DELETE from emails where id = 10 AND name = "modi";

result, err := e.WithContext(ctx).Where(e.ID.Eq(10), e.Name.Eq("modi")).Delete()

result.RowsAffected // затронутое количество строк
err                 // ошибка

Удалить с первичным ключом

GEN позволяет удалять объекты, используя первичный ключ (первичные ключи) с встроенным условием, это работает с числами.

u.WithContext(ctx).Where(u.ID.In(1,2,3)).Delete()
// DELETE FROM users WHERE id IN (1,2,3);

Пакетное удаление

Указанное значение не имеет первичного значения, GEN выполнит пакетное удаление, оно удалит все совпадающие записи.

e := query.Use(db).Email

e.WithContext(ctx).Where(e.Name.Like("%modi%")).Delete()
// DELETE from emails where email LIKE "%modi%";

Мягкое удаление

Если ваша модель включает поле gorm.DeletedAt (которое включено в gorm.Model), она автоматически получит возможность мягкого удаления!

При вызове Delete запись НЕ будет удалена из базы данных, но GORM установит значение DeletedAt на текущее время, и данные больше не будут найдены обычными методами запроса.

// Пакетное удаление
u.WithContext(ctx).Where(u.Age.Eq(20)).Delete() ```
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// Soft deleted records will be ignored when querying
users, err := u.WithContext(ctx).Where(u.Age.Eq(20)).Find()
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

If you don’t want to include gorm.Model, you can enable the soft delete feature like:

type User struct {
    ID      int
    Deleted gorm.DeletedAt
    Name    string
}
Find soft deleted records

You can find soft deleted records with Unscoped

users, err := db.WithContext(ctx).Unscoped().Where(u.Age.Eq(20)).Find()
// SELECT * FROM users WHERE age = 20;
Delete permanently

You can delete matched records permanently with Unscoped

o.WithContext(ctx).Unscoped().Where(o.ID.Eq(10)).Delete()
// DELETE FROM orders WHERE id=10;

DIY method

Method interface

Method interface is an abstraction of query methods, all functions it contains are query methods and above comments describe the specific query conditions or logic. SQL supports simple where query or execute raw SQL. Simple query conditions wrapped by where(), and raw SQL wrapped by sql() (not required)

Method interface supports descriptive comment that describes how the method works. It starts with method name and followed descriptive message (not required). It is distinguished from query comment by blank line (with descriptive message) or space (without descriptive message).

type Method interface {
    // where("name=@name and age=@age")
    SimpleFindByNameAndAge(name string, age int) (gen.T, error)

    // FindUserToMap query by id and return id->instance
    // 
    // sql(select * from users where id=@id)
    FindUserToMap(id int) (gen.M, error)
    
    // InsertValue insert into users (name,age) values (@name,@age)
    InsertValue(age int, name string) error
}

Return values must contain less than 1 gen.T/gen.M/gen.RowsAffected and less than 1 error. You can also use bulitin type (like string/ int) as the return parameter, gen.T represents return a single result struct's pointer, []gen.T represents return an array of result structs' pointer,

Syntax of template
placeholder
  • gen.T представляет указанную struct или table
  • gen.M представляет map[string]interface
  • gen.RowsAffected представляет SQL выполненное rowsAffected (тип: int64)
  • @@table представляет имя таблицы (если параметр метода не содержит переменную table, GEN сгенерирует table из модели структуры)
  • @@<columnName> представляет имя столбца или имя таблицы
  • @<name> представляет нормальную переменную запроса
template

Логические операции должны быть заключены в {{}}, и в конце должен использоваться {{end}}, все шаблоны поддерживают вложение

  • if / else if / else условие принимает параметр bool или выражение операции, которое соответствует синтаксису Golang.
  • where Предложение where будет вставлено только в том случае, если дочерние элементы что-то возвращают. Ключевое слово and или or перед предложением будет удалено. И and будет добавлено автоматически, когда между условиями запроса нет ключевого слова соединения.
  • Set Предложение set будет вставлено только в том случае, если дочерние элементы что-то возвращают. , перед массивом столбцов будет удален. И , будет добавлен автоматически, когда между столбцами запроса нет ключевого слова соединения.
  • ... Скоро появится
If clause
{{if cond1}}
    // do something here
{{else if cond2}}
    // do something here
{{else}}
    // do something here
{{end}}

Пример использования в необработанном SQL:

// select * from users where {{if name !=""}} name=@name{{end}}
methond(name string) (gen.T,error) 

Пример использования в шаблоне необработанного SQL:

select * from @@table where
{{if age>60}}
    status="older"
{{else if age>30}}
    status="middle-ager"
{{else if age>18}}
    status="younger"
{{else}}
    {{if sex=="male"}}
        status="boys"
    {{else}}
        status="girls"
    {{end}}
{{end}}
Where clause
{{where}}
    // do something here
{{end}}

Пример использования в необработанном SQL

// select * from
``` **Где**

id=@id


**Метод (id int) ошибка**

```sql
select * from @@table 

Пример использования в шаблоне SQL

{{where}}
    {{if cond}}id=@id {{end}}
    {{if name != ""}}@@key=@value{{end}}
{{end}}

Предложение set

{{set}}
    // sepecify update expression here
{{end}}

Пример использования в SQL

// update users {{set}}name=@name{{end}} methond() error

Пример использования шаблона SQL

update @@table 
{{set}}
    {{if name!=""}} name=@name {{end}}
    {{if age>0}} age=@age {{end}}
{{end}}
where id=@id

Интерфейс метода, пример

type Method interface {
    // Where("name=@name and age=@age")
    SimpleFindByNameAndAge(name string, age int) (gen.T, error)
    
    // select * from users where id=@id
    FindUserToMap(id int) (gen.M, error)
    
    // sql(insert into @@table (name,age) values (@name,@age) )
    InsertValue(age int, name string) error
    
    // select name from @@table where id=@id
    FindNameByID(id int) string
    
    // select * from @@table
    //  {{where}}
    //      id>0
    //      {{if cond}}id=@id {{end}}
    //      {{if key!="" && value != ""}} or @@key=@value{{end}}
    //  {{end}}
    FindByIDOrCustom(cond bool, id int, key, value string) ([]gen.T, error)
    
    // update @@table
    //  {{set}}
    //      update_time=now()
    //      {{if name != ""}}
    //          name=@name
    //      {{end}}
    //  {{end}}
    //  {{where}}
    //      id=@id
    //  {{end}}
    UpdateName(name string, id int) (gen.RowsAffected,error)
}

Умный выбор полей

GEN позволяет выбирать конкретные поля с помощью Select. Если вы часто используете это в своём приложении, возможно, вы захотите определить меньшую структуру для использования API, которая может автоматически выбирать конкретные поля, например:

type User struct {
  ID     uint
  Name   string
  Age    int
  Gender string
  // hundreds of fields
}

type APIUser struct {
  ID   uint
  Name string
}

type Method interface{
    // select * from user
    FindSome() ([]APIUser, error)
}

apiusers, err := u.WithContext(ctx).Limit(10).FindSome()
// SELECT `id`, `name` FROM `users` LIMIT 10

Расширенные темы

Подсказки

Подсказки оптимизатора позволяют контролировать, какой план выполнения запроса выбирает оптимизатор запросов. GORM поддерживает его с помощью gorm.io/hints, например:

import "gorm.io/hints"

u := query.Use(db).User

users, err := u.WithContext(ctx).Clauses(hints.New("MAX_EXECUTION_TIME(10000)")).Find()
// SELECT * /*+ MAX_EXECUTION_TIME(10000) */ FROM `users`

Индексы позволяют передавать подсказки индекса в базу данных на случай, если планировщик запросов запутается.

import "gorm.io/hints"

u := query.Use(db).User

users, err := u.WithContext(ctx).Clauses(hints.UseIndex("idx_user_name")).Find()
// SELECT * FROM `users` USE INDEX (`idx_user_name`)

users, err := u.WithContext(ctx).Clauses(hints.ForceIndex("idx_user_name", "idx_user_id").ForJoin()).Find()
// SELECT * FROM `users` FORCE INDEX FOR JOIN (`idx_user_name`,`idx_user_id`)"

Двоичный файл

Установите GEN как двоичный инструмент:

go install gorm.io/gen/tools/gentool@latest

Использование:

$ gentool -h
Usage of gentool:
  -db string
      input mysql or postgres or sqlite or sqlserver. consult[https://gorm.io/docs/connecting_to_the_database.html] (default "mysql")
  -dsn string
      consult[https://gorm.io/docs/connecting_to_the_database.html]
  -fieldNullable
      generate with pointer when field is nullable
  -fieldWithIndexTag
      generate field with gorm index tag
  -fieldWithTypeTag
      generate field with gorm column type tag
  -modelPkgName string
      generated model code's package name
  -outFile string
      query code file name, default: gen.go
  -outPath string
      specify a directory for output (default "./dao/query")
  -tables string
      enter the required data table or leave it blank
  -withUnitTest
      generate unit test for query code

Пример:

gentool -dsn "user:pwd@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" -tables "orders,doctor"

Сопровождающие

@riverchu

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Описание недоступно Развернуть Свернуть
MIT
Отмена

Обновления

Пока нет обновлений

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/gorm-gen.git
git@api.gitlife.ru:oschina-mirror/gorm-gen.git
oschina-mirror
gorm-gen
gorm-gen
master