QBS stands for Query By Struct. A Go ORM.

Qbs

Qbs stands for Query By Struct. A Go ORM. 中文版 README

Build Status

ChangeLog

  • 2013.03.14: index name has changed to {table name}_{column name}.
    • For existing application with existing database, update to this change may lead to creating redundant index, you may need to drop duplicated index manually.
  • 2013.03.14: make all internal structures unexported.
  • 2013.05.22: fixed memory leak issue.
  • 2013.05.23: Breaking change, improved performance up to 100%, removed deprecated methods, make Db and Tx field unexported.
  • 2013.05.25: Added QueryStruct method to do raw SQL query and fill the result into struct or slice of struct. Added support for defining custom table/struct and column/field name convertion function, so you are not forced to use snake case in database.
  • 2013.05.27: dropped go 1.0 support, only support go 1.1, changed to use build-in sql.DB connection pool.
  • 2013.05.29: Added Iterate method for processing large data without loading all rows into memory.
  • 2013.08.03: Fixed a bug that exceeds max_prepared_stmt_count

Features

  • Define table schema in struct type, create table if not exists.
  • Detect table columns in database and alter table add new column automatically.
  • Define selection clause in struct type, fields in the struct type become the columns to be selected.
  • Define join query in struct type by add pointer fields which point to the parent table's struct.
  • Do CRUD query by struct value.
  • After a query, all the data you need will be filled into the struct value.
  • Compose where clause by condition, which can easily handle complex precedence of "AND/OR" sub conditions.
  • If Id value in the struct is provided, it will be added to the where clause.
  • "Created" column will be set to current time when insert, "Updated" column will be set to current time when insert and update.
  • Struct type can implement Validator interface to do validation before insert or update.
  • Support MySQL, PosgreSQL and SQLite3.
  • Support connection pool.

Performance

Qbs.Find is about 60% faster on mysql, 130% faster on postgreSQL than raw Db.Query, about 20% slower than raw Stmt.Query. (benchmarked on windows). The reason why it is faster than Db.Query is because all prepared Statements are cached in map.

Install

Only support go 1.1+

Go get to get the most recent source code.

go get github.com/coocood/qbs

New version may break backwards compatibility, so for production project, it's better to download the tagged version. The most recent release is v0.2.

tags with same minor version would be backward compatible, e.g v0.1 and v0.1.1.

tags with different minor version would break compatibility, e.g v0.1.1 and v0.2.

API Documentation

See Gowalker for complete documentation.

Get Started

First you need to register your database

  • The qbs.Register function has two more arguments than sql.Open, they are database name and dilect instance.

  • You only need to call it once at the start time..

      func RegisterDb(){
          qbs.Register("mysql","qbs_test@/qbs_test?charset=utf8&parseTime=true&loc=Local", "qbs_test", qbs.NewMysql())
      }
    

Define a model User

  • If the field name is Id and field type is int64, the field will be considered as the primary key of the table. if you want define a primary key with name other than Id, you can set the tag qbs:"pk" to explictly mark the field as primary key.

  • The tag of Name field qbs:"size:32,index" is used to define the column attributes when create the table, attributes are comma seperated, inside double quotes.

  • The size:32 tag on a string field will be translated to SQL varchar(32), add index attribute to create a index on the column, add unique attribute to create a unique index on the column

  • Some DB (MySQL) can not create a index on string column without size defined.

      type User struct {
          Id   int64
          Name string `qbs:"size:32,index"`
      }
    
  • If you want to create multi column index, you should implement Indexed interface by define a Indexes method like the following.

      func (*User) Indexes(indexes *qbs.Indexes){
          //indexes.Add("column_a", "column_b") or indexes.AddUnique("column_a", "column_b")
      }
    

Create a new table

  • call qbs.GetMigration function to get a Migration instance, and then use it to create a table.

  • When you create a table, if the table already exists, it will not recreate it, but looking for newly added columns or indexes in the model, and execute add column or add index operation.

  • It is better to do create table task at the start time, because the Migration only do incremental operation, it is safe to keep the table creation code in production enviroment.

  • CreateTableIfNotExists expect a struct pointer parameter.

      func CreateUserTable() error{
          migration, err := qbs.GetMigration()
          if err != nil {
              return err
          }
          defer migration.Close()
          return migration.CreateTableIfNotExists(new(User))
      }
    

