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

OSCHINA-MIRROR/bigpigeon-toyorm

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

Toyorm

Это мощная библиотека SQL ORM для Golang, обладающая некоторыми забавными функциями.

Build Status Codecov Go Report Card GoDoc Присоединяйтесь к чату
Build Status codecov Go Report Card GoDoc Join the chat

  • Простой пример
  • Пример веб-сайта
  • Подключение к базе данных
    • Импорт драйвера базы данных
      // если база данных mysql
      _ "github.com/go-sql-driver/mysql"
      // если база данных sqlite3
      _ "github.com/mattn/go-sqlite3"
      // когда база данных postgres
      _ "github.com/lib/pq"
    • Создание игрушки
      // если базой данных является mysql, убедитесь, что в вашем mysql есть схема toyorm_example
      toy, err = toyorm.Open("mysql", "root:@tcp(localhost:3306)/toyorm_example?charset=utf8&parseTime=True")
      // если база данных — sqlite3
      toy,err = toyorm.Open("sqlite3", "toyorm_test.db")
      // при использовании базы данных postgres
      toy, err = toyorm.Open("postgres", "user=postgres dbname=toyorm sslmode=disable")
  • Определение модели
    • Пример
    type Extra map[string]interface{}
    
    func (e Extra) Scan(value interface{}) error {
        switch v := value.(type) {
        case string:
             return json.Unmarshal([]byte(v), e)
        case []byte:
             return json.Unmarshal(v, e)
        default:
             return errors.New("not support type")
        }
    }
    
    func (e Extra) Value() (driver.Value, error) {
         return json.Marshal(e)
    }
    
    type UserDetail struct {
        ID       int  `toyorm:"primary key;auto_increment"`
        UserID
    

Поддержка баз данных

sqlite3 mysql postgresql

Версия Go

version go-1.9


Простой пример

здесь


Пример веб-сайта

здесь


Подключение к базе данных

Импортируйте драйвер базы данных.

// если база данных mysql
_ "github.com/go-sql-driver/mysql"
// если база данных sqlite3
_ "github.com/mattn/go-sqlite3"
// когда база данных postgres
_ "github.com/lib/pq"

Создайте игрушку.

// если базой данных является mysql, убедитесь, что в вашем mysql есть схема toyorm_example
toy, err = toyorm.Open("mysql", "root:@tcp(localhost:3306)/toyorm_example?charset=utf8&parseTime=True")
// если база данных — sqlite3
toy,err = toyorm.Open("sqlite3", "toyorm_test.db")
// при использовании базы данных postgres
toy, err = toyorm.Open("postgres", "user=postgres dbname=toyorm sslmode=disable")

Определение модели

Пример

type Extra map[string]interface{}

func (e Extra) Scan(value interface{}) error {
     switch v := value.(type) {
     case string:
          return json.Unmarshal([]byte(v), e)
     case []byte:
          return json.Unmarshal(v, e)
     default:
          return errors.New("not support type")
     }
}

func (e Extra) Value() (driver.Value, error) {
     return json.Marshal(e)
}

type UserDetail struct {
     ID       int  `toyorm:"primary key;auto_increment"`
     UserID

*Примечание: перевод выполнен с помощью системы машинного перевода.* **Тип translate**

Если тип SQL не совпадает, toyorm проигнорирует его в операции Sql.

Вы можете использовать тег поля **\<type:sql_type\>** для указания их типа SQL.

Следующие типы будут автоматически переведены в типы SQL:

| Тип Go | Тип SQL |
|--------|---------|
bool    | BOOLEAN |
int8, int16, int32, uint8, uint16, uint32 | INTEGER |
int64, uint64, int, uint | BIGINT |
float32, float64 | FLOAT |
string  | VARCHAR(255) |
time.Time | TIMESTAMP |
[]byte    | VARCHAR(255) |
sql.NullBool | BOOLEAN |
sql.NullInt64 | BIGINT |
sql.NullFloat64 | FLOAT |
sql.NullString | VARCHAR(255) |
sql.RawBytes | VARCHAR(255) |

**Специальные поля**

1. Специальные поля имеют некоторые процессы в обработчиках, не пытайтесь изменить их тип или установить значение.

| Имя поля | Тип | Описание |
|----------|-------|------------|
CreatedAt | time.Time  | генерируется при создании элемента |
UpdatedAt | time.Time  | генерируется при обновлении/создании элемента |
DeletedAt | *time.Time* | режим удаления  мягкий |
Cas       | int        | (не поддерживается для sqlite3) операция сохранения завершится ошибкой, если её значение будет изменено третьей стороной, например, в postgres: cas = 1 insert xxx conflict (id) update cas = 2 where cas = 1 |

**Теги полей**

1. Формат тега может быть **\<key:value\>** или **\<key\>**.

2. Следующие теги являются специальными:

| Ключ | Значение | Описание |
|--------------|------------------------|-----------|
index         | void или string         | используется для оптимизации, когда условие поиска имеет это поле, если вы хотите сделать комбинированный, просто установите одинаковое имя индекса с полями |
unique index  | void или string         | имеет ограничение уникального индекса, остальное аналогично индексу |
primary key   | void                   | допускается несколько первичных ключей, но некоторые операции не поддерживаются |
\-            | void                    | игнорирует это поле в SQL |
type          | string                  | тип SQL |
column        | string                  | имя столбца SQL |
auto_increment| void                    | рекомендуется, если ваш первичный ключ таблицы имеет атрибут auto_increment, необходимо добавить его |
autoincrement | void                    | то же, что и auto_increment |
foreign key   | void                   | чтобы добавить функцию внешнего ключа при создании таблицы |
alias         | string                 | изменить имя поля с помощью toyorm |
join          | string                 | выбрать связанное поле при вызове brick.Join |
belong to     | string                 | выбрать связанное поле при вызове brick.Preload с контейнером BelongTo |
one to one    | string                 | выбрать связанное поле при вызове brick.Preload с контейнером OneToOne |
one to many   | string                 | выбрать связанное поле при вызозе brick.Preload с контейнером OneToMany |
default       | srring                 | значение по умолчанию в базе данных |

Другие пользовательские теги будут добавлены в конец поля CREATE TABLE.

#### Связывание моделей

1. Тип модели должен быть структурой или точкой со структурой.

2. Модель  это информация, которую toyorm знает о таблице.
```golang
brick := toy.Model(&User{})
// или
brick := toy.Model(User{})

Операция SQL


Создание таблицы

var err error
_, err = toy.Model(&User{}).Debug().CreateTable()
// CREATE TABLE user (id BIGINT AUTO_INCREMENT,created_at TIMESTAMP NULL,updated_at TIMESTAMP NULL,deleted_at TIMESTAMP NULL,name VARCHAR(255),age BIGINT ,sex VARCHAR(255) , PRIMARY KEY(id))
// CREATE INDEX idx_user_deletedat ON user(deleted_at)
// CREATE UNIQUE INDEX udx_user_name ON user(name)
_, err =toy.Model(&UserDetail{}).Debug().CreateTable()
// CREATE TABLE user_detail (id BIGINT AUTO_INCREMENT,user_id BIGINT,main_page Text,extra VARCHAR(1024), PRIMARY KEY(id))
// CREATE INDEX idx_user_detail_userid ON user_detail(user_id)
_, err =toy.Model(&Blog{}).Debug().CreateTable()
``` CREATE TABLE blog (
  id BIGINT AUTO_INCREMENT,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  deleted_at TIMESTAMP NULL,
  user_id BIGINT,
  title VARCHAR(255),
  content VARCHAR(255) ,
  PRIMARY KEY(id)
);

// CREATE INDEX idx_blog_deletedat ON blog(deleted_at)
// CREATE INDEX idx_blog_userid ON blog(user_id)
// CREATE INDEX idx_blog_title ON blog(title)

#### drop table
```golang
var err error
_, err =toy.Model(&User{}).Debug().DropTable()
// DROP TABLE user
_, err =toy.Model(&UserDetail{}).Debug().DropTable()
// DROP TABLE user_detail
_, err =toy.Model(&Blog{}).Debug().DropTable()
// DROP TABLE blog

insert/save data

// insert with autoincrement will set id to source data

user := &User{
    Name: "bigpigeon",
    Age:  18,
    Sex:  "male",
}
_, err = toy.Model(&User{}).Debug().Insert(&user)
// INSERT INTO user(created_at,updated_at,name,age,sex) VALUES(?,?,?,?,?) , args:[]interface {}{time.Time{wall:0xbe8df5112e7f07c8, ext:210013499, loc:(*time.Location)(0x141af80)}, time.Time{wall:0xbe8df5112e7f1768, ext:210017044, loc:(*time.Location)(0x141af80)}, "bigpigeon", 18, "male"}
// print user format with json
/* {
  "ID": 1,
  "CreatedAt": "2018-01-11T20:47:00.780077+08:00",
  "UpdatedAt": "2018-01-11T20:47:00.780081+08:00",
  "DeletedAt": null,
  "Name": "bigpigeon",
  "Age": 18,
  "Sex": "male",
  "Detail": null,
  "Friends": null,
  "Blog": null
}*/

// save data use "REPLACE INTO" when primary key exist

users := []User{
    {
        ModelDefault: toyorm.ModelDefault{ID: 1},
        Name:         "bigpigeon",
        Age:          18,
        Sex:          "male",
    },
    {
        Name: "fatpigeon",
        Age:  27,
        Sex:  "male",
    },
}
_, err = toy.Model(&User{}).Debug().Save(&user)
// SELECT id,created_at FROM user WHERE id IN (?), args:[]interface {}{0x1}
// REPLACE INTO user(id,created_at,updated_at,name,age,sex) VALUES(?,?,?,?,?,?) , args:[]interface {}{0x1, time.Time{wall:0x0, ext:63651278036, loc:(*time.Location)(nil)}, time.Time{wall:0xbe8dfb5511465918, ext:302600558, loc:(*time.Location)(0x141af80)}, "bigpigeon", 18, "male"}
// INSERT INTO user(created_at,updated_at,name,age,sex) VALUES(?,?,?,?,?) , args:[]interface {}{time.Time{wall:0xbe8dfb551131b7d8, ext:301251230, loc:(*time.Location)(0x141af80)}, time.Time{wall:0xbe8dfb5511465918, ext:302600558, loc:(*time.Location)(0x141af80)}, "fatpigeon", 27, "male"}

update

toy.Model(&User{}).Debug().Update(&User{
    Age: 4,
})
// UPDATE user SET updated_at=?,age=? WHERE deleted_at IS NULL, args:[]interface {}{time.Time{wall:0xbe8df4eb81b6c050, ext:233425327, loc:(*time.Location)(0x141af80)}, 4}

find

find one

var user User
_, err = toy.Model(&User{}).Debug().Find(&user}
// SELECT id,created_at,updated_at,deleted_at,name,age,sex FROM user WHERE deleted_at IS NULL LIMIT 1, args:[]interface {}(nil)
// print user format with json
/* {
  "ID": 1,
  "CreatedAt": "2018-01-11T12:47:01Z",
  "UpdatedAt": "2018-01-11T12:47:01Z",
  "DeletedAt": null,
  "Name": "bigpigeon",
  "Age": 4,
  "Sex": "male",
  "Detail": null,
  "Friends": null,
  "Blog": null
}*/

find multiple

var users []User
_, err = brick.Debug().Find(&users)
fmt.Printf("find users %s\n", JsonEncode(&users))

// SELECT id,created_at,updated_at,deleted_at,name,age,sex FROM user WHERE deleted_at IS NULL, args:[]interface {}(nil)

delete

delete with primary key

_, err = brick.Debug().Delete(&user)
// UPDATE user SET deleted_at=? WHERE id IN (?), args:[]interface {}{(*time.Time)(0xc4200f0520), 0x1}

delete with condition

_, err = brick.Debug().Where(toyorm.ExprEqual, Offsetof(User{}.Name), "bigpigeon").DeleteWithConditions()
// UPDATE user SET deleted_at=? WHERE name = ?, args:[]interface {}{(*time.Time)(0xc4200dbfa0), "bigpigeon"}

ToyBrick


use toy.Model will create a ToyBrick, you need use it to build grammar and operate the database brick.Where(, , [value])

whereGroup add multiple condition with same expr

*brick.WhereGroup(<expr>, <group>)*

Conditions will copy conditions and clean old conditions

*brick.Conditions(<toyorm.Search>)*

Or & and condition will use or/and to link new condition when current condition is not nil

*brick.Or().Condition(<expr>, <Key>, [value])*
*brick.Or().ConditionGroup(<expr>, <group>)*
*brick.And().Condition(<expr>, <Key>, [value])*

or & and conditions will use or/and to link new conditions

*brick.Or().Conditions(<toyorm.Search>)*
*brick.And().Conditions(<toyorm.Search>)*
SearchExpr
SearchExpr to sql example
ExprAnd AND brick.WhereGroup(ExprAnd, Product{Name:"food one", Count: 4}) // WHERE name = "food one" AND Count = 4
ExprOr OR brick.WhereGroup(ExprOr, Product{Name:"food one", Count: 4}) // WHERE name = "food one" OR Count = "4"
ExprEqual = brick.Where(ExprEqual, OffsetOf(Product{}.Name), "food one") // WHERE name = "find one"
ExprNotEqual <> brick.Where(ExprNotEqual, OffsetOf(Product{}.Name), "food one") // WHERE name <> "find one"
ExprGreater > brick.Where(ExprGreater, OffsetOf(Product{}.Count), 3) // WHERE count > 3
ExprGreaterEqual >= brick.Where(ExprGreaterEqual, OffsetOf(Product{}.Count), 3) // WHERE count >= 3
ExprLess < brick.Where(ExprLess, OffsetOf(Product{}.Count), 3) // WHERE count < 3
ExprLessEqual <= brick.Where(ExprLessEqual, OffsetOf(Product{}.Count), 3) // WHERE count <= 3
ExprBetween Between brick.Where(ExprBetween, OffsetOf(Product{}.Count), [2]int{2,3}) // WHERE count BETWEEN 2 AND 3
ExprNotBetween NOT Between brick.Where(ExprNotBetween, OffsetOf(Product{}.Count), [2]int{2,3}) // WHERE count NOT BETWEEN 2 AND 3
ExprIn IN brick.Where(ExprIn, OffsetOf(Product{}.Count), []int{1, 2, 3}) // WHERE count IN (1,2,3)
ExprNotIn NOT IN brick.Where(ExprNotIn, OffsetOf(Product{}.Count), []int{1, 2, 3}) // WHERE count NOT IN (1,2,3)
ExprLike LIKE brick.Where(ExprLike, OffsetOf(Product{}.Name), "one") // WHERE name LIKE "one"
ExprNotLike NOT LIKE brick.Where(ExprNotLike, OffsetOf(Product{}.Name), "one") // WHERE name NOT LIKE "one"
ExprNull IS NULL brick.Where(ExprNull, OffsetOf(Product{}.DeletedAt)) // WHERE DeletedAt IS NULL
ExprNotNull IS NOT NULL brick.Where(ExprNotNull, OffsetOf(Product{}.DeletedAt)) // WHERE DeletedAt IS NOT NULL
example

Single condition

brick = brick.Where(toyorm.ExprEqual, Offsetof(Product{}.Tag), "food")
// ИЛИ
brick = brick.Where("=", Offsetof(Product{}.Tag), "food")
// WHERE tag = "food"

Combination condition

brick = brick.Where(toyorm.ExprEqual, Offsetof(Product{}.Count), 2).And().
    Condition(toyorm.ExprGreater, Offsetof(Product{}.Price), 3).Or().
    Condition(toyorm.ExprEqual, Offsetof(Product{}.Count), 4)
// ИЛИ
brick = brick.Where("=", Offsetof(Product{}.Count), 2).And().
    Condition(">", Offsetof(Product{}.Price), 3).Or().
    Condition("=", Offsetof(Product{}.Count), 4)
// WHERE count = 2 and price > 3 or count = 4

Priority condition

brick.Where(toyorm.ExprGreater, Offsetof(Product{}.Price), 3).And().Conditions(
    brick.Where(toyorm.ExprEqual, Offsetof(Product{}.Count), 2).Or().
    Condition(toyorm.ExprEqual, Offsetof(Product{}.Count), 1).Search
)
// ИЛИ
brick.Where(">", Offsetof(Product{}.Price), 3).And().Conditions(
    brick.Where("=", Offsetof(Product{}.Count), 2).Or().
    Condition("=", Offsetof(Product{}.Count), 1).Search
)
// WHERE price > 3 and (count = 2 or count = 1)
``` **Пользовательский поиск**

```golang
var data Product
// если драйвер mysql, то использовать "USE INDEX" вместо "INDEXED BY"
result, err := brick.Template("SELECT $Columns FROM $ModelName INDEXED BY idx_product_name $Conditions").
    Where("=", Offsetof(Product{}.Name), "bag").Find(&data)
// SELECT id,created_at,updated_at,deleted_at,name,price,count,tag FROM product INDEXED BY idx_product_name  WHERE deleted_at IS NULL AND name = ? LIMIT 1  args:["bag"]

Пользовательское обновление

set count = count + 2

result, err := brick.Template(fmt.Sprintf("UPDATE $ModelName SET $Values,$FN-Count = $0x%x + ? $Conditions", Offsetof(Product{}.Count)), 2).
    Where("=", Offsetof(Product{}.Name), "bag").Update(&Product{Price: 200})
// UPDATE product SET updated_at = ?,price = ?,count = count + ?  WHERE deleted_at IS NULL AND name = ?  args:["2018-04-01T17:50:35.205377+08:00",200,2,"bag"]

Заполнитель

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

Действие Заполнитель $ModelName $Columns $Values $Conditions
Поиск product id,data,... - WHERE ... ORDER BY ... GROUP BY ... LIMIT ... OFFSET ...
Вставка product id,data,... ?,?,... WHERE ... ORDER BY ... GROUP BY ... LIMIT ... OFFSET ...
Сохранение product id,data,... ?,?,... WHERE ... ORDER BY ... GROUP BY ... LIMIT ... OFFSET ...
Обновление product id,data,... id = ?,data = ?,... WHERE ... ORDER BY ... GROUP BY ... LIMIT ... OFFSET ...

Поточно-безопасный

Поточно-безопасен, если вы соблюдаете следующее соглашение:

  1. Убедитесь, что объект ToyBrick доступен только для чтения. Если вы хотите изменить его, создайте новый.

  2. Не используйте append для изменения данных среза ToyBrick, используйте make и copy для клонирования нового среза.

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

Предварительная загрузка требует наличия поля отношения и поля контейнера. Поле отношения используется для связи основной записи и дополнительной записи. Поле контейнера используется для хранения дополнительной записи.

Пример предварительной загрузки: здесь

Один к одному

Поле отношения находится в дополнительной модели. Имя поля отношения должно быть именем типа основной модели + имя первичного ключа основной модели.

type User struct {
    toyorm.ModelDefault
    // Поле контейнера
    Detail *UserDetail
}

type UserDetail struct {
    ID int `toyorm:"primary key;auto_increment"`
    // Поле отношения
    UserID uint `toyorm:"index"`
    MainPage string `toyorm:"type:Text"`
}

// Загрузка предварительной загрузки
brick = toy.Model(&User{}).Debug().Preload(OffsetOf(User.Detail)).Enter()

Принадлежит

Поле отношения находится в основной модели. Имя поля отношения должно совпадать с именем поля контейнера дополнительной модели + первичный ключ дополнительной модели.

type User struct {
    toyorm.ModelDefault
    // Поле контейнера
    Detail *UserDetail
    // Поле отношения
    DetailID int `toyorm:"index"`
}

type UserDetail struct {
    ID int `toyorm:"primary key;auto_increment"`
    MainPage string `toyorm:"type:Text"`
}

Один ко многим

Поле отношения находится в дополнительной модели. Имя поля отношения должно соответствовать типу основной модели + первичному ключу основной модели.

type User struct {
    toyorm.ModelDefault
    // Поле контейнера
    Blog []Blog
}

type Blog struct {
    toyorm.ModelDefault
    // Поле отношения
    UserID uint `toyorm:"index"`
    Title string `toyorm:"index"`
    Content string
}

Многие ко многим

Для многих ко многим не нужно указывать связь, она находится в средней модели. ``` stick"). Join(Offsetof(tab.Detail)). Join(Offsetof(detailTab.ColorJoin)).Or().Condition("=", Offsetof(colorTab.Name), "black"). Swap().Swap() var scanData []Product result, err = brick.Find(&scanData) // SELECT m.id,m.created_at,m.deleted_at,m.name,m.count,m.price,m_0.product_id,m_0.title,m_0.custom_page,m_0.extra,m_0.color,m_0_0.name,m_0_0.code FROM product as `m` JOIN `product_detail` AS `m_0` ON m.id = m_0.product_id JOIN `color` AS `m_0_0` ON m_0.color = m_0_0.name WHERE m.deleted_at IS NULL AND (m.name = ? OR m_0_0.name = ?) args:["clean stick","black"]

brick := toy.Model(&tab).Debug(). Join(Offsetof(tab.Detail)). Join(Offsetof(detailTab.ColorJoin)).OrderBy(Offsetof(colorTab.Name)). Swap().Swap() var scanData []Product result, err = brick.Find(&scanData) // SELECT m.id,m.created_at,m.deleted_at,m.name,m.count,m.price,m_0.product_id,m_0.title,m_0.custom_page,m_0.extra,m_0.color,m_0_0.name,m_0_0.code FROM product as m JOIN product_detail AS m_0 ON m.id = m_0.product_id JOIN color AS m_0_0 ON m_0.color = m_0_0.name WHERE m.deleted_at IS NULL ORDER BY m_0_0.name


Также может быть установлен GroupBy, но здесь не приводится пример.

**Предварительная загрузка при объединении**

Метод предварительной загрузки также работает в режиме объединения.

```golang
brick := toy.Model(&tab).Debug().
    Join(Offsetof(tab.Detail)).Preload(Offsetof(detailTab.Comment)).Enter().
    Join(Offsetof(detailTab.ColorJoin)).Swap().Swap()
var scanData []Product
result, err = brick.Find(&scanData)
// SELECT m.id,m.created_at,m.deleted_at,m.name,m.count,m.price,m_0.product_id,m_0.title,m_0.custom_page,m_0.extra,m_0.color,m_0_0.name,m_0_0.code FROM `product` as `m` JOIN `product_detail` AS `m_0` ON m.id = m_0.product_id JOIN `color` AS `m_0_0` ON m_0.color = m_0_0.name   WHERE m.deleted_at IS NULL
// SELECT id,created_at,updated_at,deleted_at,product_detail_product_id,data FROM `comment`   WHERE deleted_at IS NULL AND product_detail_product_id IN (?,?,?)  args:[1,2,3]

Пользовательское имя таблицы

Можно настроить собственное имя таблицы для разных платформ.

type User struct {
    ID uint32 `toyorm:"primary key"`
    Name string `toyorm:"index"`
    Platform string `toyorm:"-"`
}
func (u *User) TableName() string {
    return "user_" + u.Platform
}

brick := toy.Model(&User{Platform:"p1"}).Debug()
brick.CreateTable()
// CREATE TABLE user_p1 (id BIGINT AUTO_INCREMENT,name VARCHAR(255), PRIMARY KEY(id))
brick := toy.Model(&User{Platform:"p2"}).Debug()
brick.CreateTable()
// CREATE TABLE user_p2 (id BIGINT AUTO_INCREMENT,name VARCHAR(255), PRIMARY KEY(id))

Метод имени таблицы также работает при предварительной загрузке и объединении.

type UserDetail struct {
    ID uint32 `toyorm:"primary key"`
    UserID uint32
    Data string
}
func (u *UserDetail) TableName() string {
    return "user_detail_" + u.Platform
}

type User struct {
    ID uint32 `toyorm:"primary key"`
    Name string `toyorm:"index"`
    Detail *UserDetail
    Platform string `toyorm:"-"`
}
func (u *User) TableName() string {
    return "user_" + u.Platform
}

brick := toy.Model(&User{Platform:"p1", Detail:&UserDetail{Platform:"p1"}}).Debug().
    Preload(Offsetof(User{}.UserDetail)).Enter()
brick.CreateTable()
// CREATE TABLE user_p1 (id BIGINT AUTO_INCREMENT,name VARCHAR(255), PRIMARY KEY(id))
// CREATE TABLE user_detail_p1 (id BIGINT AUTO_INCREMEHT, user_id BIGINT, data VARCHAR(255), PRIMARY KEY(id))

В режиме «один ко многим» или «многие ко многим» необходимо установить значение первого элемента. use Report to view sql action

report format

insert

user := User{
    Detail: &UserDetail{
        MainPage: "some html code with you page",
        Extra:    Extra{"title": "my blog"},
    },
    Blog: []Blog{
        {Title: "how to write a blog", Content: "first ..."},
        {Title: "blog introduction", Content: "..."},
    },
    Friends: []*User{
        {
            Detail: &UserDetail{
                MainPage: "some html code with you page",
                Extra:    Extra{},
            },
            Blog: []Blog{
                {Title: "some python tech", Content: "first ..."},
                {Title: "my eleme_union_meal usage", Content: "..."},
            },
            Name: "fatpigeon",
            Age:  18,
            Sex:  "male",
        },
    },
    Name: "bigpigeon",
    Age:  18,
    Sex:  "male",
}
result, err = brick.Save(&user)
// error process ...
fmt.Printf("report:\n%s\n", result.Report())

/*
// [0, ] means affected the 0 element
// [0-0, ] means affected the 0 element the 0 sub element
report:
[0, ] INSERT INTO user(created_at,updated_at,deleted_at,name,age,sex) VALUES(?,?,?,?,?,?)  args:["2018-02-28T17:31:20.012285+08:00","2018-02-28T17:31:20.012285+08:00",null,"bigpigeon",18,"male"]
    preload Detail
    [0-, ] INSERT INTO user_detail(user_id,main_page,extra) VALUES(?,?,?)  args:[1,"some html code with you page",{"title":"my blog"}]
    preload Blog
    [0-0, ] INSERT INTO blog(created_at,updated_at,deleted_at,user_id,title,content) VALUES(?,?,?,?,?,?)  args:["2018-02-28T17:31:20.013968+08:00","2018-02-28T17:31:20.013968+08:00",null,1,"how to write a blog","first ..."]
    [0-1, ] INSERT INTO blog(created_at,updated_at,deleted_at,user_id,title,content) VALUES(?,?,?,?,?,?)  args:["2018-02-28T17:31:20.013968+08:00","2018-02-28T17:31:20.013968+08:00",null,1,"blog introduction","..."]
    preload Friends
    [0-0, ] INSERT INTO user(created_at,updated_at,deleted_at,name,age,sex) VALUES(?,?,?,?,?,?)  args:["2018-02-28T17:31:20.015207+08:00","2018-02-28T17:31:20.015207+08:00",null,"fatpigeon",18,"male"]
        preload Detail
        [0-0-, ] INSERT INTO user_detail(user_id,main_page,extra) VALUES(?,?,?)  args:[2,"some html code with you page",{}]
        preload Blog
        [0-0-0, ] INSERT INTO blog(created_at,updated_at,deleted_at,user_id,title,content) VALUES(?,?,?,?,?,?)  args:["2018-02-28T17:31:20.016389+08:00","2018-02-28T17:31:20.016389+08:00",null,2,"some python tech","first ..."]
        [0-0-1, ] INSERT INTO blog(created_at,updated_at,deleted_at,user_id,title,content) VALUES(?,?,?,?,?,?)  args:["2018-02-28T17:31:20.016389+08:00","2018-02-28T17:31:20.016389+08:00",null,2,"my eleme_union_meal usage","..."]
*/

find

```golang
brick := brick.Preload(Offsetof(User{}.Friends)).
    Preload(Offsetof(User{}.Detail)).Enter().
    Preload(Offsetof(User{}.Blog)).Enter().
    Enter()
var users []User
result, err = brick.Find(&users)
// some error process
...
// print the report
fmt.Printf("report:\n%s\n", result.Report())

// report log
/*
report:
[0, 1, ] SELECT id,created_at,updated_at,deleted_at,name,age,sex FROM user WHERE deleted_at IS NULL  args:null
    preload Detail
    [0-, 1-, ] SELECT id,user_id,main_page,extra FROM user_detail WHERE user_id IN (?,?)  args:[2,1]
    preload Blog
    [0-0, 0-1, 1-0, 1-1, ] SELECT id,created_at,updated_at,deleted_at,user_id,title,content FROM blog WHERE deleted_at IS NULL AND user_id IN (?,?)  args:[1,2]
    preload Friends
    [0-0, ] SELECT id,created_at,updated_at,deleted_at,name,age,sex FROM user WHERE deleted_at IS NULL AND id IN (?)  args:[2]
        preload Detail
        [0-0-, ] SELECT id,user_id,main_page,extra FROM user_detail WHERE user_id IN (?)  args:[2]
        preload Blog
        [0-0-0, 0-0-1, ] SELECT

* * *

В запросе представлен фрагмент кода на языке Go, который выполняет операции с данными модели пользователя и его друзей. В коде используются структуры данных для представления информации о пользователе, его друзьях, деталях и блогах. Также в коде выполняется сохранение данных в базу данных и их извлечение.

Однако, без контекста сложно понять, что именно делает этот код. Возможно, он создаёт или обновляет записи в базе данных, связанные с пользователями и их друзьями. Или же он просто выводит информацию о существующих записях.

Для более точного понимания того, что делает этот код, необходимо знать контекст его использования и цели, которые он преследует. **use Err to view sql action error**

```golang
var users []struct {
    ID     uint32
    Age    bool
    Detail *UserDetail
    Blog   []Blog
}
result, err = brick.Find(&users)
if err != nil {
    panic(err)
}
if err := result.Err(); err != nil {
    fmt.Printf("error:\n%s\n", err)
}

/*
error:
SELECT id,age FROM user WHERE deleted_at IS NULL  args:null errors(
    [0]sql: Scan error on column index 1: sql/driver: couldn't convert "18" into type bool
    [1]sql: Scan error on column index 1: sql/driver: couldn't convert "18" into тип bool
)
*/

Selector

toyorm поддерживает следующий селектор:

Операция Селектор OffsetOf Имя строки map[OffsetOf]interface{} map[string]interface{} struct
Update Нет Нет Да Да Да Да
Insert Нет Нет Да Да Да Да
Save Нет Нет Да Да Да Да
Where & Conditions Да Да Нет Нет Нет Нет
WhereGroup & ConditionGroup Нет Нет Да Да Нет Нет
BindFields Да Да Нет Нет Нет Нет
Preload & Custom Preload Да Да Нет Нет Нет Нет
OrderBy Да Да Нет Нет Нет Нет
Find Нет Нет Нет Нет Нет Да

Collection

Коллекция предоставляет множество операций с базой данных.

ToyCollection

ToyCollection — это основа коллекции, она похожа на Toy.

toyCollection, err = toyorm.OpenCollection("sqlite3", []string{"", ""}...)

CollectionBrick

CollectionBrick используется для создания грамматики и управления базой данных, как ToyBrick.

brick := toyCollection.Model(&User{})

Selector

Селектор используется для выбора базы данных при вставке/сохранении.

CollectionBrick имеет стандартный селектор dbPrimaryKeySelector.

Вы можете настроить селектор БД.

func idSelector(n int, keys ...interface{}) int {
    sum := 0
    for _, k := range keys {
        switch val := k.(type) {
        case int:
            sum += val
        case int32:
            sum += int(val)
        case uint:
            sum += int(val)
        case uint32:
            sum += int(val)
        default:
            panic("primary key type not match")
        }
    }
    return sum % n
}

brick = brick.Selector(idSelector)

Генератор идентификаторов

В этом режиме тег поля auto_increment был недействителен.

Вам нужно создать генератор идентификаторов.

// контекстный генератор идентификаторов
type IDGenerator map[*toyorm.Model]chan int

func (g IDGenerator) CollectionIDGenerate(ctx *toyorm.CollectionContext) error {
    if g[ctx.Brick.Model] == nil {
        idGenerate := make(chan int)
        go func() {
            current := 1
            for {
                // если есть redis, используйте redis-cli
                idGenerate <- current
                current++
            }

        }()
        g[ctx.Brick.Model] = idGenerate
    }
    primaryKey := ctx.Brick.Model.GetOnePrimary()
    for _, record := range ctx.Result.Records.GetRecords() {
        if field := record.Field(primaryKey.Name()); field.IsValid() == false || toyorm.IsZero(field) {
            v := <-g[ctx.Brick.Model]
            record.SetField(primaryKey.Name(), reflect.ValueOf(v))
        }
    }
    return nil

}

// установить генератор идентификатора в контексте модели
toy.SetModelHandlers("Save", brick.Model,
``` ```
toyorm.CollectionHandlersChain{idGenerate.CollectionIDGenerate})
// Устанавливаем генератор идентификаторов для всех моделей в контексте предварительной загрузки
for _, pBrick := range brick.MapPreloadBrick {
        toy.SetModelHandlers("Save", pBrick.Model, toyorm.CollectionHandlersChain{idGenerate.CollectionIDGenerate})
}

SQL-действия

SQL-действия с коллекцией Toy аналогичны действиям Toy.

Вставка:

users := []User{
    {Name: "Turing"},
    {Name: "Shannon"},
    {Name: "Ritchie"},
    {Name: "Jobs"},
}
result, err = userBrick.Insert(&users)
// Обработка ошибки

// Просмотр журнала отчётов
fmt.Printf("report:\n%s", result.Report())

Поиск:

var jobs User
result, err = userBrick.Where(toyorm.ExprEqual, Offsetof(User{}.Name), "Jobs").Find(&jobs)
// Обработка ошибки

// Просмотр журнала отчётов
fmt.Printf("report:\n%s", result.Report())

Удаление:

result, err = userBrick.Delete(&jobs)
// Обработка ошибки

// Просмотр журнала отчётов
fmt.Printf("delete report:\n%s\n", result.Report())

Пример коллекции

Здесь

Toy-Doctor

Проверка параметров в вызове метода ToyBrick

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

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

Введение

golang orm. Развернуть Свернуть
MIT
Отмена

Обновления

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

Участники

все

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

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