Generate Go code to convert database rows into arbitrary structs. Works with any database driver. Don't have to worry about database columns and struct names matching or tagging structs. No ORM magic.
Install or upgrade Scaneo with this command.
go get -u github.com/variadico/scaneo
Otherwise, check out the releases page.
scaneo [options] paths...
-o, -output
Set the name of the generated file. Default is scans.go.
-p, -package
Set the package name for the generated file. Default is current
directory name.
-u, -unexport
Generate unexported functions. Default is export all.
-w, -whitelist
Only include structs specified in case-sensitive, comma-delimited
string.
-f, -funcs
Generate SQL helper functions. Default is false.
-i, -import
Override package to import type from.
-v, -version
Print version and exit.
-h, -help
Print help and exit.
First, we start with a file that looks like this, called tables.go
.
package models
import (
"time"
"github.com/lib/pq"
)
type Post struct {
ID int
Created time.Time
Published pq.NullTime
Draft bool
Title string
Body string
}
Second, run scaneo tables.go
. This will create a new file called scans.go
,
which looks like this.
// DON'T EDIT *** generated by scaneo *** DON'T EDIT //
package models
import "database/sql"
func ScanPost(r *sql.Row) (Post, error) {
var s Post
if err := r.Scan(
&s.ID,
&s.Created,
&s.Published,
&s.Draft,
&s.Title,
&s.Body,
); err != nil {
return Post{}, err
}
return s, nil
}
func ScanPosts(rs *sql.Rows) ([]Post, error) {
structs := make([]Post, 0, 16)
var err error
for rs.Next() {
var s Post
if err = rs.Scan(
&s.ID,
&s.Created,
&s.Published,
&s.Draft,
&s.Title,
&s.Body,
); err != nil {
return nil, err
}
structs = append(structs, s)
}
return structs, nil
}
Third, call those functions from other parts of your code, like this.
func serveHome(resp http.ResponseWriter, req *http.Request) {
rows, err := db.Query("select * from post")
if err != nil {
log.Println(err)
return
}
posts, err := models.ScanPosts(rows) // ScanPosts was auto-generated!
if err != nil {
log.Println(err)
}
// ... send posts to template or whatever...
}
If you want to use scaneo
with go generate
, then just add this comment to
the top of tables.go
.
//go:generate scaneo $GOFILE
package models
// ... rest of code...
Now you can call go generate
in package models
and scans.go
will be
created.
Why did you write this instead of using sqlx, modl, gorm, gorp, etc?
- Didn't know which one I should learn. Already knew
database/sql
. - All I wanted was structs from the database.
- I can write SQL.
In addition, I wasn't entirely sure how much more convenient an ORM would be in contrast to writing the SQL myself. After reading some usage examples, it kind of felt like I was writing SQL anyway.
// github.com/go-gorp/gorp
dbMap.Select(&users, "SELECT * FROM users WHERE last_name = ? OR age < ? ORDER BY age DESC LIMIT 2", "Doe", 12)
// github.com/eaigner/hood
hd.Where("last_name", "=", "Doe").Or("age", "<", 12).OrderBy("age").Limit(2).Find(&users)
// github.com/jmoiron/sqlx
db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")
// github.com/astaxie/beedb
orm.Where("last_name = ? OR age < ?", "Doe", 12).Limit(2).OrderBy("age").FindAll(&users)
Do my table columns have to match my struct field names?
Nope. The names don't actually matter at all. However, what does matter is the order in which you declare the types in your struct. That has to match the database table. So if your table looks like this:
user_id | first_name | last_name
---------+------------+-----------
1 | Silvio | Rodríguez
then, your struct field names must follow the same order, like this.
type User struct {
ID int
FirstName string
LastName string
}