Integrating Gorp with Revel

Being a web developer, I have been itching to start developing web application with Go. I explored around the laziest way to build one, and shortlisted Revel to build my first web application with Go.

The quick start guide for Revel is more then helpful in getting up and running. Like in most web apps, I need a database to store data and I have picked gorp for mapping database with my domain model.

In this example, we will create a user struct and map it to a table in mysql

Lets create a user struct, in models package

package models

import (
	"github.com/coopernurse/gorp"
	"github.com/revel/revel"	
)

type User struct {
	Id               int64
	Email            string
	FirstName        string
	LastName         string
	Password         string
	IsEnabled        bool

	// automatically used as the Version col
	// use table.SetVersionCol("columnName") to map a different
	// struct field as the version field
	Version      int64
	DateCreated  int64
	LastUpdated  int64

	//transients
	ConfirmPassword string
}

Now, we need to create a mapping between user struct and table in mysql.

Login to mysql console and create a database, lets call our database “paster”. We do not need to create any tables, just a database.

Now, Open app.conf file in conf directory of your app and add these 2 lines

db.import = github.com/go-sql-driver/mysql
db.driver = mysql

Now in our controllers package, we need to create a gorp.go file which has a GorpController and an init() function and looks like

package controllers

import (
	"database/sql"
	"github.com/coopernurse/gorp"
	_ "github.com/go-sql-driver/mysql"
	r "github.com/revel/revel"
	"social-paster/app/models"
)

var (
	Dbm *gorp.DbMap
)

func InitDB() {
	db, err := sql.Open("mysql", "db_username:db_password@/paster")
	if(err != nil){
	  panic("Unable to connect to the database")
	}
	Dbm = &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}

	setColumnSizes := func(t *gorp.TableMap, colSizes map[string]int) {
		for col, size := range colSizes {
			t.ColMap(col).MaxSize = size
		}
	}

	t := Dbm.AddTable(models.User{}).SetKeys(true, "Id")
	t.ColMap("ConfirmPassword").Transient = true
       
        t.ColMap("Email").SetNotNull(true)
	t.ColMap("FirstName").SetNotNull(true)
	t.ColMap("LastName").SetNotNull(true)
	t.ColMap("Password").SetNotNull(true)

	t.ColMap("Email").Unique = true

	setColumnSizes(t, map[string]int{
		"FirstName": 30,
		"LastName":  30,
	})

	Dbm.TraceOn("[gorp]", r.INFO)
	Dbm.CreateTablesIfNotExists()

}

type GorpController struct {
	*r.Controller
	Txn *gorp.Transaction
}

func (c *GorpController) Begin() r.Result {
	txn, err := Dbm.Begin()
	if err != nil {
		panic(err)
	}
	c.Txn = txn
	return nil
}

func (c *GorpController) Commit() r.Result {
	if c.Txn == nil {
		return nil
	}
	if err := c.Txn.Commit(); err != nil && err != sql.ErrTxDone {
		panic(err)
	}
	c.Txn = nil
	return nil
}

func (c *GorpController) Rollback() r.Result {
	if c.Txn == nil {
		return nil
	}
	if err := c.Txn.Rollback(); err != nil && err != sql.ErrTxDone {
		panic(err)
	}
	c.Txn = nil
	return nil
}

Here in the InitDB function, we largely define that we need a table mapped to user struct, we also define ConfirmPassword as a transient field and Email as unique, Also we define that each of FirstName and LastName of a user can not be larger then 30 characters. Also, we have set “Id” as an auto incremented Primary key.

In the GorpController, which is of type revel controller we define Begin() Commit() and RollBack() methods.

Now, we need to create another file called init.go (Note that this init.go is different from init.go defined in /app folder of your application) in controllers package and define a function init() in it.

package controllers

import (
	"github.com/revel/revel"
)

func init() {
	revel.OnAppStart(InitDB)
	revel.InterceptMethod((*GorpController).Begin, revel.BEFORE)
	revel.InterceptMethod((*GorpController).Commit, revel.AFTER)
	revel.InterceptMethod((*GorpController).Rollback, revel.FINALLY)
}

Here, on AppStart we call InitDB function, defined in gorp.go file. We also define intercept methods for our GorpControllers (controllers which “extend” GorpController, as we shall see in later posts) and specify that every action in our controllers start inside a database transaction and transaction is committed once the action gets completed and is rolled back in case there is any trouble or exception.

Now, when we run the app, if all is good, we should see a table in our “paster” database in mysql with the name “user”.

Cheers, have successfully mapped User struct to user table and can proceed further with saving and querying data.

Let me know, in case there is anything amiss here. Appreciate your feedback!

Cheers!

~~ Whizdumb ~~
Email : sachin.xpert@gmail.com

One response

Comments are closed.