Get and use *qbs.Qbs instance:

  • Suppose we are in a handle http function. call qbs.GetQbs() to get a instance.

  • Be sure to close it by calling defer q.Close() after get it.

  • qbs has connection pool, the default size is 100, you can call qbs.ChangePoolSize() to change the size.

      func GetUser(w http.ResponseWriter, r *http.Request){
      	q, err := qbs.GetQbs()
      	if err != nil {
      		fmt.Println(err)
      		w.WriteHeader(500)
      		return
      	}
      	defer q.Close()
      	u, err := FindUserById(q, 6)
      	data, _ := json.Marshal(u)
      	w.Write(data)
      }
    

Inset a row:

  • Call Save method to insert or update the row,if the primary key field Id has not been set, Save would execute insert stamtment.

  • If Id is set to a positive integer, Save would query the count of the row to find out if the row already exists, if not then execute INSERT statement. otherwise execute UPDATE.

  • Save expects a struct pointer parameter.

      func CreateUser(q *qbs.Qbs) (*User,error){
          user := new(User)
          user.Name = "Green"
          _, err := q.Save(user)
          return user,err
      }
    

Find:

  • If you want to get a row by Id, just assign the Id value to the model instance.

      func FindUserById(q *qbs.Qbs, id int64) (*User, error) {
          user := new(User)
          user.Id = id
          err := q.Find(user)
          return user, err
      }
    
  • Call FindAll to get multiple rows, it expects a pointer of slice, and the element of the slice must be a pointer of struct.

      func FindUsers(q *qbs.Qbs) ([]*User, error) {
      	var users []*User
      	err := q.Limit(10).Offset(10).FindAll(&users)
      	return users, err
      }
    
  • If you want to add conditions other than Id, you should all Where method. WhereEqual("name", name) is equivalent to Where("name = ?", name), just a shorthand method.

  • Only the last call to Where/WhereEqual counts, so it is only applicable to define simple condition.

  • Notice that the column name passed to WhereEqual method is lower case, by default, all the camel case field name and struct name will be converted to snake case in database storage, so whenever you pass a column name or table name parameter in string, it should be in snake case.

  • You can change the convertion behavior by setting the 4 convertion function variable: FieldNameToColumnName,StructNameToTableName,ColumnNameToFieldName,TableNameToStructName to your own function.

      func FindUserByName(q *qbs.Qbs, n string) (*User, error) {
          user := new(User)
          err := q.WhereEqual("name", n).Find(user)
          return user, err
      }
    
  • If you need to define more complex condition, you should call Condition method, it expects a *Condition parameter. you can get a new condition instance by calling qbs.NewCondition, qbs.NewEqualCondition or qbs.NewInCondition function.

  • *Condition instance has And, Or ... methods, can be called sequentially to construct a complex condition.

  • Condition method of Qbs instance should only be called once as well, it will replace previous condition defined by Condition or Where methods.

      func FindUserByCondition(q *qbs.Qbs) (*User, error) {
          user := new(User)
          condition1 := qbs.NewCondition("id > ?", 100).Or("id < ?", 50).OrEqual("id", 75)
          condition2 := qbs.NewCondition("name != ?", "Red").And("name != ?", "Black")
          condition1.AndCondition(condition2)
          err := q.Condition(condition1).Find(user)
          return user, err
      }
    

Update a single row

  • To update a single row, you should call Find first, then update the model, and Save it.

      func UpdateOneUser(q *qbs.Qbs, id int64, name string) (affected int64, error){
      	user, err := FindUserById(q, id)
      	if err != nil {
      		return 0, err
      	}
      	user.Name = name
      	return q.Save(user)
      }
    

Update multiple row

  • Call Update to update multiple rows at once, but you should call this method cautiously, if the the model struct contains all the columns, it will update every column, most of the time this is not what we want.

  • The right way to do it is to define a temporary model struct in method or block, that only contains the column we want to update.

      func UpdateMultipleUsers(q *qbs.Qbs)(affected int64, error) {
      	type User struct {
      		Name string
      	}
      	user := new(User)
      	user.Name = "Blue"
      	return q.WhereEqual("name", "Green").Update(user)
      }
    

