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.
Hood is a database agnostic ORM for Go developed by @eaignr. It was written with following points in mind:
Dialects currently implemented
Adding a dialect is simple. Just create a new file named .go and the corresponding struct type, and mixin the Base dialect. Then implement the methods that are specific to the new dialect (for an example see postgres.go).
You can find the documentation over at GoDoc. To get a sense of the API, it's best to take a quick look at the unit tests, as they are always up to date!
If the dialect is registered, you can open the database directly using
hd, err := hood.Open("postgres", "user= dbname=")or you can pass an existing database and dialect to hood.New(*sql.DB, hood.Dialect)
hd := hood.New(db, NewPostgres())Schemas can be declared using the following syntax (only for demonstration purposes, would not produce valid SQL since it has 2 primary keys)
…Schema creation is completely optional, you can use any other tool you like.
The following built in field properties are defined (via sql: tag):
pk the field is a primary keynotnull the field must be NOT NULLsize(x) the field must have the specified size, e.g. for varchar size(128)default(x) the field has the specified default value, e.g. default(5) or default('orange')- ignores the fieldTo use migrations, you first have to install the hood tool. To do that run the following:
go get github.com/eaigner/hood
cd $GOPATH/src/github.com/eaigner/hood
./install.shAssuming you have your $GOPATH/bin directory in your PATH, you can now invoke the hood tool with hood.
Before we can use migrations we have to create a database configuration file first. To do this type
hood create:configThis command will create a db/config.json file relative to your current directory. It will look something like this:
{
"development": {
"driver": "",
"source": ""
},
"production": {
"driver": "",
"source": ""
},
"test": {
"driver": "",
"source": ""
}
}Populate it with your database credentials. The driver and source fields are the strings you would pass
to the sql.Open(2) function. Now hood knows about our database, so let's create our first migration with
hood create:migration CreateUserTableand another one
hood create:migration AddUserNameIndexThis command creates new migrations in db/migrations. Next we have to populate the
generated migrations Up (and Down) methods like so:
func (m *M) CreateUserTable_1357605106_Up(hd *hood.Hood) {
type Users struct {
Id hood.Id
First string
Last string
}
hd.CreateTable(&Users{})
}func (m *M) AddUserNameIndex_1357605116_Up(hd *hood.Hood) {
hd.CreateIndex("users", "name_index", true, "first", "last")
}The passed in hood instance is a transaction that will be committed after the method.
Now we can run migrations with
hood db:migrateand roll back with
hood db:rollbackFor a complete list of commands invoke hood -help
A schema.go file is automatically generated in the db directory. This file reflects the
current state of the database! In our example, it will look like this:
package db
import (
"github.com/eaigner/hood"
)
type Users struct {
Id hood.Id
First string
Last string
}
func (table *Users) Indexes(indexes *hood.Indexes) {
indexes.AddUnique("name_index", "first", "last")
}Besides the sql: struct tag, you can specify a validate: tag for model validation:
presence validates that a field is setlen(min:max) validates that a string field’s length lies within the specified rangelen(min:) validates that it has the specified min length, len(:max) or max lengthrange(min:max) validates that an int value lies in the specific rangerange(min:) validates that it has the specified min value,range(:max) or max value^[a-z]+$, validates that a string matches the regular expression^You can also define multiple validations on one field, e.g. validate:"len(:12),presence"
For more complex validations you can use custom validation methods. The methods
are added to the schema and must start with Validate and return an error.
For example:
func (u *User) ValidateUsername() error {
rx := regexp.MustCompile(`[a-z0-9]+`)
if !rx.MatchString(u.Name) {
return NewValidationError(1, "username contains invalid characters")
}
return nil
}You can add hooks to a model to run on a specific action like so:
func (u *User) BeforeUpdate() error {
u.Updated = time.Now()
return nil
}If the hook returns an error on a Before- action it is not performed!
The following hooks are defined:
Before/AfterSaveBefore/AfterInsertBefore/AfterUpdateBefore/AfterDelete…No open issues yet, or sync has not completed.