Golang database/sql ORM

Connecting to a database

Bun works on top of database/sqlGetting started - 图1open in new window so the first thing you need to do is to create a sql.DB. In this tutorial we will be using SQLite but Bun also works with PostgreSQL, MySQL, and MSSQL.

  1. import (
  2. "database/sql"
  3. "github.com/uptrace/bun/driver/sqliteshim"
  4. )
  5. sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared")
  6. if err != nil {
  7. panic(err)
  8. }

Having a sql.DB, you can create a bun.DB using the corresponding SQLite dialect that comes with Bun:

  1. import (
  2. "github.com/uptrace/bun"
  3. "github.com/uptrace/bun/dialect/sqlitedialect"
  4. )
  5. db := bun.NewDB(sqldb, sqlitedialect.New())

To see executed queries in stdout, install a query hook:

  1. import "github.com/uptrace/bun/extra/bundebug"
  2. db.AddQueryHook(bundebug.NewQueryHook(
  3. bundebug.WithVerbose(true),
  4. bundebug.FromEnv("BUNDEBUG"),
  5. ))

Now you are ready to execute queries using database/sql API:

  1. res, err := db.ExecContext(ctx, "SELECT 1")
  2. var num int
  3. err := db.QueryRowContext(ctx, "SELECT 1").Scan(&num)

Or using Bun’s query builder:

  1. res, err := db.NewSelect().ColumnExpr("1").Exec(ctx)
  2. var num int
  3. err := db.NewSelect().ColumnExpr("1").Scan(ctx, &num)

Defining models

Bun uses struct-based models to construct queries and scan results. A typical Bun model looks like this:

  1. type User struct {
  2. bun.BaseModel `bun:"table:users,alias:u"`
  3. ID int64 `bun:",pk,autoincrement"`
  4. Name string
  5. }

Having a model, you can create and drop tables:

  1. // Create users table.
  2. res, err := db.NewCreateTable().Model((*User)(nil)).Exec(ctx)
  3. // Drop users table.
  4. res, err := db.NewDropTable().Model((*User)(nil)).Exec(ctx)
  5. // Drop and create tables.
  6. err := db.ResetModel(ctx, (*User)(nil))

Insert rows:

  1. // Insert a single user.
  2. user := &User{Name: "admin"}
  3. res, err := db.NewInsert().Model(user).Exec(ctx)
  4. // Insert multiple users (bulk-insert).
  5. users := []User{user1, user2}
  6. res, err := db.NewInsert().Model(&users).Exec(ctx)

Update rows:

  1. user := &User{ID: 1, Name: "admin"}
  2. res, err := db.NewUpdate().Model(user).Column("name").WherePK().Exec(ctx)

Delete rows:

  1. user := &User{ID: 1}
  2. res, err := db.NewDelete().Model(user).WherePK().Exec(ctx)

And select rows scanning the results:

  1. // Select a user by a primary key.
  2. user := new(User)
  3. err := db.NewSelect().Model(user).Where("id = ?", 1).Scan(ctx)
  4. // Select first 10 users.
  5. var users []User
  6. err := db.NewSelect().Model(&users).OrderExpr("id ASC").Limit(10).Scan(ctx)

Scanning query results

When it comes to scanning query results, Bun is very flexible and allows scanning into structs:

  1. user := new(User)
  2. err := db.NewSelect().Model(user).Limit(1).Scan(ctx)

Into scalars:

  1. var id int64
  2. var name string
  3. err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &id, &name)

Into a map[string]interface{}:

  1. var m map[string]interface{}
  2. err := db.NewSelect().Model((*User)(nil)).Limit(1).Scan(ctx, &m)

And into slices of the types above:

  1. var users []User
  2. err := db.NewSelect().Model(&users).Limit(1).Scan(ctx)
  3. var ids []int64
  4. var names []string
  5. err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &ids, &names)
  6. var ms []map[string]interface{}
  7. err := db.NewSelect().Model((*User)(nil)).Scan(ctx, &ms)

You can also return results from insert/update/delete queries and scan them too:

  1. var ids []int64
  2. res, err := db.NewDelete().Model((*User)(nil)).Returning("id").Exec(ctx, &ids)

Table relationships

Bun also recognizes common table relationships, for example, you can define a belongs-to relation:

  1. type Story struct {
  2. ID int64
  3. Title string
  4. AuthorID int64
  5. Author *User `bun:"rel:belongs-to,join:author_id=id"`
  6. }

And Bun will join the story author for you:

  1. story := new(Story)
  2. err := db.NewSelect().
  3. Model(story).
  4. Relation("Author").
  5. Limit(1).
  6. Scan(ctx)
  1. SELECT
  2. "story"."id", "story"."title", "story"."author_id",
  3. "author"."id" AS "author__id",
  4. "author"."name" AS "author__name"
  5. FROM "stories" AS "story"
  6. LEFT JOIN "users" AS "author" ON ("author"."id" = "story"."author_id")
  7. LIMIT 1

See exampleGetting started - 图2open in new window for details.

Using Bun with existing code

If you already have code that uses *sql.Tx or *sql.Conn, you can still use Bun query builder without rewriting the existing code:

  1. tx, err := sqldb.Begin()
  2. if err != nil {
  3. panic(err)
  4. }
  5. res, err := bundb.NewInsert().
  6. Conn(tx). // run the query using the existing transaction
  7. Model(&model).
  8. Exec(ctx)

What’s next

By now, you should have basic understanding of Bun API. Next, learn how to define models and write queries.