goen

package module
v0.0.0-alpha.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2018 License: MIT Imports: 15 Imported by: 0

README

wercker status Coverage Status godoc

goen

goen is a typesafe GOlang ENtity interface for relational databases.

It provides a way of programmatically to interact with relational databases. It aims to implement in a go way and not to provide fully ORM features.

goen has following concepts:

  • No any fields or methods introducing into user's struct
  • Work with plain go's database/sql
  • Based on RTTI
  • Support bulk operation
  • Go generate is used only for utilities, it's not a core logic

Installation

go get -u github.com/kamichidu/goen/...

goen provides a binary tool used by go generate, please be sure that $GOPATH/bin is on your $PATH .

Usage

Write your first entity is.

package entity

type User struct {
    UserID int `goen:"" primary_key:""`
    Name, Email, PasswordHash string
}

Then put the following on any file of that package:

//go:generate goen -o goen.go

Now, all you have to do is run go generate ./... and a goen.go file will be generated.

Define entities

An entity is just a go struct have a struct tag goen:"" . All fields of this struct will be columns in the database table. An entity also needs to have one primary key. The primary key is defined using the primary_key:"" struct tag on the primary key fields.

Let's review the rules and conventions for entity fields:

  • All the fields with basic types or types that implement sql.Scanner and driver.Valuer will be considered a column in the table of their matching type.
  • By default, the name of a table/view will be the name of the struct converted to lower snake case (e.g.User => user, UserFriend => user_friend)
  • By default, the name of a column will be the name of the struct field converted to lower snake case (e.g. UserName => user_name, UserID => user_id). You can override it with the struct tag column:"custom_name".

Struct tags

Tag Description
goen:"" Indicates this struct as an entity. goen finds structs that have this struct tag.
table:"table_name" Specifies a table name.
view:"view_name" Specifies a view name for readonly entity.
primary_key:"" Indicates this field is a part of primary key
primary_key:"column_name" Indicates this field is a part of primary key and specifies a column name
primary_key:"column_name,omitempty" Indicates this field is a part of primary key, specifies a column name and this field is omitting if empty
primary_key:",omitempty" Indicates this field is a part of primary key, and this field is omitting if empty
column:"column_name" Specifies a column name
column:"column_name,omitempty" Specifies a column name and this field is omitting if empty
column:",omitempty" Specifies this field is omitting if empty
foreign_key:"column_name" Indicates this field is referencing another entity, and specifies keys
foreign_key:"column_name1,column_name2:reference_column_name" Indicates this field is referencing another entity, and specifies key pairs
ignore:"" Specifies this columns is to be ignored

Documentation

Overview

Example (BulkOperation)
package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/kamichidu/goen"
	_ "github.com/kamichidu/goen/dialect/sqlite3"
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "./sqlite.db")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	ddl := []string{
		`drop table if exists testing`,
		`create table testing (id integer primary key, name varchar(256))`,
	}
	if _, err := db.Exec(strings.Join(ddl, ";")); err != nil {
		panic(err)
	}

	dbc := goen.NewDBContext("sqlite3", db)

	// set patch compiler, it compiles patches to bulk
	dbc.Compiler = goen.BulkCompiler

	for i := 0; i < 3; i++ {
		dbc.Patch(goen.InsertPatch(
			"testing",
			[]string{"name"},
			[]interface{}{fmt.Sprintf("name-%d", i)},
		))
	}
	dbc.DebugMode(true)
	dbc.Logger = log.New(os.Stdout, "", 0)
	if err := dbc.SaveChanges(); err != nil {
		panic(err)
	}
}
Output:

goen: "INSERT INTO testing (name) VALUES (?),(?),(?)" with [name-0 name-1 name-2]
Example (QueryCount)
package main

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/kamichidu/goen"
	_ "github.com/kamichidu/goen/dialect/sqlite3"
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "./sqlite.db")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	ddl := []string{
		`drop table if exists testing`,
		`create table testing (id integer primary key, name varchar(256))`,
	}
	if _, err := db.Exec(strings.Join(ddl, ";")); err != nil {
		panic(err)
	}

	dbc := goen.NewDBContext("sqlite3", db)

	for i := 0; i < 3; i++ {
		dbc.Patch(goen.InsertPatch(
			"testing",
			[]string{"name"},
			[]interface{}{fmt.Sprintf("name-%d", i)},
		))
	}
	if err := dbc.SaveChanges(); err != nil {
		panic(err)
	}

	row := dbc.QueryRow(`select count(*) from testing`)

	var count int64
	if err := row.Scan(&count); err != nil {
		panic(err)
	}
	fmt.Printf("dbc founds %d records\n", count)

	var records []map[string]interface{}
	rows, err := dbc.Query(`select id, name from testing`)
	if err != nil {
		panic(err)
	}
	defer rows.Close()

	if err := dbc.Scan(rows, &records); err != nil {
		panic(err)
	}

	fmt.Print("records:\n")
	for _, record := range records {
		fmt.Printf("id=%d name=%q\n", record["id"], record["name"])
	}
}
Output:

dbc founds 3 records
records:
id=1 name="name-0"
id=2 name="name-1"
id=3 name="name-2"
Example (Transaction)
package main

import (
	"database/sql"
	"fmt"
	"strings"

	"github.com/kamichidu/goen"
	_ "github.com/kamichidu/goen/dialect/sqlite3"
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "./sqlite.db")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	ddl := []string{
		`drop table if exists testing`,
		`create table testing (id integer primary key, name varchar(256))`,
	}
	if _, err := db.Exec(strings.Join(ddl, ";")); err != nil {
		panic(err)
	}

	dbc := goen.NewDBContext("sqlite3", db)

	tx, err := dbc.DB.Begin()
	if err != nil {
		panic(err)
	}
	txc := dbc.UseTx(tx)
	if txc.Tx != tx {
		panic("UseTx makes clones DBContext and set Tx with given value")
	}
	txc.Patch(&goen.Patch{
		Kind:      goen.PatchInsert,
		TableName: "testing",
		Columns:   []string{"name"},
		Values:    []interface{}{"kamichidu"},
	})
	if err := txc.SaveChanges(); err != nil {
		panic(err)
	}

	func() {
		rows, err := dbc.Query(`select * from testing`)
		if err != nil {
			panic(err)
		}
		defer rows.Close()

		var records []map[string]interface{}
		if err := dbc.Scan(rows, &records); err != nil {
			panic(err)
		}
		fmt.Printf("dbc founds %d records when not committed yet\n", len(records))
	}()
	func() {
		rows, err := txc.Query(`select * from testing`)
		if err != nil {
			panic(err)
		}
		defer rows.Close()

		var records []map[string]interface{}
		if err := txc.Scan(rows, &records); err != nil {
			panic(err)
		}
		fmt.Printf("txc founds %d records when not committed yet since it's same transaction\n", len(records))
	}()

	if err := tx.Commit(); err != nil {
		panic(err)
	}

	func() {
		rows, err := dbc.Query(`select * from testing`)
		if err != nil {
			panic(err)
		}
		defer rows.Close()

		var records []map[string]interface{}
		if err := dbc.Scan(rows, &records); err != nil {
			panic(err)
		}
		fmt.Printf("dbc founds %d records after committed\n", len(records))
	}()
	func() {
		_, err := txc.Query(`select * from testing`)
		fmt.Printf("txc returns error after committed: %q\n", err)
	}()
}
Output:

dbc founds 0 records when not committed yet
txc founds 1 records when not committed yet since it's same transaction
dbc founds 1 records after committed
txc returns error after committed: "sql: statement is closed"

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Register

func Register(name string, dialect dialect.Dialect)

func TxScope

func TxScope(tx *sql.Tx, err error) txScope
Example (Do)
package main

import (
	"database/sql"

	"github.com/kamichidu/goen"
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "./sqlite.db")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	dbc := goen.NewDBContext("sqlite3", db)

	err = goen.TxScope(db.Begin()).Do(func(tx *sql.Tx) error {
		txc := dbc.UseTx(tx)

		// manipulate with txc
		txc.QueryRow("select 1")

		// return nil, will be called tx.Commit() by TxScope
		// otherwise, will be called tx.Rollback()
		return nil
	})
	if err != nil {
		panic(err)
	}

}
Output:

Example (FuncCall)
package main

import (
	"database/sql"

	"github.com/kamichidu/goen"
	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "./sqlite.db")
	if err != nil {
		panic(err)
	}
	defer db.Close()

	dbc := goen.NewDBContext("sqlite3", db)

	err = goen.TxScope(db.Begin())(func(tx *sql.Tx) error {
		txc := dbc.UseTx(tx)

		// manipulate with txc
		txc.QueryRow("select 1")

		// return nil, will be called tx.Commit() by TxScope
		// otherwise, will be called tx.Rollback()
		return nil
	})
	if err != nil {
		panic(err)
	}

}
Output:

