Documentation
¶
Overview ¶
Package model allows Go structs to behave as database models.
While this package exports several types the only one you currently need to be concerned with is type Models. All of the examples in this package use a global instance of Models defined in the examples subpackage; you may refer to that global instance for an instantiation example.
Note that in the examples for this package when you see examples.Models or examples.Connect() it is referring the examples subdirectory for this package and NOT the subdirectory for sqlh (i.e. both sqlh and sqlh/model have an examples subdirectory.)
Index ¶
- Variables
- type Model
- type Models
- func (me *Models) Insert(Q sqlh.IQueries, value interface{}) error
- func (me *Models) Lookup(value interface{}) (m *Model, err error)
- func (me *Models) Register(value interface{}, opts ...interface{})
- func (me *Models) Update(Q sqlh.IQueries, value interface{}) error
- func (me *Models) Upsert(Q sqlh.IQueries, value interface{}) error
- type QueryBinding
- type TableName
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrUnsupported error = errors.New("unsupported")
Functions ¶
This section is empty.
Types ¶
type Model ¶
type Model struct {
// Table is the related database table.
Table schema.Table
// Statements are the SQL database statements.
Statements statements.Table
// V is a *set.Value of a model instance M.
V *set.Value
// VSlice is a *set.Value of a model slice []M.
VSlice *set.Value
// Mapping is the column to struct field mapping.
Mapping *set.Mapping
// BoundMapping is a cached BoundMapping of a zero value for
// the model. Gathering query arguments and scan targets will
// be faster when executing queries by performing the following:
// cp := Model.BoundMapping.Copy() // Copy the cached BoundMapping.
// cp.Rebind( modelInstance ) // Bind the copy to an instance of the model.
// cp.Fields( ... ) // Get a slice of query arguments by column name.
// cp.Assignable( ... ) // Get a slice of scan targets by column name.
BoundMapping set.BoundMapping
}
Model relates a Go type to its Table.
func (*Model) BindQuery ¶
func (me *Model) BindQuery(query *statements.Query) QueryBinding
BindQuery returns a QueryBinding that facilitates running queries against instaces of the model.
func (*Model) NewInstance ¶
func (me *Model) NewInstance() interface{}
NewInstance creates an instance of the model's zero value.
type Models ¶
type Models struct {
//
// Mapper defines how SQL column names map to fields in Go structs.
Mapper *set.Mapper
//
// Grammar defines the SQL grammar to use for SQL generation.
Grammar grammar.Grammar
//
// Models is a map of Go types to Model instances. This member is automatically
// instantiated during calls to Register().
Models map[reflect.Type]*Model
//
// StructTag specifies the struct tag name to use when inspecting types
// during register. If not set will default to "model".
StructTag string
}
Models is a registry of Models and methods to manipulate them.
Example (Insert) ¶
package main
import (
"fmt"
"time"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
var zero time.Time
model := &examples.Address{
// Id, CreatedTime, ModifiedTime are not set and updated by the database.
Street: "1234 The Street",
City: "Small City",
State: "ST",
Zip: "98765",
}
// Create a mock database.
db, err := examples.Connect(examples.ExAddressInsert)
if err != nil {
fmt.Println("err", err.Error())
return
}
// Insert our model.
if err := examples.Models.Insert(db, model); err != nil {
fmt.Println("err", err.Error())
return
}
// Check expected model fields are no longer zero values.
if model.Id == 0 {
fmt.Println("id not updated")
}
if model.CreatedTime.Equal(zero) {
fmt.Println("created time not updated")
}
if model.ModifiedTime.Equal(zero) {
fmt.Println("modified time not updated")
}
fmt.Printf("Model inserted.")
}
Output: Model inserted.
Example (InsertSlice) ¶
package main
import (
"fmt"
"time"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
var zero time.Time
models := []*examples.Address{
// Id, CreatedTime, ModifiedTime are not set and updated by the database.
{
Street: "1234 The Street",
City: "Small City",
State: "ST",
Zip: "98765",
},
{
Street: "55 Here We Are",
City: "Big City",
State: "TS",
Zip: "56789",
},
}
// Create a mock database.
db, err := examples.Connect(examples.ExAddressInsertSlice)
if err != nil {
fmt.Println("err", err.Error())
return
}
// Insert our models.
if err := examples.Models.Insert(db, models); err != nil {
fmt.Println("err", err.Error())
return
}
// Check expected model fields are no longer zero values.
for _, model := range models {
if model.Id == 0 {
fmt.Println("id not updated")
return
}
if model.CreatedTime.Equal(zero) {
fmt.Println("created time not updated")
return
}
if model.ModifiedTime.Equal(zero) {
fmt.Println("modified time not updated")
return
}
}
fmt.Printf("%v model(s) inserted.", len(models))
}
Output: 2 model(s) inserted.
Example (Relationship) ¶
package main
import (
"fmt"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
// This single example shows the common cases of INSERT|UPDATE|UPSERT as distinct code blocks.
// examples.Relationship has a composite key and no auto-updating fields.
{
// Demonstrates INSERT of a single model.
db, err := examples.Connect(examples.ExRelationshipInsert)
if err != nil {
fmt.Println(err.Error())
return
}
//
relate := &examples.Relationship{
LeftId: 1,
RightId: 10,
Toggle: false,
}
if err = examples.Models.Insert(db, relate); err != nil {
fmt.Println(err)
return
}
fmt.Println("Insert success.")
}
{
// Demonstrates UPDATE of a single model.
db, err := examples.Connect(examples.ExRelationshipUpdate)
if err != nil {
fmt.Println(err.Error())
return
}
//
relate := &examples.Relationship{
LeftId: 1,
RightId: 10,
Toggle: true,
}
if err = examples.Models.Update(db, relate); err != nil {
fmt.Println(err)
return
}
fmt.Println("Update success.")
}
{
// Demonstrates UPSERT of a single model.
db, err := examples.Connect(examples.ExRelationshipUpsert)
if err != nil {
fmt.Println(err.Error())
return
}
//
relate := &examples.Relationship{
LeftId: 1,
RightId: 10,
Toggle: false,
}
if err = examples.Models.Upsert(db, relate); err != nil {
fmt.Println(err)
return
}
fmt.Println("Upsert success.")
}
}
Output: Insert success. Update success. Upsert success.
Example (RelationshipSlice) ¶
package main
import (
"fmt"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
// This single example shows the common cases of INSERT|UPDATE|UPSERT as distinct code blocks.
// examples.Relationship has a composite key and no auto-updating fields.
{
// Demonstrates INSERT of a slice of models.
db, err := examples.Connect(examples.ExRelationshipInsertSlice)
if err != nil {
fmt.Println(err.Error())
return
}
//
relate := []*examples.Relationship{
{
LeftId: 1,
RightId: 10,
Toggle: false,
},
{
LeftId: 2,
RightId: 20,
Toggle: true,
},
{
LeftId: 3,
RightId: 30,
Toggle: false,
},
}
if err = examples.Models.Insert(db, relate); err != nil {
fmt.Println(err)
return
}
fmt.Println("Insert success.")
}
{
// Demonstrates UPDATE of a slice of models.
db, err := examples.Connect(examples.ExRelationshipUpdateSlice)
if err != nil {
fmt.Println(err.Error())
return
}
//
relate := []*examples.Relationship{
{
LeftId: 1,
RightId: 10,
Toggle: true,
},
{
LeftId: 2,
RightId: 20,
Toggle: false,
},
{
LeftId: 3,
RightId: 30,
Toggle: true,
},
}
if err = examples.Models.Update(db, relate); err != nil {
fmt.Println(err)
return
}
fmt.Println("Update success.")
}
{
// Demonstrates UPSERT of a slice of models.
db, err := examples.Connect(examples.ExRelationshipUpsertSlice)
if err != nil {
fmt.Println(err.Error())
return
}
//
relate := []*examples.Relationship{
{
LeftId: 1,
RightId: 10,
Toggle: false,
},
{
LeftId: 2,
RightId: 20,
Toggle: true,
},
{
LeftId: 3,
RightId: 30,
Toggle: false,
},
}
if err = examples.Models.Upsert(db, relate); err != nil {
fmt.Println(err)
return
}
fmt.Println("Upsert success.")
}
}
Output: Insert success. Update success. Upsert success.
Example (Update) ¶
package main
import (
"fmt"
"time"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
var zero time.Time
model := &examples.Address{
Id: 42,
CreatedTime: time.Now().Add(-1 * time.Hour),
// ModifiedTime is zero value; will be updated by database.
Street: "1234 The Street",
City: "Small City",
State: "ST",
Zip: "98765",
}
// Create a mock database.
db, err := examples.Connect(examples.ExAddressUpdate)
if err != nil {
fmt.Println("err", err.Error())
return
}
// Update our model.
if err := examples.Models.Update(db, model); err != nil {
fmt.Println("err", err.Error())
return
}
// Check expected model fields are no longer zero values.
if model.ModifiedTime.Equal(zero) {
fmt.Println("modified time not updated")
}
fmt.Printf("Model updated.")
}
Output: Model updated.
Example (UpdateSlice) ¶
package main
import (
"fmt"
"time"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
var zero time.Time
models := []*examples.Address{
// ModifiedTime is not set and updated by the database.
{
Id: 42,
CreatedTime: time.Now().Add(-2 * time.Hour),
Street: "1234 The Street",
City: "Small City",
State: "ST",
Zip: "98765",
},
{
Id: 62,
CreatedTime: time.Now().Add(-1 * time.Hour),
Street: "55 Here We Are",
City: "Big City",
State: "TS",
Zip: "56789",
},
}
// Create a mock database.
db, err := examples.Connect(examples.ExAddressUpdateSlice)
if err != nil {
fmt.Println("err", err.Error())
return
}
// Update our models.
if err := examples.Models.Update(db, models); err != nil {
fmt.Println("err", err.Error())
return
}
// Check expected model fields are no longer zero values.
for _, model := range models {
if model.ModifiedTime.Equal(zero) {
fmt.Println("modified time not updated")
return
}
}
fmt.Printf("%v model(s) updated.", len(models))
}
Output: 2 model(s) updated.
Example (Upsert) ¶
package main
import (
"fmt"
"time"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
var zero time.Time
model := &examples.Upsertable{
Id: "some-unique-string",
String: "Hello, World!",
Number: 42,
}
// Create a mock database.
db, err := examples.Connect(examples.ExUpsert)
if err != nil {
fmt.Println("err", err.Error())
return
}
// Upsert our model.
if err := examples.Models.Upsert(db, model); err != nil {
fmt.Println("err", err.Error())
return
}
// Check expected model fields are no longer zero values.
if model.CreatedTime.Equal(zero) {
fmt.Println("created time not updated")
}
if model.ModifiedTime.Equal(zero) {
fmt.Println("modified time not updated")
}
fmt.Printf("Model upserted.")
}
Output: Model upserted.
Example (UpsertSlice) ¶
package main
import (
"fmt"
"time"
"github.com/nofeaturesonlybugs/sqlh/model/examples"
)
func main() {
var zero time.Time
models := []*examples.Upsertable{
{
Id: "some-unique-string",
String: "Hello, World!",
Number: 42,
},
{
Id: "other-unique-string",
String: "Goodbye, World!",
Number: 10,
},
}
// Create a mock database.
db, err := examples.Connect(examples.ExUpsertSlice)
if err != nil {
fmt.Println("err", err.Error())
return
}
// Upsert our models.
if err := examples.Models.Upsert(db, models); err != nil {
fmt.Println("err", err.Error())
return
}
// Check expected model fields are no longer zero values.
for _, model := range models {
if model.CreatedTime.Equal(zero) {
fmt.Println("created time not updated")
return
}
if model.ModifiedTime.Equal(zero) {
fmt.Println("modified time not updated")
return
}
}
fmt.Printf("%v model(s) upserted.", len(models))
}
Output: 2 model(s) upserted.
func (*Models) Register ¶
func (me *Models) Register(value interface{}, opts ...interface{})
Register adds a Go type to the Models instance. As part of initialization your application should register all types T that need to interact with the database.
Register is not goroutine safe; implement locking in the application level if required.
func (*Models) Upsert ¶ added in v0.3.0
Upsert attempts to persist values via UPSERTs.
Upsert only works on primary keys that are defined as "key"; in other words columns tagged with "key,auto" are not used in the generated query.
Upsert only supports primary keys; currently there is no support for upsert on UNIQUE indexes that are not primary keys.
type QueryBinding ¶
type QueryBinding interface {
// Query accepts either a single model M or a slice of models []M. It then
// runs and returns the result of QueryOne or QuerySlice.
Query(sqlh.IQueries, interface{}) error
// QueryOne runs the query against a single instance of the model.
QueryOne(sqlh.IQueries, interface{}) error
// QuerySlice runs the query against a slice of model instances.
QuerySlice(sqlh.IQueries, interface{}) error
}
QueryBinding binds a model with a query to facilitate running the query against instances of types described by the model.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package examples provides types and functions to facilitate the examples and test code in the model package.
|
Package examples provides types and functions to facilitate the examples and test code in the model package. |
|
Package statements builds uses a grammar to build SQL statements scoped to entities within the database.
|
Package statements builds uses a grammar to build SQL statements scoped to entities within the database. |