Update README

This commit is contained in:
Jinzhu 2013-11-17 12:02:22 +08:00
parent bf1db1534e
commit 094a546815
4 changed files with 77 additions and 32 deletions

View File

@ -12,41 +12,51 @@ Yet Another ORM library for Go, aims for developer friendly
* Transaction
* Logger Support
* Bind struct with tag
* Iteration Support via sql.Rows
* sql.Scanner
* Every feature comes with tests
* Convention Over Configuration
* Developer Friendly
## Conventions
* Table name is the plural of struct name's snake case.
Disable pluralization with `db.SingularTable(true)`, or [specify your table name](#specify-table-name)
* Column name is the snake case of field's name.
* Use `Id int64` field as primary key.
* Use tag `sql` to change field's property, change the tag name with `db.SetTagIdentifier(new_name)`.
* Use `CreatedAt` to store record's created time if it exist.
* Use `UpdatedAt` to store record's updated time if it exist.
* Use `DeletedAt` to store record's deleted time if it exist. [Soft Delete](#soft-delete)
```go
// TableName: `users`, gorm will pluralize struct's name as table name, you are possible to turn off this feature
type User struct {
Id int64 // Id: Database Primary key
Id int64
Birthday time.Time
Age int64
Name string `sql:"size:255"` // set this field's length in database
CreatedAt time.Time // Time of record is created, will be insert automatically
UpdatedAt time.Time // Time of record is updated, will be updated automatically
DeletedAt time.Time // Time of record is deleted, refer `Soft Delete` for more
Name string `sql:"size:255"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time
Emails []Email // Embedded structs
BillingAddress Address // Embedded struct
BillingAddressId sql.NullInt64 // Embedded struct BillingAddress's foreign key
ShippingAddress Address // Embedded struct
ShippingAddressId int64 // Embedded struct ShippingAddress's foreign key
BillingAddressId sql.NullInt64 // BillingAddress's foreign key
ShippingAddress Address // Another Embedded struct with same type
ShippingAddressId int64 // ShippingAddress's foreign key
IgnoreMe int64 `sql:"-"` // Ignore this field
}
type Email struct { // TableName: `emails`
type Email struct {
Id int64
UserId int64 // Foreign key for above embedded structs
Email string `sql:"type:varchar(100);"` // Set this field's type in database
UserId int64 // Foreign key for User
Email string `sql:"type:varchar(100);"` // Set this field's type
Subscribed bool
}
type Address struct { // TableName: `addresses`
type Address struct {
Id int64
Address1 string `sql:"not null;unique"` // Set this field as not null and unique in database
Address1 string `sql:"not null;unique"` // Set this field as not nullable and unique in database
Address2 string `sql:"type:varchar(100);unique"`
Post sql.NullString `sql:not null`
// FYI, "NOT NULL" will only works well with NullXXX Scanner, because golang will initalize a default value for most type...
@ -735,7 +745,6 @@ db.Where("email = ?", "x@example.org").Attrs(User{RegisteredIp: "111.111.111.111
```
## TODO
* Rows, Row
* Cache Stmt for performance
* Join, Having, Group, Includes
* Scopes, Valiations

35
do.go
View File

@ -341,6 +341,16 @@ func (s *Do) related(value interface{}, foreign_keys ...string) *Do {
return s
}
func (s *Do) row() *sql.Row {
s.prepareQuerySql()
return s.db.db.QueryRow(s.sql, s.sqlVars...)
}
func (s *Do) rows() (*sql.Rows, error) {
s.prepareQuerySql()
return s.db.db.Query(s.sql, s.sqlVars...)
}
func (s *Do) query() *Do {
defer s.trace(time.Now())
var (
@ -400,12 +410,8 @@ func (s *Do) query() *Do {
func (s *Do) count(value interface{}) *Do {
defer s.trace(time.Now())
s.search = s.search.clone().selects("count(*)")
s.prepareQuerySql()
if !s.db.hasError() {
s.err(s.db.db.QueryRow(s.sql, s.sqlVars...).Scan(value))
}
s.err(s.row().Scan(value))
return s
}
@ -419,18 +425,13 @@ func (s *Do) pluck(column string, value interface{}) *Do {
return s
}
s.prepareQuerySql()
if !s.db.hasError() {
rows, err := s.db.db.Query(s.sql, s.sqlVars...)
if s.err(err) == nil {
defer rows.Close()
for rows.Next() {
dest := reflect.New(dest_out.Type().Elem()).Interface()
s.err(rows.Scan(dest))
dest_out.Set(reflect.Append(dest_out, reflect.ValueOf(dest).Elem()))
}
rows, err := s.rows()
if s.err(err) == nil {
defer rows.Close()
for rows.Next() {
dest := reflect.New(dest_out.Type().Elem()).Interface()
s.err(rows.Scan(dest))
dest_out.Set(reflect.Append(dest_out, reflect.ValueOf(dest).Elem()))
}
}
return s

View File

@ -1364,6 +1364,33 @@ func TestQueryChain(t *testing.T) {
}
}
func TestRow(t *testing.T) {
row := db.Table("users").Where("name = ?", "2").Select("age").Row()
var age int64
row.Scan(&age)
if age != 20 {
t.Errorf("Scan with Row")
}
}
func TestRows(t *testing.T) {
rows, err := db.Table("users").Where("name = ?", "3").Select("name, age").Rows()
if err != nil {
t.Errorf("Not error should happen, but got", err)
}
count := 0
for rows.Next() {
var name string
var age int64
rows.Scan(&name, &age)
count++
}
if count != 2 {
t.Errorf("Should found two records with name 3")
}
}
func BenchmarkGorm(b *testing.B) {
b.N = 2000
for x := 0; x < b.N; x++ {

View File

@ -93,6 +93,14 @@ func (s *DB) Find(out interface{}, where ...interface{}) *DB {
return s.clone().do(out).where(where...).query().db
}
func (s *DB) Row() *sql.Row {
return s.do(s.data).row()
}
func (s *DB) Rows() (*sql.Rows, error) {
return s.do(s.data).rows()
}
func (s *DB) Attrs(attrs ...interface{}) *DB {
return s.clone().search.attrs(attrs...).db
}