Types

type CompilerOptions

type CompilerOptions struct {
	StmtBuilder sqr.StatementBuilderType

	Patches *PatchList
}

type DBContext

type DBContext struct {
	// This field is readonly.
	// Sets via NewDBContext().
	DB *sql.DB

	// This field is readonly.
	// Sets via UseTx(); or nil.
	Tx *sql.Tx

	// The patch compiler.
	// This is used when need to compile patch to query.
	Compiler PatchCompiler

	// The logger for debugging.
	// This is used when DebugMode(true).
	Logger Logger

	// The depth limit to avoid circular Include work.
	// When this value is positive, Include works N-times.
	// When this value is negative, Include works unlimited.
	// When this value is 0, Include never works.
	// Default is 10.
	MaxIncludeDepth int
	// contains filtered or unexported fields
}

DBContext holds *sql.DB (and *sql.Tx) with contextual values. goen is intended to work with *sql.DB (and *sql.Tx) via DBContext.

func NewDBContext

func NewDBContext(dialectName string, db *sql.DB) *DBContext

NewDBContext creates DBContext with given dialectName and db.

func (*DBContext) CompilePatch

func (dbc *DBContext) CompilePatch() *SqlizerList

func (*DBContext) DebugMode

func (dbc *DBContext) DebugMode(enabled bool)

DebugMode sets debug flag.

func (*DBContext) Dialect

func (dbc *DBContext) Dialect() dialect.Dialect

Dialect returns dialect.Dialect holds by this context.

func (*DBContext) Include

func (dbc *DBContext) Include(v interface{}, sc *ScopeCache, loader IncludeLoader) error

func (*DBContext) Patch

func (dbc *DBContext) Patch(v *Patch)

func (*DBContext) Query

func (dbc *DBContext) Query(query string, args ...interface{}) (*sql.Rows, error)

func (*DBContext) QueryRow

func (dbc *DBContext) QueryRow(query string, args ...interface{}) *sql.Row

func (*DBContext) SaveChanges

func (dbc *DBContext) SaveChanges() error

func (*DBContext) SaveChangesContext

func (dbc *DBContext) SaveChangesContext(ctx context.Context) error

func (*DBContext) Scan

func (dbc *DBContext) Scan(rows *sql.Rows, v interface{}) error

func (*DBContext) UseTx

func (dbc *DBContext) UseTx(tx *sql.Tx) *DBContext

UseTx returns clone of current DBContext with given *sql.Tx.

type IncludeBuffer

type IncludeBuffer list.List

func (*IncludeBuffer) AddRecords

func (l *IncludeBuffer) AddRecords(records interface{})

type IncludeLoader

type IncludeLoader interface {
	Load(later *IncludeBuffer, sc *ScopeCache, records interface{}) error
}

type IncludeLoaderFunc

type IncludeLoaderFunc func(*IncludeBuffer, *ScopeCache, interface{}) error

func (IncludeLoaderFunc) Load

func (fn IncludeLoaderFunc) Load(later *IncludeBuffer, sc *ScopeCache, records interface{}) error

type IncludeLoaderList

type IncludeLoaderList []IncludeLoader

func (*IncludeLoaderList) Append

func (list *IncludeLoaderList) Append(v ...IncludeLoader)

func (IncludeLoaderList) Load

func (list IncludeLoaderList) Load(later *IncludeBuffer, sc *ScopeCache, records interface{}) error

type Logger

type Logger interface {
	Print(...interface{})
	Printf(string, ...interface{})
}

type MapRowKey

type MapRowKey struct {
	Table string

	Key map[string]interface{}
}

func (*MapRowKey) RowKey

func (key *MapRowKey) RowKey() ([]string, []interface{})

func (*MapRowKey) TableName

func (key *MapRowKey) TableName() string

func (*MapRowKey) ToSql

func (key *MapRowKey) ToSql() (string, []interface{}, error)

type MetaSchema

type MetaSchema struct {
	// contains filtered or unexported fields
}

func (*MetaSchema) Compute

func (m *MetaSchema) Compute()

func (*MetaSchema) DeletePatchOf

func (m *MetaSchema) DeletePatchOf(entity interface{}) *Patch

func (*MetaSchema) InsertPatchOf

func (m *MetaSchema) InsertPatchOf(entity interface{}) *Patch

func (*MetaSchema) KeyStringFromRowKey

func (m *MetaSchema) KeyStringFromRowKey(rowKey RowKey) (string, error)

func (*MetaSchema) LoadOf

