mirror of
http://github.com/goal-web/database
synced 2025-12-24 10:40:53 +08:00
feat: 初始化数据库仓库
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
tests/.env
|
||||
tests/db.sqlite
|
||||
10
config.go
Normal file
10
config.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Default string
|
||||
Connections map[string]contracts.Fields
|
||||
}
|
||||
103
drivers/base.go
Normal file
103
drivers/base.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"github.com/goal-web/collection"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/database/events"
|
||||
exceptions2 "github.com/goal-web/database/exceptions"
|
||||
"github.com/goal-web/database/table"
|
||||
"github.com/goal-web/database/tx"
|
||||
"github.com/goal-web/supports/exceptions"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
*sqlx.DB
|
||||
events contracts.EventDispatcher
|
||||
}
|
||||
|
||||
func (this *Base) Query(query string, args ...interface{}) (results contracts.Collection, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
this.events.Dispatch(&events.QueryExecuted{Sql: query, Bindings: args})
|
||||
}
|
||||
}()
|
||||
rows, err := this.DB.Query(query, args...)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := table.ParseRows(rows)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return collection.FromFieldsSlice(data), nil
|
||||
}
|
||||
|
||||
func (this *Base) Get(dest interface{}, query string, args ...interface{}) (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
this.events.Dispatch(&events.QueryExecuted{Sql: query, Bindings: args})
|
||||
}
|
||||
}()
|
||||
return this.DB.Get(dest, query, args...)
|
||||
}
|
||||
func (this *Base) Select(dest interface{}, query string, args ...interface{}) (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
this.events.Dispatch(&events.QueryExecuted{Sql: query, Bindings: args})
|
||||
}
|
||||
}()
|
||||
return this.DB.Get(dest, query, args...)
|
||||
}
|
||||
|
||||
func (this *Base) Begin() (contracts.DBTx, error) {
|
||||
sqlxTx, err := this.DB.Beginx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.New(sqlxTx, this.events), err
|
||||
}
|
||||
|
||||
func (this *Base) Transaction(fn func(tx contracts.SqlExecutor) error) (err error) {
|
||||
sqlxTx, err := this.Begin()
|
||||
if err != nil {
|
||||
return exceptions2.BeginException{Exception: exceptions.WithError(err, nil)}
|
||||
}
|
||||
|
||||
defer func() { // 处理 panic 情况
|
||||
if recoverErr := recover(); recoverErr != nil {
|
||||
rollbackErr := sqlxTx.Rollback()
|
||||
err = recoverErr.(error)
|
||||
if rollbackErr != nil {
|
||||
err = exceptions2.RollbackException{Exception: exceptions.WithPrevious(rollbackErr, nil, exceptions.WithError(err, nil))}
|
||||
} else {
|
||||
err = exceptions2.TransactionException{Exception: exceptions.WithError(err, nil)}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err = fn(sqlxTx)
|
||||
|
||||
if err != nil {
|
||||
rollbackErr := sqlxTx.Rollback()
|
||||
if rollbackErr != nil {
|
||||
return exceptions2.RollbackException{Exception: exceptions.WithPrevious(rollbackErr, nil, exceptions.WithError(err, nil))}
|
||||
}
|
||||
return exceptions2.TransactionException{Exception: exceptions.WithError(err, nil)}
|
||||
}
|
||||
|
||||
return sqlxTx.Commit()
|
||||
}
|
||||
|
||||
func (this *Base) Exec(query string, args ...interface{}) (result contracts.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
this.events.Dispatch(&events.QueryExecuted{Sql: query, Bindings: args})
|
||||
}
|
||||
}()
|
||||
return this.DB.Exec(query, args...)
|
||||
}
|
||||
36
drivers/mysql.go
Normal file
36
drivers/mysql.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/logs"
|
||||
"github.com/goal-web/supports/utils"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Mysql struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
func MysqlConnector(config contracts.Fields, events contracts.EventDispatcher) contracts.DBConnection {
|
||||
dsn := utils.GetStringField(config, "unix_socket")
|
||||
if dsn == "" {
|
||||
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s",
|
||||
utils.GetStringField(config, "username"),
|
||||
utils.GetStringField(config, "password"),
|
||||
utils.GetStringField(config, "host"),
|
||||
utils.GetStringField(config, "port"),
|
||||
utils.GetStringField(config, "database"),
|
||||
utils.GetStringField(config, "charset"),
|
||||
)
|
||||
}
|
||||
db, err := sqlx.Connect("mysql", dsn)
|
||||
if err != nil {
|
||||
logs.WithError(err).WithField("config", config).Fatal("mysql 数据库初始化失败")
|
||||
}
|
||||
db.SetMaxOpenConns(utils.GetIntField(config, "max_connections"))
|
||||
db.SetMaxIdleConns(utils.GetIntField(config, "max_idles"))
|
||||
|
||||
return &Mysql{&Base{db, events}}
|
||||
}
|
||||
32
drivers/postgresql.go
Normal file
32
drivers/postgresql.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/logs"
|
||||
"github.com/goal-web/supports/utils"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
type PostgresSQL struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
func PostgresSqlConnector(config contracts.Fields, events contracts.EventDispatcher) contracts.DBConnection {
|
||||
db, err := sqlx.Connect("postgres", fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
|
||||
utils.GetStringField(config, "host"),
|
||||
utils.GetStringField(config, "port"),
|
||||
utils.GetStringField(config, "username"),
|
||||
utils.GetStringField(config, "password"),
|
||||
utils.GetStringField(config, "database"),
|
||||
utils.GetStringField(config, "sslmode"),
|
||||
))
|
||||
if err != nil {
|
||||
logs.WithError(err).WithField("config", config).Fatal("postgreSql 数据库初始化失败")
|
||||
}
|
||||
db.SetMaxOpenConns(utils.GetIntField(config, "max_connections"))
|
||||
db.SetMaxIdleConns(utils.GetIntField(config, "max_idles"))
|
||||
|
||||
return &PostgresSQL{&Base{db, events}}
|
||||
}
|
||||
23
drivers/sqlite.go
Normal file
23
drivers/sqlite.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/logs"
|
||||
"github.com/goal-web/supports/utils"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type Sqlite struct {
|
||||
*Base
|
||||
}
|
||||
|
||||
func SqliteConnector(config contracts.Fields, events contracts.EventDispatcher) contracts.DBConnection {
|
||||
db, err := sqlx.Connect("sqlite3", utils.GetStringField(config, "database"))
|
||||
|
||||
if err != nil {
|
||||
logs.WithError(err).WithField("config", config).Fatal("sqlite 数据库初始化失败")
|
||||
}
|
||||
|
||||
return &Sqlite{&Base{db, events}}
|
||||
}
|
||||
15
events/executed.go
Normal file
15
events/executed.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package events
|
||||
|
||||
type QueryExecuted struct {
|
||||
Sql string
|
||||
Bindings []interface{}
|
||||
Connection string
|
||||
}
|
||||
|
||||
func (this *QueryExecuted) Event() string {
|
||||
return "QUERY_EXECUTED"
|
||||
}
|
||||
|
||||
func (this *QueryExecuted) Sync() bool {
|
||||
return true
|
||||
}
|
||||
24
exception.go
Normal file
24
exception.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
)
|
||||
|
||||
type ConnectionErrorCode int
|
||||
|
||||
const (
|
||||
DbDriverDontExist ConnectionErrorCode = iota
|
||||
DbConnectionDontExist
|
||||
)
|
||||
|
||||
type DBConnectionException struct {
|
||||
error
|
||||
Connection string
|
||||
Code ConnectionErrorCode
|
||||
fields contracts.Fields
|
||||
}
|
||||
|
||||
func (this DBConnectionException) Fields() contracts.Fields {
|
||||
this.fields["Code"] = this.Code
|
||||
return this.fields
|
||||
}
|
||||
15
exceptions/exceptions.go
Normal file
15
exceptions/exceptions.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package exceptions
|
||||
|
||||
import "github.com/goal-web/contracts"
|
||||
|
||||
type TransactionException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
|
||||
type RollbackException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
|
||||
type BeginException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
56
factory.go
Normal file
56
factory.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/utils"
|
||||
)
|
||||
|
||||
type Factory struct {
|
||||
events contracts.EventDispatcher
|
||||
config contracts.Config
|
||||
connections map[string]contracts.DBConnection
|
||||
drivers map[string]contracts.DBConnector
|
||||
dbConfig Config
|
||||
}
|
||||
|
||||
func (this *Factory) Connection(name ...string) contracts.DBConnection {
|
||||
connection := this.dbConfig.Default
|
||||
if len(name) > 0 {
|
||||
connection = name[0]
|
||||
}
|
||||
if connection, existsConnection := this.connections[connection]; existsConnection {
|
||||
return connection
|
||||
}
|
||||
|
||||
this.connections[connection] = this.make(connection)
|
||||
|
||||
return this.connections[connection]
|
||||
}
|
||||
|
||||
func (this *Factory) Extend(name string, driver contracts.DBConnector) {
|
||||
this.drivers[name] = driver
|
||||
}
|
||||
|
||||
func (this *Factory) make(name string) contracts.DBConnection {
|
||||
config := this.config.Get("database").(Config)
|
||||
|
||||
if connectionConfig, existsConnection := config.Connections[name]; existsConnection {
|
||||
driverName := utils.GetStringField(connectionConfig, "driver")
|
||||
if driver, existsDriver := this.drivers[driverName]; existsDriver {
|
||||
return driver(connectionConfig, this.events)
|
||||
}
|
||||
|
||||
panic(DBConnectionException{
|
||||
error: errors.New("该数据库驱动不存在:" + driverName),
|
||||
Code: DbDriverDontExist,
|
||||
fields: connectionConfig,
|
||||
})
|
||||
}
|
||||
|
||||
panic(DBConnectionException{
|
||||
error: errors.New("数据库连接不存在:" + name),
|
||||
Code: DbConnectionDontExist,
|
||||
Connection: name,
|
||||
})
|
||||
}
|
||||
29
go.mod
Normal file
29
go.mod
Normal file
@@ -0,0 +1,29 @@
|
||||
module github.com/goal-web/database
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/goal-web/application v0.1.0
|
||||
github.com/goal-web/collection v0.1.4
|
||||
github.com/goal-web/config v0.1.0
|
||||
github.com/goal-web/contracts v0.1.16
|
||||
github.com/goal-web/events v0.1.5
|
||||
github.com/goal-web/querybuilder v0.1.10
|
||||
github.com/goal-web/supports v0.1.11
|
||||
github.com/jmoiron/sqlx v1.3.4
|
||||
github.com/lib/pq v1.10.4
|
||||
github.com/mattn/go-sqlite3 v1.14.10
|
||||
github.com/stretchr/testify v1.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/apex/log v1.9.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/goal-web/container v0.1.3 // indirect
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/qbhy/parallel v1.3.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c // indirect
|
||||
)
|
||||
114
go.sum
Normal file
114
go.sum
Normal file
@@ -0,0 +1,114 @@
|
||||
github.com/apex/log v1.9.0 h1:FHtw/xuaM8AgmvDDTI9fiwoAL25Sq2cxojnZICUU8l0=
|
||||
github.com/apex/log v1.9.0/go.mod h1:m82fZlWIuiWzWP04XCTXmnX0xRkYYbCdYn8jbJeLBEA=
|
||||
github.com/apex/logs v1.0.0/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
|
||||
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
||||
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
|
||||
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/goal-web/application v0.1.0 h1:4zOgmL1DdW7RSF5u1dsEhItecENCl0Kwt5VuP//0OfI=
|
||||
github.com/goal-web/application v0.1.0/go.mod h1:pHFKbdFw0UsckCsgHKF6agBqdfxT/qNoY1OEWhI5HKM=
|
||||
github.com/goal-web/collection v0.1.4 h1:O4uSYVeoO/CS/ztLp4FsOIBEFXG6oHwbdPU08hnOtVw=
|
||||
github.com/goal-web/collection v0.1.4/go.mod h1:Syaj+9lKGoSAenQTJErMvAG/iBAZlr+Rn4rENR1S5J4=
|
||||
github.com/goal-web/config v0.1.0 h1:mZjlTYEUDOIngauKq6mbJBnJFOVYudH+mtt8VJq1GGY=
|
||||
github.com/goal-web/config v0.1.0/go.mod h1:d2/W9nOfLMLBq1ICpNXZe/4cKMWIQdYzz4wS9qmpxbk=
|
||||
github.com/goal-web/container v0.1.3 h1:Xt1mbfDptgGnXEbQegjC078j5cPNXZMgKXmc9O0t/fE=
|
||||
github.com/goal-web/container v0.1.3/go.mod h1:IpfV+0J1xaKUTl7Pymc02h0wNQtCKWcQqI8ASFd/p/Y=
|
||||
github.com/goal-web/contracts v0.1.1/go.mod h1:lKHynU2Kgk6xyxL4afOJM4TO1kSa3RrCJ2bm5RtFMBw=
|
||||
github.com/goal-web/contracts v0.1.7/go.mod h1:lKHynU2Kgk6xyxL4afOJM4TO1kSa3RrCJ2bm5RtFMBw=
|
||||
github.com/goal-web/contracts v0.1.8/go.mod h1:lKHynU2Kgk6xyxL4afOJM4TO1kSa3RrCJ2bm5RtFMBw=
|
||||
github.com/goal-web/contracts v0.1.13/go.mod h1:lKHynU2Kgk6xyxL4afOJM4TO1kSa3RrCJ2bm5RtFMBw=
|
||||
github.com/goal-web/contracts v0.1.16 h1:sDvlEpzLyTOn6YmFwo64iYR8Giwv4afWLXeeyAkH/fM=
|
||||
github.com/goal-web/contracts v0.1.16/go.mod h1:lKHynU2Kgk6xyxL4afOJM4TO1kSa3RrCJ2bm5RtFMBw=
|
||||
github.com/goal-web/events v0.1.5 h1:vtoXRd0oYBZMdC/Z58TTFKtJXe8QWcgBgUgZOdm9N7A=
|
||||
github.com/goal-web/events v0.1.5/go.mod h1:iS8y/sKknId/0+PoI6OBOL5M9pSLi65lsUgW4OTAqy0=
|
||||
github.com/goal-web/querybuilder v0.1.10 h1:+jJScOMVczLLEYYXJexKjelOE3ukaZc9ulEg/ij4UtY=
|
||||
github.com/goal-web/querybuilder v0.1.10/go.mod h1:2sOjB1+J49xpem9Y8CwZZCNdkRzWh7Mc287W5z80TMQ=
|
||||
github.com/goal-web/supports v0.1.1/go.mod h1:TlQ+7igTZhAN2//GcF9lM6hmi3xh/We/PxDjtFTB4Ow=
|
||||
github.com/goal-web/supports v0.1.2/go.mod h1:TlQ+7igTZhAN2//GcF9lM6hmi3xh/We/PxDjtFTB4Ow=
|
||||
github.com/goal-web/supports v0.1.10/go.mod h1:md5XsCyIjSwYO0qU+ECXd6kCtLm7OeiqgUjoC6vW284=
|
||||
github.com/goal-web/supports v0.1.11 h1:IY9f0X1T/vi2tYWlhrJZiFhev1bH0M4ndhqQlkddA9Y=
|
||||
github.com/goal-web/supports v0.1.11/go.mod h1:md5XsCyIjSwYO0qU+ECXd6kCtLm7OeiqgUjoC6vW284=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w=
|
||||
github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
|
||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
|
||||
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/qbhy/parallel v1.3.0 h1:QxiO29iAGKo5Qt6IntwkP6UJongA7rjMI05IshPLymk=
|
||||
github.com/qbhy/parallel v1.3.0/go.mod h1:gjKS0IACnz3SWXkeEOUvPPZZxpeQAOW47jT4cpWPvqU=
|
||||
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||
github.com/smartystreets/gunit v1.0.0/go.mod h1:qwPWnhz6pn0NnRBP++URONOVyNkPyr4SauJk4cUOwJs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0=
|
||||
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
||||
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
||||
github.com/tj/go-buffer v1.1.0/go.mod h1:iyiJpfFcR2B9sXu7KvjbT9fpM4mOelRSDTbntVj52Uc=
|
||||
github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0=
|
||||
github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPfx6jb1bBgRFjl5lytqVqZXEaeqWP8lTEao=
|
||||
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
||||
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
35
service_provider.go
Normal file
35
service_provider.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/database/drivers"
|
||||
)
|
||||
|
||||
type ServiceProvider struct {
|
||||
}
|
||||
|
||||
func (this *ServiceProvider) Register(application contracts.Application) {
|
||||
application.Singleton("db.factory", func(config contracts.Config, events contracts.EventDispatcher) contracts.DBFactory {
|
||||
return &Factory{
|
||||
events: events,
|
||||
config: config,
|
||||
dbConfig: config.Get("database").(Config),
|
||||
connections: make(map[string]contracts.DBConnection),
|
||||
drivers: map[string]contracts.DBConnector{
|
||||
"mysql": drivers.MysqlConnector,
|
||||
"postgres": drivers.PostgresSqlConnector,
|
||||
"sqlite": drivers.SqliteConnector,
|
||||
},
|
||||
}
|
||||
})
|
||||
application.Singleton("db", func(config contracts.Config, factory contracts.DBFactory) contracts.DBConnection {
|
||||
return factory.Connection()
|
||||
})
|
||||
}
|
||||
|
||||
func (this *ServiceProvider) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *ServiceProvider) Stop() {
|
||||
}
|
||||
76
table/aggregate.go
Normal file
76
table/aggregate.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/exceptions"
|
||||
)
|
||||
|
||||
func (this *Table) Count(columns ...string) int64 {
|
||||
sql, bindings := this.WithCount(columns...).SelectSql()
|
||||
var num int64
|
||||
err := this.getExecutor().Get(&num, sql, bindings...)
|
||||
if err != nil {
|
||||
exceptions.Throw(SelectException{exceptions.WithError(err, contracts.Fields{
|
||||
"columns": columns,
|
||||
"sql": sql,
|
||||
"bindings": bindings,
|
||||
})})
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func (this *Table) Avg(column string, as ...string) int64 {
|
||||
sql, bindings := this.WithAvg(column, as...).SelectSql()
|
||||
var num int64
|
||||
err := this.getExecutor().Get(&num, sql, bindings...)
|
||||
if err != nil {
|
||||
exceptions.Throw(SelectException{exceptions.WithError(err, contracts.Fields{
|
||||
"column": column,
|
||||
"sql": sql,
|
||||
"bindings": bindings,
|
||||
})})
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func (this *Table) Sum(column string, as ...string) int64 {
|
||||
sql, bindings := this.WithSum(column, as...).SelectSql()
|
||||
var num int64
|
||||
err := this.getExecutor().Get(&num, sql, bindings...)
|
||||
if err != nil {
|
||||
exceptions.Throw(SelectException{exceptions.WithError(err, contracts.Fields{
|
||||
"column": column,
|
||||
"sql": sql,
|
||||
"bindings": bindings,
|
||||
})})
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func (this *Table) Max(column string, as ...string) int64 {
|
||||
sql, bindings := this.WithMax(column, as...).SelectSql()
|
||||
var num int64
|
||||
err := this.getExecutor().Get(&num, sql, bindings...)
|
||||
if err != nil {
|
||||
exceptions.Throw(SelectException{exceptions.WithError(err, contracts.Fields{
|
||||
"column": column,
|
||||
"sql": sql,
|
||||
"bindings": bindings,
|
||||
})})
|
||||
}
|
||||
return num
|
||||
}
|
||||
|
||||
func (this *Table) Min(column string, as ...string) int64 {
|
||||
sql, bindings := this.WithMin(column, as...).SelectSql()
|
||||
var num int64
|
||||
err := this.getExecutor().Get(&num, sql, bindings...)
|
||||
if err != nil {
|
||||
exceptions.Throw(SelectException{exceptions.WithError(err, contracts.Fields{
|
||||
"column": column,
|
||||
"sql": sql,
|
||||
"bindings": bindings,
|
||||
})})
|
||||
}
|
||||
return num
|
||||
}
|
||||
20
table/chunk.go
Normal file
20
table/chunk.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package table
|
||||
|
||||
import "github.com/goal-web/contracts"
|
||||
|
||||
func (this *Table) Chunk(size int, handler func(collection contracts.Collection, page int) error) (err error) {
|
||||
page := 1
|
||||
for err == nil {
|
||||
newCollection := this.SimplePaginate(int64(size), int64(page))
|
||||
err = handler(newCollection, page)
|
||||
page++
|
||||
if newCollection.Len() < size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Table) ChunkById(size int, handler func(collection contracts.Collection, page int) error) error {
|
||||
return this.OrderBy("id").Chunk(size, handler)
|
||||
}
|
||||
27
table/exceptions.go
Normal file
27
table/exceptions.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package table
|
||||
|
||||
import "github.com/goal-web/contracts"
|
||||
|
||||
type CreateException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
|
||||
type InsertException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
|
||||
type UpdateException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
|
||||
type DeleteException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
|
||||
type SelectException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
|
||||
type NotFoundException struct {
|
||||
contracts.Exception
|
||||
}
|
||||
140
table/insert.go
Normal file
140
table/insert.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/exceptions"
|
||||
"github.com/goal-web/supports/utils"
|
||||
)
|
||||
|
||||
func (this *Table) Create(fields contracts.Fields) interface{} {
|
||||
sql, bindings := this.CreateSql(fields)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
if err != nil {
|
||||
panic(CreateException{exceptions.WithError(err, fields)})
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
panic(CreateException{exceptions.WithError(err, fields)})
|
||||
}
|
||||
|
||||
if _, existsPrimaryKey := fields[this.primaryKey]; !existsPrimaryKey {
|
||||
fields[this.primaryKey] = id
|
||||
}
|
||||
|
||||
if this.class != nil {
|
||||
return this.class.New(fields)
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (this *Table) Insert(values ...contracts.Fields) bool {
|
||||
sql, bindings := this.InsertSql(values)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
return rowsAffected > 0
|
||||
}
|
||||
|
||||
func (this *Table) InsertGetId(values ...contracts.Fields) int64 {
|
||||
sql, bindings := this.InsertSql(values)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
id, err := result.LastInsertId()
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (this *Table) InsertOrIgnore(values ...contracts.Fields) int64 {
|
||||
sql, bindings := this.InsertIgnoreSql(values)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
return rowsAffected
|
||||
}
|
||||
|
||||
func (this *Table) InsertOrReplace(values ...contracts.Fields) int64 {
|
||||
sql, bindings := this.InsertReplaceSql(values)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
|
||||
if err != nil {
|
||||
panic(InsertException{exceptions.WithError(err, contracts.Fields{
|
||||
"values": values,
|
||||
"sql": sql,
|
||||
})})
|
||||
}
|
||||
|
||||
return rowsAffected
|
||||
}
|
||||
|
||||
func (this *Table) FirstOrCreate(values ...contracts.Fields) interface{} {
|
||||
var attributes contracts.Fields
|
||||
argsLen := len(values)
|
||||
if argsLen > 0 {
|
||||
for field, value := range values[0] {
|
||||
attributes[field] = value
|
||||
this.Where(field, value)
|
||||
}
|
||||
if result := this.First(); result != nil {
|
||||
return result
|
||||
} else if argsLen > 1 {
|
||||
utils.MergeFields(attributes, values[1])
|
||||
}
|
||||
return this.Create(attributes)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
31
table/model.go
Normal file
31
table/model.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
)
|
||||
|
||||
type model struct {
|
||||
class contracts.Class
|
||||
table string
|
||||
connection string
|
||||
}
|
||||
|
||||
func Model(class contracts.Class, table string, connection ...string) *Table {
|
||||
conn := ""
|
||||
if len(connection) > 0 {
|
||||
conn = connection[0]
|
||||
}
|
||||
return FromModel(model{class: class, table: table, connection: conn})
|
||||
}
|
||||
|
||||
func (model model) GetClass() contracts.Class {
|
||||
return model.class
|
||||
}
|
||||
|
||||
func (model model) GetTable() string {
|
||||
return model.table
|
||||
}
|
||||
|
||||
func (model model) GetConnection() string {
|
||||
return model.connection
|
||||
}
|
||||
52
table/select.go
Normal file
52
table/select.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/exceptions"
|
||||
)
|
||||
|
||||
func (this *Table) Get() contracts.Collection {
|
||||
sql, bindings := this.SelectSql()
|
||||
|
||||
rows, err := this.getExecutor().Query(sql, bindings...)
|
||||
if err != nil {
|
||||
panic(SelectException{exceptions.WithError(err, contracts.Fields{"sql": sql, "bindings": bindings})})
|
||||
}
|
||||
|
||||
// 返回 Collection<fields>
|
||||
if this.class == nil {
|
||||
return rows
|
||||
}
|
||||
// 返回指定的 Collection<Class>
|
||||
//results := reflect.MakeSlice(reflect.SliceOf(this.class.Type), 0, 0)
|
||||
//fmt.Println(results, utils.GetTypeKey(reflect.TypeOf(results)))
|
||||
//err := this.getExecutor().Select(&results, sql, bindings...)
|
||||
|
||||
//if err != nil {
|
||||
// panic(SelectException{exceptions.WithError(err, contracts.Fields{
|
||||
// "sql": sql,
|
||||
// "bindings": bindings,
|
||||
// "model": this.class.ClassName(),
|
||||
// })})
|
||||
//}
|
||||
|
||||
return rows.Map(func(fields contracts.Fields) interface{} {
|
||||
return this.class.New(fields)
|
||||
})
|
||||
}
|
||||
|
||||
func (this *Table) Find(key interface{}) interface{} {
|
||||
return this.Where(this.primaryKey, key).First()
|
||||
}
|
||||
|
||||
func (this *Table) First() interface{} {
|
||||
return this.Take(1).Get().First()
|
||||
}
|
||||
|
||||
func (this *Table) FirstOrFail() interface{} {
|
||||
if result := this.First(); result != nil {
|
||||
return result
|
||||
}
|
||||
panic(NotFoundException{exceptions.WithError(errors.New("未找到"), contracts.Fields{})})
|
||||
}
|
||||
96
table/table.go
Normal file
96
table/table.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/goal-web/application"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/querybuilder"
|
||||
"github.com/goal-web/supports/exceptions"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
contracts.QueryBuilder
|
||||
executor contracts.SqlExecutor
|
||||
|
||||
table string
|
||||
primaryKey string
|
||||
class contracts.Class
|
||||
}
|
||||
|
||||
func getTable(name string) *Table {
|
||||
builder := querybuilder.NewQuery(name)
|
||||
instance := &Table{
|
||||
QueryBuilder: builder,
|
||||
primaryKey: "id",
|
||||
table: name,
|
||||
}
|
||||
builder.Bind(instance)
|
||||
return instance
|
||||
}
|
||||
|
||||
// Query 将使用默认 connection
|
||||
func Query(name string) *Table {
|
||||
return getTable(name).SetConnection(application.Get("db").(contracts.DBConnection))
|
||||
}
|
||||
|
||||
func FromModel(model contracts.Model) *Table {
|
||||
return WithConnection(model.GetTable(), model.GetConnection()).SetClass(model.GetClass())
|
||||
}
|
||||
|
||||
// WithConnection 使用指定链接
|
||||
func WithConnection(name string, connection interface{}) *Table {
|
||||
if connection == "" || connection == nil {
|
||||
return getTable(name)
|
||||
}
|
||||
return getTable(name).SetConnection(connection)
|
||||
}
|
||||
|
||||
// WithTX 使用TX
|
||||
func WithTX(name string, tx contracts.DBTx) contracts.QueryBuilder {
|
||||
return getTable(name).SetExecutor(tx)
|
||||
}
|
||||
|
||||
// SetConnection 参数要么是 contracts.DBConnection 要么是 string
|
||||
func (this *Table) SetConnection(connection interface{}) *Table {
|
||||
if conn, ok := connection.(contracts.DBConnection); ok {
|
||||
this.executor = conn
|
||||
} else {
|
||||
this.executor = application.Get("db.factory").(contracts.DBFactory).Connection(connection.(string))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
// SetClass 设置类
|
||||
func (this *Table) SetClass(class contracts.Class) *Table {
|
||||
this.class = class
|
||||
return this
|
||||
}
|
||||
|
||||
// SetPrimaryKey 设置主键
|
||||
func (this *Table) SetPrimaryKey(name string) *Table {
|
||||
this.primaryKey = name
|
||||
return this
|
||||
}
|
||||
|
||||
// getExecutor 获取 sql 语句的执行者
|
||||
func (this *Table) getExecutor() contracts.SqlExecutor {
|
||||
return this.executor
|
||||
}
|
||||
|
||||
// SetExecutor 参数必须是 contracts.DBTx 实例
|
||||
func (this *Table) SetExecutor(executor contracts.SqlExecutor) contracts.QueryBuilder {
|
||||
this.executor = executor
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *Table) Delete() int64 {
|
||||
sql, bindings := this.DeleteSql()
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
if err != nil {
|
||||
panic(DeleteException{exceptions.WithError(err, contracts.Fields{"sql": sql, "bindings": bindings})})
|
||||
}
|
||||
num, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
panic(DeleteException{exceptions.WithError(err, contracts.Fields{"sql": sql, "bindings": bindings})})
|
||||
}
|
||||
return num
|
||||
}
|
||||
60
table/update.go
Normal file
60
table/update.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/exceptions"
|
||||
"github.com/goal-web/supports/utils"
|
||||
)
|
||||
|
||||
func (this *Table) UpdateOrInsert(attributes contracts.Fields, values ...contracts.Fields) bool {
|
||||
this.WhereFields(attributes)
|
||||
sql, bindings := this.UpdateSql(attributes)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
if err != nil {
|
||||
panic(UpdateException{exceptions.WithError(err, contracts.Fields{
|
||||
"attributes": attributes,
|
||||
"values": values,
|
||||
})})
|
||||
}
|
||||
num, _ := result.RowsAffected()
|
||||
if num > 0 {
|
||||
return true
|
||||
}
|
||||
if len(values) > 0 {
|
||||
utils.MergeFields(attributes, values[0])
|
||||
}
|
||||
return this.Insert(attributes)
|
||||
}
|
||||
|
||||
func (this *Table) UpdateOrCreate(attributes contracts.Fields, values ...contracts.Fields) interface{} {
|
||||
this.WhereFields(attributes)
|
||||
sql, bindings := this.UpdateSql(attributes)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
if err != nil {
|
||||
panic(UpdateException{exceptions.WithError(err, contracts.Fields{
|
||||
"attributes": attributes,
|
||||
"values": values,
|
||||
})})
|
||||
}
|
||||
num, _ := result.RowsAffected()
|
||||
if num > 0 {
|
||||
return true
|
||||
}
|
||||
if len(values) > 0 {
|
||||
utils.MergeFields(attributes, values[0])
|
||||
}
|
||||
return this.Insert(attributes)
|
||||
}
|
||||
|
||||
func (this *Table) Update(fields contracts.Fields) int64 {
|
||||
sql, bindings := this.UpdateSql(fields)
|
||||
result, err := this.getExecutor().Exec(sql, bindings...)
|
||||
if err != nil {
|
||||
panic(UpdateException{exceptions.WithError(err, fields)})
|
||||
}
|
||||
num, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
panic(UpdateException{exceptions.WithError(err, fields)})
|
||||
}
|
||||
return num
|
||||
}
|
||||
31
table/utils.go
Normal file
31
table/utils.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/goal-web/contracts"
|
||||
)
|
||||
|
||||
func ParseRows(rows *sql.Rows) ([]contracts.Fields, error) {
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columnLength := len(columns)
|
||||
cache := make([]interface{}, columnLength)
|
||||
for index, _ := range cache {
|
||||
var a interface{}
|
||||
cache[index] = &a
|
||||
}
|
||||
var results []contracts.Fields
|
||||
for rows.Next() {
|
||||
_ = rows.Scan(cache...)
|
||||
|
||||
item := make(map[string]interface{})
|
||||
for i, data := range cache {
|
||||
item[columns[i]] = *data.(*interface{})
|
||||
}
|
||||
results = append(results, item)
|
||||
}
|
||||
_ = rows.Close()
|
||||
return results, nil
|
||||
}
|
||||
85
tests/bootstrap.go
Normal file
85
tests/bootstrap.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goal-web/application"
|
||||
"github.com/goal-web/config"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/database"
|
||||
"github.com/goal-web/events"
|
||||
"github.com/goal-web/supports/utils"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func getApp(path string) contracts.Application {
|
||||
env := "testing"
|
||||
app := application.Singleton()
|
||||
app.Instance("path", path)
|
||||
|
||||
// 设置异常处理器
|
||||
app.Singleton("exceptions.handler", func() contracts.ExceptionHandler {
|
||||
return DefaultExceptionHandler{}
|
||||
})
|
||||
|
||||
app.RegisterServices(
|
||||
&config.ServiceProvider{
|
||||
Env: env,
|
||||
Paths: []string{path},
|
||||
Sep: "=",
|
||||
ConfigProviders: map[string]config.Provider{
|
||||
"database": func(env contracts.Env) interface{} {
|
||||
return database.Config{
|
||||
Default: utils.StringOr(env.GetString("db.connection"), "mysql"),
|
||||
Connections: map[string]contracts.Fields{
|
||||
"sqlite": {
|
||||
"driver": "sqlite",
|
||||
"database": env.GetString("sqlite.database"),
|
||||
},
|
||||
"mysql": {
|
||||
"driver": "mysql",
|
||||
"host": env.GetString("db.host"),
|
||||
"port": env.GetString("db.port"),
|
||||
"database": env.GetString("db.database"),
|
||||
"username": env.GetString("db.username"),
|
||||
"password": env.GetString("db.password"),
|
||||
"unix_socket": env.GetString("db.unix_socket"),
|
||||
"charset": utils.StringOr(env.GetString("db.charset"), "utf8mb4"),
|
||||
"collation": utils.StringOr(env.GetString("db.collation"), "utf8mb4_unicode_ci"),
|
||||
"prefix": env.GetString("db.prefix"),
|
||||
"strict": env.GetBool("db.struct"),
|
||||
"max_connections": env.GetInt("db.max_connections"),
|
||||
"max_idles": env.GetInt("db.max_idles"),
|
||||
},
|
||||
"pgsql": {
|
||||
"driver": "postgres",
|
||||
"host": env.GetString("db.pgsql.host"),
|
||||
"port": env.GetString("db.pgsql.port"),
|
||||
"database": env.GetString("db.pgsql.database"),
|
||||
"username": env.GetString("db.pgsql.username"),
|
||||
"password": env.GetString("db.pgsql.password"),
|
||||
"charset": utils.StringOr(env.GetString("db.pgsql.charset"), "utf8mb4"),
|
||||
"prefix": env.GetString("db.pgsql.prefix"),
|
||||
"schema": utils.StringOr(env.GetString("db.pgsql.schema"), "public"),
|
||||
"sslmode": utils.StringOr(env.GetString("db.pgsql.sslmode"), "disable"),
|
||||
"max_connections": env.GetInt("db.pgsql.max_connections"),
|
||||
"max_idles": env.GetInt("db.pgsql.max_idles"),
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
events.ServiceProvider{},
|
||||
&database.ServiceProvider{},
|
||||
)
|
||||
|
||||
pidPath := path + "/goal.pid"
|
||||
// 写入 pid 文件
|
||||
_ = ioutil.WriteFile(pidPath, []byte(fmt.Sprintf("%d", os.Getpid())), os.ModePerm)
|
||||
|
||||
go func() {
|
||||
_ = app.Start()
|
||||
}()
|
||||
return app
|
||||
}
|
||||
113
tests/database_test.go
Normal file
113
tests/database_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/database/table"
|
||||
"github.com/goal-web/supports/class"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func getQuery(name string) contracts.QueryBuilder {
|
||||
getApp("/Users/qbhy/project/go/goal-web/goal/database/tests")
|
||||
return table.WithConnection(name, "sqlite")
|
||||
}
|
||||
func userModel() contracts.QueryBuilder {
|
||||
getApp("/Users/qbhy/project/go/goal-web/goal/database/tests")
|
||||
return UserModel().SetConnection("sqlite")
|
||||
}
|
||||
|
||||
func TestTableCreate(t *testing.T) {
|
||||
user := getQuery("users").Create(contracts.Fields{
|
||||
"name": "qbhy",
|
||||
}).(contracts.Fields)
|
||||
|
||||
fmt.Println(user)
|
||||
assert.True(t, user["id"].(int64) > 0)
|
||||
}
|
||||
|
||||
func TestTableSelect(t *testing.T) {
|
||||
|
||||
users := getQuery("users").Get()
|
||||
|
||||
fmt.Println(users)
|
||||
|
||||
users.Map(func(user contracts.Fields) {
|
||||
fmt.Println(user, user["id"])
|
||||
})
|
||||
|
||||
assert.True(t, users != nil)
|
||||
}
|
||||
|
||||
func TestTableQuery(t *testing.T) {
|
||||
|
||||
getQuery("users").Delete()
|
||||
|
||||
user := getQuery("users").Create(contracts.Fields{
|
||||
"name": "qbhy",
|
||||
}).(contracts.Fields)
|
||||
|
||||
fmt.Println(user)
|
||||
userId := user["id"].(int64)
|
||||
// 判断插入是否成功
|
||||
assert.True(t, userId > 0)
|
||||
|
||||
// 获取数据总量
|
||||
assert.True(t, getQuery("users").Count() == 1)
|
||||
|
||||
// 修改数据
|
||||
num := getQuery("users").Where("name", "qbhy").Update(contracts.Fields{
|
||||
"name": "goal",
|
||||
})
|
||||
assert.True(t, num == 1)
|
||||
// 判断修改后的数据
|
||||
user = getQuery("users").Where("name", "goal").First().(contracts.Fields)
|
||||
|
||||
err := getQuery("users").Chunk(10, func(collection contracts.Collection, page int) error {
|
||||
assert.True(t, collection.Len() == 1)
|
||||
fmt.Println(collection.ToJson())
|
||||
return nil
|
||||
})
|
||||
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.True(t, user["id"] == userId)
|
||||
assert.True(t, user["name"] == "goal")
|
||||
assert.True(t, getQuery("users").Find(userId).(contracts.Fields)["id"] == userId)
|
||||
assert.True(t, getQuery("users").Where("id", userId).Delete() == 1)
|
||||
assert.Nil(t, getQuery("users").Find(userId))
|
||||
}
|
||||
|
||||
// 定义 class
|
||||
var UserClass = class.Make(new(User1))
|
||||
|
||||
// 定义结构体
|
||||
type User1 struct {
|
||||
Id int64 `json:"id"`
|
||||
NickName string `json:"name"`
|
||||
}
|
||||
|
||||
// 定义模型
|
||||
func UserModel() *table.Table {
|
||||
return table.Model(UserClass, "users")
|
||||
}
|
||||
|
||||
func TestModel(t *testing.T) {
|
||||
user := userModel().Create(contracts.Fields{
|
||||
"name": "qbhy",
|
||||
}).(User1)
|
||||
|
||||
fmt.Println("创建后返回模型", user)
|
||||
|
||||
fmt.Println("用table查询:",
|
||||
getQuery("users").Get().ToJson()) // query 返回 Collection<contracts.Fields>
|
||||
|
||||
fmt.Println(userModel(). // model 返回 Collection<User1>
|
||||
Get().
|
||||
Map(func(user User1) {
|
||||
fmt.Println("id:", user.Id)
|
||||
}).ToJson())
|
||||
|
||||
fmt.Println(userModel().Where("id", ">", 0).Delete())
|
||||
}
|
||||
29
tests/exception_hanlder.go
Normal file
29
tests/exception_hanlder.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/supports/logs"
|
||||
"github.com/goal-web/supports/utils"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type DefaultExceptionHandler struct {
|
||||
dontReportExceptions []reflect.Type
|
||||
}
|
||||
|
||||
func NewDefaultHandler(dontReportExceptions []contracts.Exception) DefaultExceptionHandler {
|
||||
return DefaultExceptionHandler{utils.ConvertToTypes(dontReportExceptions)}
|
||||
}
|
||||
|
||||
func (handler DefaultExceptionHandler) Handle(exception contracts.Exception) {
|
||||
logs.WithException(exception).
|
||||
WithField("exception", reflect.TypeOf(exception).String()).
|
||||
Error("DefaultExceptionHandler")
|
||||
}
|
||||
|
||||
func (handler DefaultExceptionHandler) Report(exception contracts.Exception) {
|
||||
}
|
||||
|
||||
func (handler DefaultExceptionHandler) ShouldReport(exception contracts.Exception) bool {
|
||||
return !utils.IsInstanceIn(exception, handler.dontReportExceptions...)
|
||||
}
|
||||
1
tests/goal.pid
Executable file
1
tests/goal.pid
Executable file
@@ -0,0 +1 @@
|
||||
42939
|
||||
43
tx/tx.go
Normal file
43
tx/tx.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
"github.com/goal-web/collection"
|
||||
"github.com/goal-web/contracts"
|
||||
"github.com/goal-web/database/events"
|
||||
"github.com/goal-web/database/table"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type Tx struct {
|
||||
*sqlx.Tx
|
||||
events contracts.EventDispatcher
|
||||
}
|
||||
|
||||
func New(tx *sqlx.Tx, events contracts.EventDispatcher) *Tx {
|
||||
return &Tx{
|
||||
Tx: tx,
|
||||
events: events,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Tx) Query(query string, args ...interface{}) (results contracts.Collection, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
this.events.Dispatch(&events.QueryExecuted{Sql: query, Bindings: args})
|
||||
}
|
||||
}()
|
||||
rows, err := this.Tx.Query(query, args...)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := table.ParseRows(rows)
|
||||
results = collection.FromFieldsSlice(data)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *Tx) Exec(query string, args ...interface{}) (contracts.Result, error) {
|
||||
return this.Tx.Exec(query, args...)
|
||||
}
|
||||
Reference in New Issue
Block a user