Struct mapping
The typical starting point with upper/db
is writing Go structs that define a
mapping between your Go application and the database it uses.
Basic field-to-column mapping
Fields in Go structs can be mapped to table records by using a special db
struct tag:
type User struct {
Name `db:"name"`
}
add the omitempty
tag option to struct fields that you don't want to send to
the database if they don't have a value, like IDs that are set to
auto-increment (or auto-generate) themselves:
// Person represents an item from the "people" collection.
type Person struct {
ID uint64 `db:"id,omitempty"` // Use `omitempty` for fields
// that are not to be sent by
// the adapter when empty.
Name string `db:"name"`
LastName string `db:"last_name"`
}
// Employee represents an item from the "employees" collection.
type Employee struct {
ID uint64 `db:"id,omitempty"` // Skip `id` column when zero.
FirstName sql.NullString `db:"first_name"`
LastName string `db:"last_name"`
}
You can provide different values in struct tags, including those used to map JSON values to fields:
type Person struct {
ID uint64 `db:"id,omitempty" json:"id"`
Name string `db:"name" json:"name"`
...
Password string `db:"password,omitempty" json:"-"`
}
Fields that don't have a db
struct tag will be omitted from queries:
type Person struct {
...
Token string
}
Complex cases for mapping
Embedding structs
Using the inline
option you can embed structs into other structs. See this
Person
struct, for instance:
type Person struct {
FirstName string `db:"first_name"`
LastName string `db:"last_name"`
}
This is a common struct that can be shared with other structs which also need
FirstName
and LastName
:
type Author struct {
ID int `db:"id,omitempty"`
Person `db:",inline"` // Embedded Person
}
type Employee struct {
ID int `db:"id,omitempty"`
Person `db:",inline"` // Embedded Person
}
See the following example: embedding Person
struct into Author
and Employee
Solving mapping ambiguities on JOINs
The previous example will work as long as you use the db:",inline"
tag. You
can even embed more than one struct into another, but you should be careful
with column ambiguities:
// Book that has ID.
type Book struct {
ID int `db:"id"` // Has an ID column.
Title string `db:"title"`
AuthorID int `db:"author_id"`
SubjectID int `db:"subject_id"`
}
// Author that has ID.
type Author struct {
ID int `db:"id"` // Also has an ID column.
LastName string `db:"last_name"`
FirstName string `db:"first_name"`
}
Embedding these two structs into a third one will cause a conflict of IDs, to
solve this conflict you can add an extra book_id
column mapping and use a
book_id
alias when querying for the book ID.
// BookAuthor
type BookAuthor struct {
// Both Author and Book have and ID column, we need this extra field to tell
// the difference between the ID of the book and the ID of the author.
BookID int `db:"book_id"`
Author `db:",inline"`
Book `db:",inline"`
}