func (m *MetaSchema) LoadOf(entity interface{}) *metaTable

func (*MetaSchema) PrimaryKeyOf

func (m *MetaSchema) PrimaryKeyOf(entity interface{}) RowKey

func (*MetaSchema) Register

func (m *MetaSchema) Register(entity interface{})

func (*MetaSchema) RowKeysOf

func (m *MetaSchema) RowKeysOf(entity interface{}) (primary RowKey, refes []RowKey)

func (*MetaSchema) UpdatePatchOf

func (m *MetaSchema) UpdatePatchOf(entity interface{}) *Patch

type Patch

type Patch struct {
	Kind PatchKind

	TableName string

	RowKey RowKey

	Columns []string

	Values []interface{}
}

func DeletePatch

func DeletePatch(tableName string, rowKey RowKey) *Patch

func InsertPatch

func InsertPatch(tableName string, columns []string, values []interface{}) *Patch

func UpdatePatch

func UpdatePatch(tableName string, columns []string, values []interface{}, rowKey RowKey) *Patch

type PatchCompiler

type PatchCompiler interface {
	Compile(*CompilerOptions) *SqlizerList
}
var (
	DefaultCompiler PatchCompiler = PatchCompilerFunc(compilePatch)

	BulkCompiler PatchCompiler = &bulkCompiler{}
)

type PatchCompilerFunc

type PatchCompilerFunc func(*CompilerOptions) *SqlizerList

func (PatchCompilerFunc) Compile

func (fn PatchCompilerFunc) Compile(opts *CompilerOptions) (sqlizers *SqlizerList)

type PatchElement

type PatchElement struct {
	// contains filtered or unexported fields
}

PatchElement is typed version of list.Element

func (*PatchElement) GetValue

func (e *PatchElement) GetValue() *Patch

GetValue gets the value stored with this element.

func (*PatchElement) Next

func (e *PatchElement) Next() *PatchElement

Next returns the next list element or nil.

func (*PatchElement) Prev

func (e *PatchElement) Prev() *PatchElement

Prev returns the previous list element or nil.

func (*PatchElement) SetValue

func (e *PatchElement) SetValue(v *Patch)

SetValue sets value with this element.

type PatchKind

type PatchKind int
const (
	PatchInsert PatchKind = iota
	PatchUpdate
	PatchDelete
)

type PatchList

type PatchList list.List

PatchList is typed version of list.List

func NewPatchList

func NewPatchList() *PatchList

func (*PatchList) Back

func (l *PatchList) Back() *PatchElement

Back is typed version of (*list.List).Back

func (*PatchList) Front

func (l *PatchList) Front() *PatchElement

Front is typed version of (*list.List).Front

func (*PatchList) Init

func (l *PatchList) Init() *PatchList

Init is typed version of (*list.List).Init

func (*PatchList) InsertAfter

func (l *PatchList) InsertAfter(v *Patch, mark *PatchElement) *PatchElement

InsertAfter is typed version of (*list.List).InsertAfter

func (*PatchList) InsertBefore

func (l *PatchList) InsertBefore(v *Patch, mark *PatchElement) *PatchElement

InsertBefore is typed version of (*list.List).InsertBefore

func (*PatchList) Len

func (l *PatchList) Len() int

Len is typed version of (*list.List).Len

func (*PatchList) MoveAfter

func (l *PatchList) MoveAfter(e *PatchElement, mark *PatchElement)

MoveAfter is typed version of (*list.List).MoveAfter

func (*PatchList) MoveBefore

func (l *PatchList) MoveBefore(e *PatchElement, mark *PatchElement)

MoveBefore is typed version of (*list.List).MoveBefore

func (*PatchList) MoveToBack

func (l *PatchList) MoveToBack(e *PatchElement)

MoveToBack is typed version of (*list.List).MoveToBack

func (*PatchList) MoveToFront

func (l *PatchList) MoveToFront(e *PatchElement)

MoveToFront is typed version of (*list.List).MoveToFront

func (*PatchList) PushBack

func (l *PatchList) PushBack(v *Patch) *PatchElement

PushBack is typed version of (*list.List).PushBack

func (*PatchList) PushBackList

func (l *PatchList) PushBackList(other *PatchList)

PushBackList is typed version of (*list.List).PushBackList

func (*PatchList) PushFront

func (l *PatchList) PushFront(v *Patch) *PatchElement

PushFront is typed version of (*list.List).PushFront

func (*PatchList) PushFrontList