Delete

  • call Delete method to delete a row, there must be at least one condition defined, either by Id value, or by Where/Condition.

      func DeleteUser(q *qbs.Qbs, id int64)(affected int64, err error) {
      	user := new(User)
      	user.Id = id
      	return q.Delete(user)
      }
    

Define another table for join query

  • For join query to work, you should has a pair of fields to define the join relationship in the model struct.

  • Here the model Post has a AuthorId int64 field, and has a Author field of type *User.

  • The rule to define join relationship is like {xxx}Id int64, {xxx} *{yyy}.

  • As the Author field is pointer type, it will be ignored when creating table.

  • As AuthorId is a join column, a index of it will be created automatically when creating the table, so you don't have to add qbs:"index" tag on it.

  • You can also set the join column explicitly by add a tag qbs:"join:Author" to it for arbitrary field Name. here Author is the struct pointer field of the parent table model.

  • To define a foreign key constraint, you have to explicitly add a tag qbs:"fk:Author" to the foreign key column, and an index will be created as well when creating table.

  • Created time.Time field will be set to the current time when insert a row,Updated time.Time field will be set to current time when update the row.

  • You can explicitly set tag qbs:"created" or qbs:"updated" on time.Time field to get the functionality for arbitrary field name.

      type Post struct {
          Id int64
          AuthorId int64
          Author *User
          Content string
          Created time.Time
          Updated time.Time
      }
    

Omit some column

  • Sometimes we do not need to get every field of a model, especially for joined field (like Author field) or large field (like Content field).

  • Omit them will get better performance.

      func FindPostsOmitContentAndCreated(q *qbs.Qbs) ([]*Post, error) {
      	var posts []*Post
      	err := q.OmitFields("Content","Created").Find(&posts)
      	return posts, err
      }
    
  • With OmitJoin, you can omit every join fields, return only the columns in a single table, and it can be used along with OmitFields.

      func FindPostsOmitJoin(q *qbs.Qbs) ([]*Post, error) {
      	var posts []*Post
      	err := q.OmitJoin().OmitFields("Content").Find(&posts)
      	return posts, err
      }
    

Projects use Qbs:

Contributors

Erik Aigner Qbs was originally a fork from hood by Erik Aigner, but I changed more than 80% of the code, then it ended up become a totally different ORM.

NuVivo314, Jason McVetta, pix64, vadimi, Ravi Teja.

