mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
implement Struct and add more docs
This commit is contained in:
@@ -35,12 +35,59 @@ fmt.Println(args)
|
||||
|
||||
Following builders are implemented right now. API document and examples are provided in the `godoc` document.
|
||||
|
||||
* [SelectBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#SelectBuilder)
|
||||
* [InsertBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#InsertBuilder)
|
||||
* [UpdateBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#UpdateBuilder)
|
||||
* [DeleteBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#DeleteBuilder)
|
||||
* [Build](https://godoc.org/github.com/huandu/go-sqlbuilder#Build)
|
||||
* [Buildf](https://godoc.org/github.com/huandu/go-sqlbuilder#Buildf)
|
||||
* [Struct](https://godoc.org/github.com/huandu/go-sqlbuilder#Struct): Builder factory for a struct.
|
||||
* [SelectBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#SelectBuilder): Builder for SELECT.
|
||||
* [InsertBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#InsertBuilder): Builder for INSERT.
|
||||
* [UpdateBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#UpdateBuilder): Builder for UPDATE.
|
||||
* [DeleteBuilder](https://godoc.org/github.com/huandu/go-sqlbuilder#DeleteBuilder): Builder for DELETE.
|
||||
* [Build](https://godoc.org/github.com/huandu/go-sqlbuilder#Build): Freestyle builder using `fmt.Sprintf`-like syntax.
|
||||
* [Buildf](https://godoc.org/github.com/huandu/go-sqlbuilder#Buildf): Advanced freestyle builder using special syntax defined in [Args#Compile](https://godoc.org/github.com/huandu/go-sqlbuilder#Args.Compile).
|
||||
|
||||
### Using `Struct` to build query for a struct and scan value from results ###
|
||||
|
||||
`Struct` stores information of type and struct fields of a struct. It works like a factory of builders. We can use `Struct` methods to create initialized SELECT/INSERT/UPDATE/DELETE builders. It can help us to save time and avoid human-error when writing column names in query for a table.
|
||||
|
||||
Read [examples](https://godoc.org/github.com/huandu/go-sqlbuilder#Struct) for `Struct` to learn how to use it.
|
||||
|
||||
What's cool, we can use `Struct` as a kind of ORM. It's quite light weight and clean without any magic comparing with other ORM package.
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
ID int64 `db:"id"`
|
||||
Name string `db:"name"`
|
||||
Status int `db:"status"`
|
||||
}
|
||||
|
||||
var userStruct = NewStruct(new(User))
|
||||
|
||||
func ExampleStruct() {
|
||||
// Prepare SELECT query.
|
||||
sb := userStruct.Select("user")
|
||||
sb.Where(sb.E("id", 1234))
|
||||
sql, args := sb.Build()
|
||||
fmt.Println(sql)
|
||||
fmt.Println(args)
|
||||
|
||||
// Execute the query.
|
||||
rows, _ := db.Query(sql, args...)
|
||||
defer rows.Close()
|
||||
|
||||
// Scan row data to user.
|
||||
// Suppose we get following data.
|
||||
//
|
||||
// | id | name | status |
|
||||
// |------|--------|--------|
|
||||
// | 1234 | huandu | 1 |
|
||||
var user User
|
||||
rows.Scan(userStruct.Addr(&user)...)
|
||||
fmt.Printf("%#v", user)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name, status FROM user WHERE id = ? LIMIT 1
|
||||
// [1234]
|
||||
// sqlbuilder.User{ID:1234, Name:"huandu", Status:1}
|
||||
}
|
||||
```
|
||||
|
||||
### Nested SQL ###
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2018 Huan Du. All rights reserved.
|
||||
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||
|
||||
package sqlbuilder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestArgs(t *testing.T) {
|
||||
cases := map[string][]interface{}{
|
||||
"abc ? def\n[123]": []interface{}{"abc $? def", 123},
|
||||
"abc ? def\n[456]": []interface{}{"abc $0 def", 456},
|
||||
"abc def\n[]": []interface{}{"abc $1 def", 123},
|
||||
"abc ? def\n[789]": []interface{}{"abc ${s} def", Named("s", 789)},
|
||||
"abc def \n[]": []interface{}{"abc ${unknown} def ", 123},
|
||||
"abc $ def\n[]": []interface{}{"abc $$ def", 123},
|
||||
"abcdef\n[]": []interface{}{"abcdef$", 123},
|
||||
"abc ? ? ? ? def\n[123 456 123 456]": []interface{}{"abc $? $? $0 $? def", 123, 456, 789},
|
||||
"abc ? raw ? raw def\n[123 123]": []interface{}{"abc $? $? $0 $? def", 123, Raw("raw"), 789},
|
||||
}
|
||||
|
||||
for expected, c := range cases {
|
||||
args := new(Args)
|
||||
|
||||
for i := 1; i < len(c); i++ {
|
||||
args.Add(c[i])
|
||||
}
|
||||
|
||||
sql, values := args.Compile(c[0].(string))
|
||||
actual := fmt.Sprintf("%v\n%v", sql, values)
|
||||
|
||||
if actual != expected {
|
||||
t.Fatalf("invalid compile result. [expected:%v] [actual:%v]", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
// Copyright 2018 Huan Du. All rights reserved.
|
||||
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||
|
||||
package sqlbuilder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// DBTag is the struct tag to describe the name for a field in struct.
|
||||
DBTag = "db"
|
||||
|
||||
// FieldTag is the struct tag to describe the tag name for a field in struct.
|
||||
// Use "," to separate different tags.
|
||||
FieldTag = "fieldtag"
|
||||
)
|
||||
|
||||
// Struct represents a struct type.
|
||||
//
|
||||
// All methods in Struct are thread-safe.
|
||||
// We can define a global variable to hold a Struct and use it in any goroutine.
|
||||
type Struct struct {
|
||||
structType reflect.Type
|
||||
fieldAlias map[string]string
|
||||
taggedFields map[string][]string
|
||||
}
|
||||
|
||||
// NewStruct analyzes type information in structValue
|
||||
// and creates a new Struct with all structValue fields.
|
||||
// If structValue is not a struct, NewStruct returns a dummy Sturct.
|
||||
func NewStruct(structValue interface{}) *Struct {
|
||||
t := reflect.TypeOf(structValue)
|
||||
t = deferencedType(t)
|
||||
s := new(Struct)
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return s
|
||||
}
|
||||
|
||||
s.structType = t
|
||||
s.fieldAlias = map[string]string{}
|
||||
s.taggedFields = map[string][]string{}
|
||||
s.parse(t)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Struct) parse(t reflect.Type) {
|
||||
l := t.NumField()
|
||||
|
||||
for i := 0; i < l; i++ {
|
||||
field := t.Field(i)
|
||||
|
||||
if field.Anonymous {
|
||||
ft := deferencedType(field.Type)
|
||||
s.parse(ft)
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse DBTag.
|
||||
dbtag := field.Tag.Get(DBTag)
|
||||
aliasIdx := strings.Index(dbtag, ",")
|
||||
alias := field.Name
|
||||
|
||||
if aliasIdx == 0 || dbtag == "" {
|
||||
s.fieldAlias[field.Name] = field.Name
|
||||
} else {
|
||||
if aliasIdx > 0 {
|
||||
alias = dbtag[:aliasIdx]
|
||||
} else {
|
||||
alias = dbtag
|
||||
}
|
||||
|
||||
// Skip the field if DBTag is "-".
|
||||
if alias == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
s.fieldAlias[alias] = field.Name
|
||||
}
|
||||
|
||||
// Parse FieldTag.
|
||||
fieldtag := field.Tag.Get(FieldTag)
|
||||
tags := strings.Split(fieldtag, ",")
|
||||
|
||||
for _, t := range tags {
|
||||
if t != "" {
|
||||
s.taggedFields[t] = append(s.taggedFields[t], alias)
|
||||
}
|
||||
}
|
||||
|
||||
s.taggedFields[""] = append(s.taggedFields[""], alias)
|
||||
}
|
||||
}
|
||||
|
||||
// Select creates a new `SelectBuilder` with table name.
|
||||
// By default, all exported fields of the s are listed as columns in SELECT
|
||||
// and LIMIT is set to 1.
|
||||
//
|
||||
// Caller is responsible to set WHERE condition to find right record.
|
||||
func (s *Struct) Select(table string) *SelectBuilder {
|
||||
return s.SelectForTag(table, "")
|
||||
}
|
||||
|
||||
// SelectForTag creates a new `SelectBuilder` with table name for a specified tag.
|
||||
// By default, all fields of the s tagged with tag are listed as columns in SELECT
|
||||
// and LIMIT is set to 1.
|
||||
//
|
||||
// Caller is responsible to set WHERE condition to find right record.
|
||||
func (s *Struct) SelectForTag(table string, tag string) *SelectBuilder {
|
||||
sb := NewSelectBuilder()
|
||||
sb.From(table)
|
||||
sb.Limit(1)
|
||||
|
||||
if s.taggedFields == nil {
|
||||
return sb
|
||||
}
|
||||
|
||||
fields, ok := s.taggedFields[tag]
|
||||
|
||||
if ok {
|
||||
sb.Select(EscapeAll(fields...)...)
|
||||
} else {
|
||||
sb.Select("*")
|
||||
}
|
||||
|
||||
return sb
|
||||
}
|
||||
|
||||
// Update creates a new `UpdateBuilder` with table name.
|
||||
// By default, all exported fields of the s is assigned in UPDATE with the field values from value.
|
||||
// If value's type is not the same as that of s, Update returns a dummy `UpdateBuilder` with table name.
|
||||
//
|
||||
// Caller is responsible to set WHERE condition to match right record.
|
||||
func (s *Struct) Update(table string, value interface{}) *UpdateBuilder {
|
||||
return s.UpdateForTag(table, "", value)
|
||||
}
|
||||
|
||||
// UpdateForTag creates a new `UpdateBuilder` with table name.
|
||||
// By default, all fields of the s tagged with tag is assigned in UPDATE with the field values from value.
|
||||
// If value's type is not the same as that of s, Update returns a dummy `UpdateBuilder` with table name.
|
||||
//
|
||||
// Caller is responsible to set WHERE condition to match right record.
|
||||
func (s *Struct) UpdateForTag(table string, tag string, value interface{}) *UpdateBuilder {
|
||||
ub := NewUpdateBuilder()
|
||||
ub.Update(table)
|
||||
|
||||
if s.taggedFields == nil {
|
||||
return ub
|
||||
}
|
||||
|
||||
fields, ok := s.taggedFields[tag]
|
||||
|
||||
if !ok {
|
||||
return ub
|
||||
}
|
||||
|
||||
v := dereferencedValue(value)
|
||||
|
||||
if v.Type() != s.structType {
|
||||
return ub
|
||||
}
|
||||
|
||||
assignments := make([]string, 0, len(fields))
|
||||
|
||||
for _, f := range fields {
|
||||
name := s.fieldAlias[f]
|
||||
data := v.FieldByName(name).Interface()
|
||||
assignments = append(assignments, ub.Assign(f, data))
|
||||
}
|
||||
|
||||
ub.Set(assignments...)
|
||||
return ub
|
||||
}
|
||||
|
||||
// InsertInto creates a new `InsertBuilder` with table name.
|
||||
// By default, all exported fields of the s is inserted in INSERT with the field values from value.
|
||||
// If value's type is not the same as that of s, Update returns a dummy `InsertBuilder` with table name.
|
||||
func (s *Struct) InsertInto(table string, value interface{}) *InsertBuilder {
|
||||
return s.InsertIntoForTag(table, "", value)
|
||||
}
|
||||
|
||||
// InsertIntoForTag creates a new `InsertBuilder` with table name.
|
||||
// By default, all fields of the s tagged with tag is inserted in INSERT with the field values from value.
|
||||
// If value's type is not the same as that of s, Update returns a dummy `InsertBuilder` with table name.
|
||||
func (s *Struct) InsertIntoForTag(table string, tag string, value interface{}) *InsertBuilder {
|
||||
ib := NewInsertBuilder()
|
||||
ib.InsertInto(table)
|
||||
|
||||
if s.taggedFields == nil {
|
||||
return ib
|
||||
}
|
||||
|
||||
fields, ok := s.taggedFields[tag]
|
||||
|
||||
if !ok {
|
||||
return ib
|
||||
}
|
||||
|
||||
v := dereferencedValue(value)
|
||||
|
||||
if v.Type() != s.structType {
|
||||
return ib
|
||||
}
|
||||
|
||||
cols := make([]string, 0, len(fields))
|
||||
values := make([]interface{}, 0, len(fields))
|
||||
|
||||
for _, f := range fields {
|
||||
name := s.fieldAlias[f]
|
||||
data := v.FieldByName(name).Interface()
|
||||
cols = append(cols, f)
|
||||
values = append(values, data)
|
||||
}
|
||||
|
||||
ib.Cols(cols...)
|
||||
ib.Values(values...)
|
||||
return ib
|
||||
}
|
||||
|
||||
// DeleteFrom creates a new `DeleteBuilder` with table name.
|
||||
//
|
||||
// Caller is responsible to set WHERE condition to match right record.
|
||||
func (s *Struct) DeleteFrom(table string) *DeleteBuilder {
|
||||
db := NewDeleteBuilder()
|
||||
db.DeleteFrom(table)
|
||||
return db
|
||||
}
|
||||
|
||||
// Addr take address of all exported fields of the s from the value.
|
||||
// The returned result can be used in `Row#Scan` directly.
|
||||
func (s *Struct) Addr(value interface{}) []interface{} {
|
||||
return s.AddrForTag("", value)
|
||||
}
|
||||
|
||||
// AddrForTag take address of all fields of the s tagged with tag from the value.
|
||||
// The returned result can be used in `Row#Scan` directly.
|
||||
//
|
||||
// If tag is not defined in s in advance,
|
||||
func (s *Struct) AddrForTag(tag string, value interface{}) []interface{} {
|
||||
fields, ok := s.taggedFields[tag]
|
||||
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.AddrWithCols(fields, value)
|
||||
}
|
||||
|
||||
// AddrWithCols take address of all columns defined in cols from the value.
|
||||
// The returned result can be used in `Row#Scan` directly.
|
||||
func (s *Struct) AddrWithCols(cols []string, value interface{}) []interface{} {
|
||||
v := dereferencedValue(value)
|
||||
|
||||
if v.Type() != s.structType {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, c := range cols {
|
||||
if _, ok := s.fieldAlias[c]; !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
addrs := make([]interface{}, 0, len(cols))
|
||||
|
||||
for _, c := range cols {
|
||||
name := s.fieldAlias[c]
|
||||
data := v.FieldByName(name).Addr().Interface()
|
||||
addrs = append(addrs, data)
|
||||
}
|
||||
|
||||
return addrs
|
||||
}
|
||||
|
||||
func deferencedType(t reflect.Type) reflect.Type {
|
||||
for k := t.Kind(); k == reflect.Ptr || k == reflect.Interface; k = t.Kind() {
|
||||
t = t.Elem()
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func dereferencedValue(value interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
for k := v.Kind(); k == reflect.Ptr || k == reflect.Interface; k = v.Kind() {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
// Copyright 2018 Huan Du. All rights reserved.
|
||||
// Licensed under the MIT license that can be found in the LICENSE file.
|
||||
|
||||
package sqlbuilder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type structUserForTest struct {
|
||||
ID int `db:"id" fieldtag:"important"`
|
||||
Name string `fieldtag:"important"`
|
||||
Status int `db:"status" fieldtag:"important"`
|
||||
CreatedAt int `db:"created_at"`
|
||||
}
|
||||
|
||||
var userForTest = NewStruct(new(structUserForTest))
|
||||
|
||||
func TestStructSelect(t *testing.T) {
|
||||
sb := userForTest.Select("user")
|
||||
sql, args := sb.Build()
|
||||
|
||||
if expected := "SELECT id, Name, status, created_at FROM user LIMIT 1"; expected != sql {
|
||||
t.Fatalf("invalid SQL. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
t.Fatalf("args must be empty. [args:%v]", args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructSelectForTag(t *testing.T) {
|
||||
sb := userForTest.SelectForTag("user", "important")
|
||||
sql, args := sb.Build()
|
||||
|
||||
if expected := "SELECT id, Name, status FROM user LIMIT 1"; expected != sql {
|
||||
t.Fatalf("invalid SQL. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
t.Fatalf("args must be empty. [args:%v]", args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructUpdate(t *testing.T) {
|
||||
user := &structUserForTest{
|
||||
ID: 123,
|
||||
Name: "Huan Du",
|
||||
Status: 2,
|
||||
CreatedAt: 1234567890,
|
||||
}
|
||||
ub := userForTest.Update("user", user)
|
||||
sql, args := ub.Build()
|
||||
|
||||
if expected := "UPDATE user SET id = ?, Name = ?, status = ?, created_at = ?"; expected != sql {
|
||||
t.Fatalf("invalid SQL. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
|
||||
if expected := []interface{}{123, "Huan Du", 2, 1234567890}; !reflect.DeepEqual(expected, args) {
|
||||
t.Fatalf("invalid args. [expected:%v] [actual:%v]", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructUpdateForTag(t *testing.T) {
|
||||
user := &structUserForTest{
|
||||
ID: 123,
|
||||
Name: "Huan Du",
|
||||
Status: 2,
|
||||
CreatedAt: 1234567890,
|
||||
}
|
||||
ub := userForTest.UpdateForTag("user", "important", user)
|
||||
sql, args := ub.Build()
|
||||
|
||||
if expected := "UPDATE user SET id = ?, Name = ?, status = ?"; expected != sql {
|
||||
t.Fatalf("invalid SQL. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
|
||||
if expected := []interface{}{123, "Huan Du", 2}; !reflect.DeepEqual(expected, args) {
|
||||
t.Fatalf("invalid args. [expected:%v] [actual:%v]", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructInsertInto(t *testing.T) {
|
||||
user := &structUserForTest{
|
||||
ID: 123,
|
||||
Name: "Huan Du",
|
||||
Status: 2,
|
||||
CreatedAt: 1234567890,
|
||||
}
|
||||
ib := userForTest.InsertInto("user", user)
|
||||
sql, args := ib.Build()
|
||||
|
||||
if expected := "INSERT INTO user (id, Name, status, created_at) VALUES (?, ?, ?, ?)"; expected != sql {
|
||||
t.Fatalf("invalid SQL. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
|
||||
if expected := []interface{}{123, "Huan Du", 2, 1234567890}; !reflect.DeepEqual(expected, args) {
|
||||
t.Fatalf("invalid args. [expected:%v] [actual:%v]", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructInsertIntoForTag(t *testing.T) {
|
||||
user := &structUserForTest{
|
||||
ID: 123,
|
||||
Name: "Huan Du",
|
||||
Status: 2,
|
||||
CreatedAt: 1234567890,
|
||||
}
|
||||
ib := userForTest.InsertIntoForTag("user", "important", user)
|
||||
sql, args := ib.Build()
|
||||
|
||||
if expected := "INSERT INTO user (id, Name, status) VALUES (?, ?, ?)"; expected != sql {
|
||||
t.Fatalf("invalid SQL. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
|
||||
if expected := []interface{}{123, "Huan Du", 2}; !reflect.DeepEqual(expected, args) {
|
||||
t.Fatalf("invalid args. [expected:%v] [actual:%v]", expected, args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructDeleteFrom(t *testing.T) {
|
||||
db := userForTest.DeleteFrom("user")
|
||||
sql, args := db.Build()
|
||||
|
||||
if expected := "DELETE FROM user"; expected != sql {
|
||||
t.Fatalf("invalid SQL. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
t.Fatalf("args must be empty. [args:%v]", args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructAddr(t *testing.T) {
|
||||
user := new(structUserForTest)
|
||||
expected := &structUserForTest{
|
||||
ID: 123,
|
||||
Name: "huandu",
|
||||
Status: 2,
|
||||
CreatedAt: 1234567890,
|
||||
}
|
||||
str := fmt.Sprintf("%v %v %v %v", expected.ID, expected.Name, expected.Status, expected.CreatedAt)
|
||||
fmt.Sscanf(str, "%d%s%d%d", userForTest.Addr(user)...)
|
||||
|
||||
if !reflect.DeepEqual(expected, user) {
|
||||
t.Fatalf("invalid user. [expected:%v] [actual:%v]", expected, user)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructAddrForTag(t *testing.T) {
|
||||
user := new(structUserForTest)
|
||||
expected := &structUserForTest{
|
||||
ID: 123,
|
||||
Name: "huandu",
|
||||
Status: 2,
|
||||
CreatedAt: 1234567890,
|
||||
}
|
||||
user.CreatedAt = 9876543210
|
||||
str := fmt.Sprintf("%v %v %v %v", expected.ID, expected.Name, expected.Status, expected.CreatedAt)
|
||||
fmt.Sscanf(str, "%d%s%d%d", userForTest.AddrForTag("important", user)...)
|
||||
expected.CreatedAt = 9876543210
|
||||
|
||||
if !reflect.DeepEqual(expected, user) {
|
||||
t.Fatalf("invalid user. [expected:%v] [actual:%v]", expected, user)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructAddrWithCols(t *testing.T) {
|
||||
user := new(structUserForTest)
|
||||
expected := &structUserForTest{
|
||||
ID: 123,
|
||||
Name: "huandu",
|
||||
Status: 2,
|
||||
CreatedAt: 1234567890,
|
||||
}
|
||||
str := fmt.Sprintf("%v %v %v %v", expected.Name, expected.ID, expected.CreatedAt, expected.Status)
|
||||
fmt.Sscanf(str, "%s%d%d%d", userForTest.AddrWithCols([]string{"Name", "id", "created_at", "status"}, user)...)
|
||||
|
||||
if !reflect.DeepEqual(expected, user) {
|
||||
t.Fatalf("invalid user. [expected:%v] [actual:%v]", expected, user)
|
||||
}
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID int64 `db:"id"`
|
||||
Name string `db:"name"`
|
||||
Status int `db:"status"`
|
||||
}
|
||||
|
||||
type testDB int
|
||||
type testRows int
|
||||
|
||||
func (db testDB) Query(query string, args ...interface{}) (testRows, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (rows testRows) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rows testRows) Scan(dest ...interface{}) error {
|
||||
fmt.Sscan("1234 huandu 1", dest...)
|
||||
return nil
|
||||
}
|
||||
|
||||
var userStruct = NewStruct(new(User))
|
||||
var db testDB
|
||||
|
||||
func ExampleStruct() {
|
||||
// Suppose we defined following type and global variable.
|
||||
//
|
||||
// type User struct {
|
||||
// ID int64 `db:"id"`
|
||||
// Name string `db:"name"`
|
||||
// Status int `db:"status"`
|
||||
// }
|
||||
//
|
||||
// var userStruct = NewStruct(new(User))
|
||||
|
||||
// Prepare SELECT query.
|
||||
sb := userStruct.Select("user")
|
||||
sb.Where(sb.E("id", 1234))
|
||||
sql, args := sb.Build()
|
||||
fmt.Println(sql)
|
||||
fmt.Println(args)
|
||||
|
||||
// Execute the query.
|
||||
rows, _ := db.Query(sql, args...)
|
||||
defer rows.Close()
|
||||
|
||||
// Scan row data to user.
|
||||
var user User
|
||||
rows.Scan(userStruct.Addr(&user)...)
|
||||
fmt.Printf("%#v", user)
|
||||
|
||||
// Output:
|
||||
// SELECT id, name, status FROM user WHERE id = ? LIMIT 1
|
||||
// [1234]
|
||||
// sqlbuilder.User{ID:1234, Name:"huandu", Status:1}
|
||||
}
|
||||
Reference in New Issue
Block a user