Getting Started
Installation
go get -u github.com/sqlbunny/sqlbunny
Project structure
sqlbunny's code generation is a library, not a command-line tool.
The recommended usage is making your own executable binary (ie, a Go main
package) that calls the sqlbunny library.
This binary should be completely separate from your main application binary.
Having both functionalities in a single binary might seem simpler, but it's not a good idea! You wouldn't be able to use sqlbunny if the application code does not compile (due to, for example, not having generated the sqlbunny models yet!).
An example folder structure looks like this. To run code generation, run the sqlbunny
executable. To run your app, run the app
executable.
- cmd/
- sqlbunny/ <- Runs sqlbunny, with your own configuration
- main.go
- app/ <- Runs your application
- main.go
- models/ <- autogenerated models package
- book.go
- bunny_*.go
Defining models
Put the following code at ./cmd/sqlbunny/main.go
:
package main
import (
. "github.com/sqlbunny/sqlbunny/gen/core"
"github.com/sqlbunny/sqlbunny/gen/migration"
"github.com/sqlbunny/sqlbunny/gen/stdtypes"
)
func main() {
Run(
&stdtypes.Plugin{},
&migration.Plugin{},
Model("book",
Field("id", "string", PrimaryKey),
Field("created_at", "time"),
Field("name", "string"),
),
)
}
The main function calls sqlbunny's Run
, which takes a slice of configuration items. Everything is done through config items: enabling plugins, defining types and models...
In this case, the first two items enable the stdtypes
and migration
plugins. stdtypes
defines many useful data types, and migration
adds support for generating migrations.
The last configuration item defines a model named book
.
Generating
To generate the models, run the sqlbunny package:
go run ./cmd/sqlbunny gen
This will generate the models
package. Let's take a look at it:
$ ls models/
bunny_array_utils.go book.go bunny_queries.go bunny_table_names.go bunny_types.go
sqlbunny has created book.go
with our model, along with a few utility files named bunny_*.go
. You can check out the Book
struct definition:
type Book struct {
ID string `bunny:"id" json:"id" `
CreatedAt time.Time `bunny:"created_at" json:"created_at" `
Name string `bunny:"name" json:"name" `
R *bookR `json:"-" toml:"-" yaml:"-"`
L bookL `json:"-" toml:"-" yaml:"-"`
}
As you can see, the struct has the 3 fields we defined previously, and two extra R
and L
fields, used for eager loading.
book.go
also contains many functions to query and modify Book
objects in the database.
Creating the tables
We have the Go code to interact with a database, but we don't have such a database with the correct tables yet. Let's fix that.
Run the following:
go run ./cmd/sqlbunny migration gensql
This is a command from the migration plugin.
This converts your model definitions to a series of SQL statements. Run them in your database to create the book
table.
CREATE TABLE "book" (
"id" text NOT NULL DEFAULT '',
"created_at" timestamptz NOT NULL,
"name" text NOT NULL DEFAULT ''
);
ALTER TABLE "book"
ADD CONSTRAINT "book_pkey" PRIMARY KEY ("id");
The SQL generated by gensql
is only suitable for creating all the tables in an empty database. If you make changes to your model
definitions and want to incrementally migrate existing databases, you will have to properly configure the migrations plugin.
Additionally, sqlbunny doesn't force you to use the migrations
plugin. You can use any tool to manage your schema. As long as the table and column names match, everything will work!
Using the generated models
Now that you have a models
package, let's do some queries! All the code below goes in ./cmd/app/main.go
.
First, we'll need some imports, and the main function
package main
import (
"context"
"database/sql"
"log"
"time"
"github.com/sqlbunny/sqlbunny/runtime/bunny"
"example.com/sqlbunny_demo/models"
)
func main() {
...
}
All ORM functions need a context with an active database. To set the database in a context, wrap the context with bunny.ContextWithDB
. The context can also be used to cancel in-progress queries (it's passed through to the SQL driver).
db, err := sql.Open("postgres", "host=localhost port=5432 dbname=postgres user=postgres password=postgres sslmode=disable")
if err != nil {
panic(err)
}
ctx := context.Background()
ctx = bunny.ContextWithDB(ctx, db)
Once you have a context, you can start doing ORM operations, such as inserting a book:
// Insert a new book
book := &models.Book{
ID: "574389527",
Name: "Harry Potter and the Philosopher's Stone",
CreatedAt: time.Now(),
}
err = book.Insert(ctx)
if err != nil {
panic(err)
}
...or fetching all books.
// Fetch all books
books, err := models.Books().All(ctx)
if err != nil {
panic(err)
}
You now have a working sqlbunny project. Enjoy!