Comments
  • Save uses value zero for insert with pk

    Save uses value zero for insert with pk

    When we try to insert a struct with Id as pk and Name as string, the result query with arguments is:

    INSERT INTO `cars` (`id`, `name`) VALUES (?, ?) []interface {}{0, "test"}
    

    when it should be

    INSERT INTO `cars` (`name`) VALUES (?) []interface {}{"test"}
    

    or

    INSERT INTO `cars` (`id`, `name`) VALUES (?, ?) []interface {}{NULL, "test"}
    
  • Create indexes using

    Create indexes using "tableName_columns" instead of "columns" as name.

    This allows us to create different tables that have indexed fields with the same name. For example a table foo with an indexed field elvis, and table bar also with an indexed field named elvis. Before, qbs would try to name both indexes elvis, and fail trying to create the second one. Now it will name them foo_elvis and bar_elvis respectively.

  • Variables declared with types derived from built-in types are not being allowed

    Variables declared with types derived from built-in types are not being allowed

    I would like to modify the JSON formatting of date, so I had to declare new type based on time.Time. Now the problem is Qbs doesn't allow the type of table field to be anything other than built-in types. It would be nice if it can detect the derived types and still treat it like normal.

  • reflect.Set: value of type []uint8 is not assignable to type time.Time

    reflect.Set: value of type []uint8 is not assignable to type time.Time

    type User struct {
        Id             int64
        Loginname      string    `qbs:"size:50,notnull,unique"`
        Name, Password string    `qbs:"size:50,index"`
        Registered     bool      `qbs:"default:false,notnull,index"`
        Created        time.Time `qbs:"index"`
        Updated        time.Time `qbs:"index"`
    }
    
    func FindUserById(q *qbs.Qbs, id int64) (*User, error) {
        user := new(User)
        user.Id = id
        err := q.Find(user)
        return user, err
    }
    

    //////////////////////////////////////////////////////////////////////////////

        q, err := qbs.GetQbs()
        if err != nil {
            fmt.Println(err)
            this.Controller.Ctx.ResponseWriter.WriteHeader(500)
            return
        }
        defer q.Close()
        u, err := db.FindUserById(q, 1)
        fmt.Fprintf(this.Controller.Ctx.ResponseWriter, u.Loginname)
    

    ////////////////////////////////////////////////////////////////////////////////// error: reflect.Set: value of type []uint8 is not assignable to type time.Time github.com/coocood/qbs.base.setModelValue(0x1f0bb7e0, 0x1f010828, 0x5fc800, 0x1f0ebfb8, 0x146, ...) D:/My Design/golang/src/github.com/coocood/qbs/base.go:56 +0x415

  • (In postgresql can't select data)报一个bug,现在posrgresql上不能select数据,只可以写入数据

    (In postgresql can't select data)报一个bug,现在posrgresql上不能select数据,只可以写入数据

    when I try the code:

    func FindUserByName(q _qbs.Qbs, n string) (_User, error) { user := new(User) err := q.WhereEqual("name", n).Find(user) return user, err }

    It return an error "sql: no rows in result set"

    I try the the git://github.com/insionng/toropress . It can't not select user info to login In Postgresql . But in sqlite everything is ok.

  • mattn/go-sqlite3 升级后创建表报错

    mattn/go-sqlite3 升级后创建表报错

    创建表报错:Column name has changed, rename column migration is not supported.

    mattn/go-sqlite3 15天前的升级 https://github.com/mattn/go-sqlite3/commit/4a7ad328b73ee507116963d0fea8ecac4cb17990 把row.Next()返回由string变为了slice

    qbs\sqlite3.go 的 columnsInTable 函数应当要修改一下

  • struct中存在未导出的字段时,CreateTableIfNotExists 提示反射错误。

    struct中存在未导出的字段时,CreateTableIfNotExists 提示反射错误。

    我定义的struct如下:

    type Card struct {
        Id     int64     `qbs:"index"` 
        Worth  float64   
        KeyPre int64     `qbs:"unique"
        keySub int64     
        Hash   string    `qbs:"size:40,index"` 
        User   int64     
        Time   time.Time 
    }
    

    其中的 keySub 不需要导出,在调用 CreateTableIfNotExists ,提示如下错误:

    panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
    
    goroutine 1 [running]:
    reflect.valueInterface(0x501560, 0x126a9ed8, 0x67, 0x126ac301, 0x1, ...)
        D:/workspace/golang/sdk/1.1/src/pkg/reflect/value.go:983 +0x99
    reflect.Value.Interface(0x501560, 0x126a9ed8, 0x67, 0x0, 0x0, ...)
        D:/workspace/golang/sdk/1.1/src/pkg/reflect/value.go:972 +0x41
    github.com/coocood/qbs.structPtrToModel(0x52cc60, 0x126a9ec0, 0x1d0001, 0x0, 0x0, ...)
        D:/workspace/golang/3rdparty/src/github.com/coocood/qbs/model.go:194 +0x468
    github.com/coocood/qbs.(*Migration).CreateTableIfNotExists(0x126bce20, 0x52cc60, 0x126a9ec0, 0x1, 0x1, ...)
        D:/workspace/golang/3rdparty/src/github.com/coocood/qbs/migration.go:19 +0x4b
    

    应该是 model.go 在反射字段时,未排查unexported field

  • Cannot load MYSQL longtext fields into Go strings

    Cannot load MYSQL longtext fields into Go strings

    I have a table in a MySQL database describable by the following qbs struct

    type LongTextTable struct {
        Id int64
        Parent_id int64 `qbs:"fk:Parent"`
        Parent *ParentTable
        Key string `qbs:"size:255"`
        String_value string `qbs:"size:4294967295"` // This field is longtext, null in the table
    }
    

    I cannot seem to load the String_value field from the db, though I can load the other fields just fine using FindAll(). Are longtext fields supported by qbs or am I perhaps doing something wrong?

  • Fix panic in dialect. If FK Id is not referenced

    Fix panic in dialect. If FK Id is not referenced

    Hello,

    Can you review this patch. I'm not sure to make a side effect on this one...

    panic: reflect: call of reflect.Value.Int on zero Value
    
    goroutine 1 [running]:
    reflect.Value.Int(0x0, 0x0, 0x0, 0x0, 0x0, ...)
        /opt/golang/src/pkg/reflect/value.go:796 +0xd0
    github.com/coocood/qbs.(*Sqlite3).SetModelValue(0xf84008c570, 0x4c47b0, 0xf8400a6270, 0x146, 0x4c6750, ...)
        [...]/github.com/coocood/qbs/sqlite3.go:51 +0x33c
    github.com/coocood/qbs.(*Qbs).scanRows(0xf84008b280, 0x4c1e80, 0xf8400a06c0, 0x160, 0xf8400a4a80, ...)
        [...]/github.com/coocood/qbs/qbs.go:225 +0x83e
    github.com/coocood/qbs.(*Qbs).doQueryRow(0xf84008b280, 0x4c1e70, 0xf8400a06c0, 0xf8400a7340, 0xf800000338, ...)
        [...]/github.com/coocood/qbs/qbs.go:167 +0x377
    github.com/coocood/qbs.(*Qbs).Find(0xf84008b280, 0x4c1e70, 0xf8400a06c0, 0xf8400a06c0, 0x100000001, ...)
        [...]/github.com/coocood/qbs/qbs.go:139 +0x349
    

    If Id in FK isn't on database.

    Regards.

    In other possible patch is:

    diff --git a/qbs.go b/qbs.go
    index 2fa4bfd..d6ca513 100644
    --- a/qbs.go
    +++ b/qbs.go
    @@ -226,7 +226,7 @@ func (q *Qbs) scanRows(rowValue reflect.Value, rows *sql.Rows) (err error) {
                                    subStruct.Set(reflect.New(subStruct.Type().Elem()))
                            }
                            subField := subStruct.Elem().FieldByName(snakeToUpperCamel(paths[1]))
    -                       if subField.IsValid() {
    +                       if subField.IsValid() && value.Elem().IsValid() {
                                    err = q.Dialect.SetModelValue(value, subField)
                                    if err != nil {
                                            return
    
    
  • Allow nullable fields

    Allow nullable fields

    This is a small patch that is built on my previous PR. This PR allows the field values to be null if the struct defines the fields to be pointers to one of the four types that the DB driver interface requires to be nullable.

  • 执行多次qbs.Exec后报1461错误

    执行多次qbs.Exec后报1461错误

    请教,在执行多次qbs.Exec(sql)后,会报这个错误
    1461: Can' t create more than max_prepared_stmt_count statements (current value: 16382)
    数据库显示:
    | Com_stmt_close | 456 |
    | Com_stmt_execute | 132936 |
    | Com_stmt_fetch | 0 |
    | Com_stmt_prepare | 91187 |
    请问,要在哪进行com_stmt_close关闭

  • struct field   time.Time   turn into  text after    migration

    struct field time.Time turn into text after migration

    package main
    
    import (
    	"fmt"
    	"github.com/coocood/qbs"
    	_ "github.com/mattn/go-sqlite3"
    	"log"
    
    	"time"
    )
    
    type Blog struct {
    	Id int64
     
    	Title     string    `db:"title"`
    	Author    string    `db:"author"`
    	Published time.Time `db:"published"`
    }
    
    func checkErr(err error, msg string) {
    	if err != nil {
    		log.Fatalln(msg, err)
    	}
    }
    
    func main() {
    
    	qbs.Register("sqlite3", "blog.db", "qbs_test", qbs.NewSqlite3())
    
    	q, err := qbs.GetQbs()
    	fmt.Print(q, err)
    	migration, err := qbs.GetMigration()
     
    	defer migration.Close()
    	err = migration.CreateTableIfNotExists(new(Blog))
    	fmt.Println(err)
    }
    
    

    generate table schema

    CREATE TABLE `blog` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `title` text, `author` text, `published` text )
    
  • Return errors in QueryRow properly

    Return errors in QueryRow properly

    • oracle.go: Handle QueryRow multiple return values
    • postgres.go: Handle QueryRow multiple return values
    • qbs.go: Return an error during QueryRow

    This fixes a panic in the qbs.Count and qbs.ContainsValue implementation

    Signed-off-by: Jeff Mickey [email protected]

  • Panic in qbs.Count wrt row.Scan on qbs.go:610

    Panic in qbs.Count wrt row.Scan on qbs.go:610

    There is a panic in qbs.Count when the rows returned to count don't exist (because the table doesn't exist yet, the connection hasn't come up yet, etc).

    2015/02/10 01:46:58 setting up DB: postgres://postgres:seekret@localhost/candidate?sslmode=disable
    qbs:2015/02/10 01:46:58 pq: the database system is starting up
    qbs:2015/02/10 01:46:58 pq: the database system is starting up
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal 0xb code=0x1 addr=0x0 pc=0x6ba924]
    
    goroutine 26 [running]:
    runtime.panic(0x863220, 0xb4ddd3)
            /opt/go/src/pkg/runtime/panic.c:279 +0xf5
    database/sql.(*Row).Scan(0x0, 0x7f715abf8d90, 0x1, 0x1, 0x0, 0x0)
            /opt/go/src/pkg/database/sql/sql.go:1636 +0x44
    github.com/coocood/qbs.(*Qbs).Count(0xc208060880, 0x891fc0, 0xc208029b20, 0x0)
            /go/src/github.com/coocood/qbs/qbs.go:610 +0x293
    

    If you look at qbs.go:610, you'll see that it calls the following:

    if q.criteria.condition != nil {
            conditionSql, args := q.criteria.condition.Merge()
            query += " WHERE " + conditionSql
            row = q.QueryRow(query, args...)
    } else {
            row = q.QueryRow(query)
    }
    var count int64
    err := row.Scan(&count)
    

    And QueryRow can return a nil row, thus calling the row.Scan with a nil row value:

    func (q *Qbs) QueryRow(query string, args ...interface{}) *sql.Row {
            q.log(query, args...)
            query = q.Dialect.substituteMarkers(query)
            stmt, err := q.prepare(query)
            if err != nil {
                    q.updateTxError(err)
                    return nil
            }
            return stmt.QueryRow(args...)
    }
    

    In this case q.prepare is failing, as postgres is still coming up. Thus return nil is called, and not checked in the callers of QueryRow. I see this as a bug in:

    • ContainsValue on line 578
    • Count on line 610

    Whereas doQueryRow returns the errors all the way out. I'm pushing a pull request that fixes this up a bit in a moment.

  • Please support sqlite3 type

    Please support sqlite3 type "datetime"

    Sqlite3 has an own data type for timestamps: datetime. Qbs does not support "datetime" with sqlite. So it's hard to use an existing database.

    When I use "text" for my time.Time types, it's working well. But this is not acceptable, because it is not possible to use the sqlite datetime features.

  • Use google go linter

    Use google go linter

    I suggest that the source code get's linted with https://github.com/golang/lint

    According to the linter, it is for example necessary to name all methods that contain the keyword id uppercase, for example getId must be getID and so on.

    here is a complete list of those uppercase keywords: https://github.com/golang/lint/blob/master/lint.go#L656

Related tags
Using-orm-with-db - Trying to use ORM to work with PostgreSQL
Using-orm-with-db - Trying to use ORM to work with PostgreSQL

Using ORM with db This repo contains the training (rough) code, and possibly not

Jul 31, 2022
beedb is a go ORM,support database/sql interface,pq/mysql/sqlite

Beedb ❗ IMPORTANT: Beedb is being deprecated in favor of Beego.orm ❗ Beedb is an ORM for Go. It lets you map Go structs to tables in a database. It's

Nov 25, 2022
A simple wrapper around sql.DB to help with structs. Not quite an ORM.

go-modeldb A simple wrapper around sql.DB to help with structs. Not quite an ORM. Philosophy: Don't make an ORM Example: // Setup require "modeldb" db

Nov 16, 2019
ORM-ish library for Go

We've moved! gorp is now officially maintained at: https://github.com/go-gorp/gorp This fork was created when the project was moved, and is provided f

Aug 23, 2022
Simple Go ORM for Google/Firebase Cloud Firestore

go-firestorm Go ORM (Object-relational mapping) for Google Cloud Firestore. Goals Easy to use Non intrusive Non exclusive Fast Features Basic CRUD ope

Dec 1, 2022
Database agnostic ORM for Go

If you are looking for something more lightweight and flexible, have a look at jet For questions, suggestions and general topics visit the group. Inde

Nov 28, 2022
Generate a Go ORM tailored to your database schema.
Generate a Go ORM tailored to your database schema.

SQLBoiler is a tool to generate a Go ORM tailored to your database schema. It is a "database-first" ORM as opposed to "code-first" (like gorm/gorp). T

Jan 2, 2023
Simple and Powerful ORM for Go, support mysql,postgres,tidb,sqlite3,mssql,oracle, Moved to https://gitea.com/xorm/xorm

xorm HAS BEEN MOVED TO https://gitea.com/xorm/xorm . THIS REPOSITORY WILL NOT BE UPDATED ANY MORE. 中文 Xorm is a simple and powerful ORM for Go. Featur

Jan 3, 2023
A better ORM for Go, based on non-empty interfaces and code generation.

reform A better ORM for Go and database/sql. It uses non-empty interfaces, code generation (go generate), and initialization-time reflection as oppose

Dec 31, 2022
100% type-safe ORM for Go (Golang) with code generation and MySQL, PostgreSQL, Sqlite3, SQL Server support. GORM under the hood.

go-queryset 100% type-safe ORM for Go (Golang) with code generation and MySQL, PostgreSQL, Sqlite3, SQL Server support. GORM under the hood. Contents

Dec 30, 2022
A better ORM for Go, based on non-empty interfaces and code generation.
A better ORM for Go, based on non-empty interfaces and code generation.

A better ORM for Go and database/sql. It uses non-empty interfaces, code generation (go generate), and initialization-time reflection as opposed to interface{}, type system sidestepping, and runtime reflection. It will be kept simple.

Dec 29, 2022
Simple and performant ORM for sql.DB

Simple and performant ORM for sql.DB Main features are: Works with PostgreSQL, MySQL, SQLite. Selecting into a map, struct, slice of maps/structs/vars

Jan 4, 2023
An orm library support nGQL for Golang

norm An ORM library support nGQL for Golang. Overview Build insert nGQL by struct / map (Support vertex, edge). Parse Nebula execute result to struct

Dec 1, 2022
golang orm

korm golang orm, 一个简单易用的orm, 支持嵌套事务 安装 go get github.com/wdaglb/korm go get github.com/go-sql-driver/mysql 支持数据库 mysql https://github.com/go-sql-driv

Oct 31, 2022
Golang ORM with focus on PostgreSQL features and performance

go-pg is in a maintenance mode and only critical issues are addressed. New development happens in Bun repo which offers similar functionality but works with PostgreSQL, MySQL, and SQLite.

Jan 8, 2023
GorosePro(Go ORM),这个版本是Gorose的专业版

GorosePro(Go ORM),这个版本是Gorose的专业版

Dec 14, 2022
The fantastic ORM library for Golang, aims to be developer friendly

GORM The fantastic ORM library for Golang, aims to be developer friendly. Overview Full-Featured ORM Associations (Has One, Has Many, Belongs To, Many

Nov 11, 2021
ORM for Cloud Spanner to boost your productivity
ORM for Cloud Spanner to boost your productivity

ORM for Cloud Spanner to boost your productivity ??

Nov 29, 2022
SQL mapper ORM framework for Golang
 SQL mapper ORM framework for Golang

SQL mapper ORM framework for Golang English 中文 Please read the documentation website carefully when using the tutorial. DOC Powerful Features High Per

Dec 8, 2021