func (l *PatchList) PushFrontList(other *PatchList)

PushFrontList is typed version of (*list.List).PushFrontList

func (*PatchList) Remove

func (l *PatchList) Remove(e *PatchElement) *Patch

Remove is typed version of (*list.List).Remove

type RowKey

type RowKey interface {
	sqr.Sqlizer

	TableName() string

	RowKey() ([]string, []interface{})
}

type ScopeCache

type ScopeCache struct {
	Meta *MetaSchema
	// contains filtered or unexported fields
}

func NewScopeCache

func NewScopeCache(meta *MetaSchema) *ScopeCache

func (*ScopeCache) AddObject

func (sc *ScopeCache) AddObject(v interface{})

func (*ScopeCache) GetObject

func (sc *ScopeCache) GetObject(rowKey RowKey) interface{}

func (*ScopeCache) HasObject

func (sc *ScopeCache) HasObject(rowKey RowKey) bool

func (*ScopeCache) RemoveObject

func (sc *ScopeCache) RemoveObject(v interface{})

type SqlizerElement

type SqlizerElement struct {
	// contains filtered or unexported fields
}

SqlizerElement is typed version of list.Element

func (*SqlizerElement) GetValue

func (e *SqlizerElement) GetValue() squirrel.Sqlizer

GetValue gets the value stored with this element.

func (*SqlizerElement) Next

func (e *SqlizerElement) Next() *SqlizerElement

Next returns the next list element or nil.

func (*SqlizerElement) Prev

func (e *SqlizerElement) Prev() *SqlizerElement

Prev returns the previous list element or nil.

func (*SqlizerElement) SetValue

func (e *SqlizerElement) SetValue(v squirrel.Sqlizer)

SetValue sets value with this element.

type SqlizerList

type SqlizerList list.List

SqlizerList is typed version of list.List

func NewSqlizerList

func NewSqlizerList() *SqlizerList

func (*SqlizerList) Back

func (l *SqlizerList) Back() *SqlizerElement

Back is typed version of (*list.List).Back

func (*SqlizerList) Front

func (l *SqlizerList) Front() *SqlizerElement

Front is typed version of (*list.List).Front

func (*SqlizerList) Init

func (l *SqlizerList) Init() *SqlizerList

Init is typed version of (*list.List).Init

func (*SqlizerList) InsertAfter

func (l *SqlizerList) InsertAfter(v squirrel.Sqlizer, mark *SqlizerElement) *SqlizerElement

InsertAfter is typed version of (*list.List).InsertAfter

func (*SqlizerList) InsertBefore

func (l *SqlizerList) InsertBefore(v squirrel.Sqlizer, mark *SqlizerElement) *SqlizerElement

InsertBefore is typed version of (*list.List).InsertBefore

func (*SqlizerList) Len

func (l *SqlizerList) Len() int

Len is typed version of (*list.List).Len

func (*SqlizerList) MoveAfter

func (l *SqlizerList) MoveAfter(e *SqlizerElement, mark *SqlizerElement)

MoveAfter is typed version of (*list.List).MoveAfter

func (*SqlizerList) MoveBefore

func (l *SqlizerList) MoveBefore(e *SqlizerElement, mark *SqlizerElement)

MoveBefore is typed version of (*list.List).MoveBefore

func (*SqlizerList) MoveToBack

func (l *SqlizerList) MoveToBack(e *SqlizerElement)

MoveToBack is typed version of (*list.List).MoveToBack

func (*SqlizerList) MoveToFront

func (l *SqlizerList) MoveToFront(e *SqlizerElement)

MoveToFront is typed version of (*list.List).MoveToFront

func (*SqlizerList) PushBack

func (l *SqlizerList) PushBack(v squirrel.Sqlizer) *SqlizerElement

PushBack is typed version of (*list.List).PushBack

func (*SqlizerList) PushBackList

func (l *SqlizerList) PushBackList(other *SqlizerList)

PushBackList is typed version of (*list.List).PushBackList

func (*SqlizerList) PushFront

func (l *SqlizerList) PushFront(v squirrel.Sqlizer) *SqlizerElement

PushFront is typed version of (*list.List).PushFront

func (*SqlizerList) PushFrontList

func (l *SqlizerList) PushFrontList(other *SqlizerList)

PushFrontList is typed version of (*list.List).PushFrontList

func (*SqlizerList) Remove

func (l *SqlizerList) Remove(e *SqlizerElement) squirrel.Sqlizer

Remove is typed version of (*list.List).Remove

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL