Query execution
Obtaining a Client interface
Queries can be executed against an RDBMS by any code that has access to a client manager. Typically your code will be part of a struct which has a member variable of type rdbms.ClientManager into which Granitic will inject a client manager.
type ArtistDAO struct {
DBClientManager rdbms.ClientManager
}
A method in your application can then use this manager to obtain a rdbms.Client, which is the interface that allows you to execute queries:
func (ad *ArtistDAO) FindAll() ([]*Artist,error) {
client, err := ad.DBClientManager.Client()
if err != nil {
return nil, err
}
}
or
func (ad *ArtistDAO) FindAll(ctx context.Context) ([]*Artist,error) {
client, err := ad.DBClientManager.ClientFromContext(ctx)
if err != nil {
return nil, err
}
}
Note that a rdbms.Client is not goroutine safe as it holds information about transaction state and implicitly about a connection to a database.
Executing templated queries
The rdbms.Client interface is designed to work closely with the query manager and provides a series of methods to execute templated queries. Those methods are named in a similar way to those on regexp.Regexp and are generally of the form:
SQLVerb[BindingType]QID[ParameterSource]
Where
- SQLVerb is Select, Delete, Update or Insert
- BindingType is optional and can be Bind or BindSingle
- ParameterSource is optional and can be either Param or Params
QID indicates the method is expecting to be passed the ID of a query template managed by the query manager
Binding
Methods with Bind
or BindSingle
provide a mechanism for automatically copying result data into structs or slices of
structs.
BindSingle
If the method name contains BindSingle
, you will pass a pointer to a struct into the method and its fields will be populated:
ad := new(ArtistDetail)
if found, err := rc.SelectBindSingleQIDParams("ARTIST_DETAIL", rid, ad); found {
return ad, err
} else {
return nil, err
}
This is typically used for queries that are guaranteed to return a single row.
Bind
If the method contains the word Bind
, you will supply an example ‘template’ instance of a struct and
the method will return a slice of that type:
params := make(map[string]interface{}
if r, err := client.SelectBindQIDParams("ARTIST_SEARCH_BASE", params, new(ArtistSearchResult)); err != nil {
return nil, err
} else {
return id.artistResults(r), nil
}
This is typically used for any query which returns an unknown number of rows.
Parameters sources
Parameters to populate template queries can either be supplied via a single name/value pair (methods with the word Param
)
or via a map or struct (methods with Params
).
When using a map, it should be of type map[string]interface{}
. When using structs, the field names on the struct
will be matched against expected variable names, unless the dbparam
struct tag is used instead.
See the Variables section of the query manager documentation for more details.
Executing non-templated queries
If you are not using the query manager, rdbms.Client
provides pass through access to the Exec
, Query
and QueryRow
methods on sql.DB
Not that these direct methods can be freely mixed with calls to the templated-query methods, even inside a transaction (see below).
Transactions
To start a transaction, invoke the StartTransaction
method on the Client
like:
client.StartTransaction()
defer db.Rollback()
````
and end it with:
```go
client.CommitTransaction()
The deferred Rollback
call will do nothing if the transaction has previously been committed.
Any query methods executed between StartTransaction
and CommitTransaction
/Rollback
will be executed
inside a ‘transaction’ as defined by your RDBMS.
Utility methods
The rdbms.Client interface provides a number of utility methods to support templated query execution.
FindFragment
returns the text of a query template that does not have any variables.
RegisterTempQuery
registers a query (not a template) with the given ID, allow those methods that expect a QID
to be used with a query not managed by the query manager.
These two methods can be used to support the construction of (some) dynamic queries without embedding SQL in your application’s Go code.
Next: Scheduled activities
Prev: Query management