Compare commits

...

112 Commits

Author SHA1 Message Date
671157cb70 release updates 2019-12-05 12:34:33 +08:00
f775479c3f fix issue in ghttp.Request.GetRequest* 2019-12-05 10:44:31 +08:00
64bb72842e comment update for unit testing case of gdb 2019-12-04 22:38:58 +08:00
e1d4ba9d23 add GetError function for ghttp.Request 2019-12-04 19:50:49 +08:00
a1edd83add README/DONATOR update 2019-12-04 18:48:13 +08:00
f7d6883405 fix issue in gredis for int set/get 2019-12-04 18:43:16 +08:00
8448a70646 change minimum go version from 1.10. to 1.11; travis ci updates 2019-12-04 17:50:03 +08:00
4018bfa899 travis updates 2019-12-04 17:36:31 +08:00
3cc9ce74e1 add IsEmpty function for gdb.Record/Result 2019-12-04 17:01:30 +08:00
eac60d845f go.mod updates 2019-12-04 16:27:19 +08:00
835c045c92 fix unit testing case for gdb 2019-12-04 16:21:35 +08:00
be15f85eae remove sql.ErrNoRows error for select functions for gdb; improve configuration feature for gdb/glog/gview 2019-12-04 16:04:52 +08:00
3e27ea0259 change duration parameter type from interface{} to time.Duration for gcache/gfcache 2019-12-04 14:42:09 +08:00
6abe660287 export router map api for ghttp.Server; comment update for ghttp.Server 2019-12-04 13:55:08 +08:00
a06ca31530 improve middleware feature for ghttp.Server 2019-12-04 10:03:03 +08:00
890865251b improve routerMap handling for ghttp.Request 2019-12-03 17:16:52 +08:00
6af90cafee rename ghttp.Domain.AddBindMiddleware to BindMiddlewareDefault for ghttp 2019-12-02 23:05:05 +08:00
2e2363bb41 change routergroup parameter name 'g' to 'group' for unit testing cases of ghttp 2019-12-02 23:00:48 +08:00
108ced2b0b improve route priority handling for ghttp.Server 2019-12-02 21:26:12 +08:00
fcba650348 add more unit testing case for gview 2019-12-02 20:27:21 +08:00
1b8a082942 change view configuration name from 'view' to 'viewer' in gins 2019-12-02 20:08:45 +08:00
40570cdb03 change logger configuration name from 'logging' to 'logger' in gins 2019-12-02 19:55:45 +08:00
10451864e6 improve parameter handling for ghttp.Requset 2019-12-01 21:41:29 +08:00
55e2646367 comment update for ghttp.Server 2019-12-01 21:05:46 +08:00
c88839edb1 comment update for ghttp.ServerConfig 2019-12-01 14:24:56 +08:00
2a2cfc289c improve ghttp.Client 2019-12-01 14:07:36 +08:00
8bbeb186c2 add Exit feature for ghttp.Response 2019-11-30 20:41:53 +08:00
be4bf39719 comment update for container 2019-11-30 18:33:51 +08:00
459c69839a comment update for container 2019-11-30 18:28:11 +08:00
a5407e57d9 comment update for ghttp.Client 2019-11-30 10:24:19 +08:00
5c749e7762 improve parameters handling for ghttp.Request 2019-11-30 09:42:07 +08:00
b3fafc64f8 improve parameter handling for ghttp.Request 2019-11-29 22:02:19 +08:00
c7b0763ab0 comment and unit testing case update for gdb 2019-11-28 23:51:05 +08:00
0cfdf60de5 change model default unsafe for gdb 2019-11-28 23:23:11 +08:00
096bff791d change model default safe for gdb 2019-11-28 23:21:27 +08:00
0cce858641 improve configuration feature for ghttp.Server/gview/glog 2019-11-28 23:19:37 +08:00
bb45d8d578 improve template cache feature for gview 2019-11-28 14:29:58 +08:00
7e43aa6b9d improve parameter parsing for ghtto.Request; add configuration feature for gview 2019-11-28 11:18:09 +08:00
4ee7c82bf1 fix issue in empty slice converting panic for gconv.Struct 2019-11-28 08:46:38 +08:00
85caa40a3d fix issue in empty slice converting panic for gconv.Struct 2019-11-27 23:38:33 +08:00
f4654bf446 fix comment issue in example codes for gmap 2019-11-27 23:19:07 +08:00
94ed0bf9c9 fix issue in ghttp.Request.GetView 2019-11-26 15:12:58 +08:00
22e3705d3e improve ghttp.GracefulServer; add more unit test case for ghttp.Server.Middleware 2019-11-26 14:56:05 +08:00
9b1cc6e9c7 add special handle for configuration of mssql for gdb 2019-11-22 19:45:42 +08:00
9429c8ff83 improve gd.Model.Clone 2019-11-21 22:21:57 +08:00
056c6d4688 remove Array type case from internal/empty.IsNil checks 2019-11-21 21:57:50 +08:00
382356bc8d improve gerror for stack information; fix issue in gconv.String for nil *time.Time type 2019-11-21 21:49:00 +08:00
1deb3510f0 improve gerror, ghttp for error stack logging; improve custom view feature for ghttp.Request 2019-11-20 18:45:09 +08:00
dd7ae1b07a unit test case updates for gdb 2019-11-20 13:36:31 +08:00
04a8755162 travis updates 2019-11-20 12:34:03 +08:00
682f99a763 travis updates 2019-11-20 12:29:34 +08:00
61282d6dab travis updates 2019-11-20 12:27:23 +08:00
ad540f7c25 update travis for adding mysql password; add custom view feature for ghttp.Response 2019-11-20 12:09:26 +08:00
a4f191c1c6 fix issue in gdb.Model for repeated condition statements; remove concurrent safety feature of gview; add default template file feature for gview 2019-11-19 21:50:17 +08:00
43531c2680 improve glog for debug configuration; add GetInt*/GetUint* functions for ghttp.Request 2019-11-19 17:26:06 +08:00
635f5d36fd improve log feature of ghttp.Server; remove trace for Notice/Warning functions for glog 2019-11-15 22:50:31 +08:00
1becc4932c add more functions for gtcp; remove default domain set for cookie feature of ghttp.Server 2019-11-14 15:41:28 +08:00
e36dd06f22 Merge pull request #342 from myvfpx/master 2019-11-08 23:45:48 +08:00
8f2c62d444 improve logging feature for ghttp.Server 2019-11-08 19:52:49 +08:00
21efde1a38 fix issue in session feature of ghttp.Server 2019-11-08 09:06:01 +08:00
d8b6466ed0 fix issue in ghttp.Server 2019-11-07 21:02:24 +08:00
d7b0228e9e add build-in function 'replace' for gview 2019-11-07 20:56:17 +08:00
9da1277b47 improve session of file storage for ghttp.Server 2019-11-07 20:42:13 +08:00
a3fd0c9a4a improve session of file storage for ghttp.Server 2019-11-07 20:36:33 +08:00
ad7375b44b improve configuration feature for ghttp.Server 2019-11-07 16:40:34 +08:00
05120b585d improve configuration feature for glog/ghttp.Server 2019-11-07 11:32:25 +08:00
9e32d74c8c improve ConfigFromMap function for ghttp.Server 2019-11-06 20:23:41 +08:00
0e62510c6f improve configuration feature, add instance feature for package glog; add package guuid for UUID feature 2019-11-06 20:22:20 +08:00
c492de4fa8 improve multipart form parsing for ghttp.Server 2019-11-06 15:37:29 +08:00
6308380541 update example for uploading files feature for ghttp.Server 2019-11-06 10:39:59 +08:00
2609db1aec improve file uploading feature for ghttp.Server 2019-11-05 19:47:35 +08:00
6c54e73dbd add example for memory storage og gsession 2019-11-05 17:33:06 +08:00
21abe62633 improve gfsnotify; add cache for tempalte file content 2019-11-04 22:42:49 +08:00
e30b2b0732 improve gsession/gmap/gtree 2019-11-04 21:26:16 +08:00
dcb74ee9df comment update 2019-11-01 20:36:09 +08:00
82bf21e831 improve gfsnotify 2019-11-01 15:31:26 +08:00
66355354fc improve gfsnotify/gview and container 2019-10-31 23:37:33 +08:00
4204125dce improve gipv4.IsIntranet 2019-10-31 15:13:34 +08:00
e6aa9d3a46 add EncodeFile/EncodeFileToString functions for gbase64 2019-10-31 14:42:01 +08:00
9416cd1274 add CopyMap function for gutil; improve ghttp.Request 2019-10-30 23:26:57 +08:00
8b5ab846b2 improve unit test case for gmutex 2019-10-30 23:01:24 +08:00
a15b93be90 comment update for package gtcp/gipv4/gipv6 2019-10-30 19:55:50 +08:00
38ee5f7d53 improve performance of gdebug 2019-10-30 15:12:19 +08:00
6f9bbbf416 add debug info for ghttp.Server 2019-10-29 20:15:13 +08:00
143bc3d8e3 travis updates 2019-10-29 20:02:28 +08:00
ea75b1a936 travis updates 2019-10-29 20:00:46 +08:00
0dde8c735e add debug info for ghttp.Server 2019-10-29 19:59:24 +08:00
1ab7f00b91 add FieldsEx function for gdb.Model 2019-10-29 19:45:21 +08:00
3ef42bfbf0 improve internal/intlog 2019-10-29 17:15:02 +08:00
05fec23457 improve internal/cmdenv 2019-10-29 17:13:29 +08:00
7225e49aa0 add package intlog for GF internal logging usage; improve redis storage feature for gsession 2019-10-29 16:45:42 +08:00
14e9deb254 add more unit test cases for gsession 2019-10-29 10:01:05 +08:00
f9569b387f add ConnectTimeout configuration for gredis 2019-10-29 09:49:29 +08:00
a0722ed51f add redis session support for gsession 2019-10-28 20:48:52 +08:00
e9ace9b17a update comment for grpool/glog 2019-10-28 17:16:50 +08:00
4791d10761 fix issue in hijack error for websocket of ghttp.Server 2019-10-28 17:01:08 +08:00
c686b1c080 improve datetime coverting for gdb 2019-10-28 16:42:30 +08:00
056d6ebbd9 improve interface design for gsession.Storage 2019-10-28 16:07:01 +08:00
baa2cb68de donator updates 2019-10-26 11:38:18 +08:00
e4909b318b Merge branch 'develop' of https://github.com/gogf/gf into develop 2019-10-26 11:03:17 +08:00
f1b7cb37c6 add configuration feature for glog 2019-10-26 10:58:07 +08:00
734728fa9c improve time handling for gdb 2019-10-25 19:54:02 +08:00
b373ace065 update comment for gqueue.Len 2019-10-25 17:32:03 +08:00
bc7b5c8626 fix issue in status checks for static file serving 2019-10-25 17:04:17 +08:00
f2b45622d6 add debug for domain group testing 2019-10-25 16:57:54 +08:00
77cb219057 enable route map printing for domain group testing 2019-10-25 16:44:54 +08:00
cf34d7bd56 fix issue in gconv.Interfaces; improve storage interface for gsession 2019-10-25 13:49:03 +08:00
5adc9be0d9 gofmt 2019-10-25 10:43:59 +08:00
63cd1128a7 Merge pull request #346 from danvinhe/feature-optimize_grand.Str 2019-10-23 22:51:58 +08:00
cce8ac5118 The 'letters' already contain numbers, so just grab characters randomly from this. 2019-09-26 11:00:59 +08:00
419b58452f 修改身份证尾数验证规则,新增银行卡号验证规则 2019-09-24 10:38:20 +08:00
b1835ea4e8 添加身份证号尾数验证规则,添加银行卡号验证规则 2019-09-24 10:21:37 +08:00
276 changed files with 7499 additions and 4267 deletions

View File

@ -9,7 +9,7 @@ import (
func main() {
// 创建一个默认的gmap对象
// 默认情况下该gmap对象不支持并发安全特性
// 初始化时可以给定true参数关闭并发安全特性,当做一个普通的map使用
// 初始化时可以给定true参数开启并发安全特性,用以并发安全场景
m := gmap.New()
// 设置键值对

View File

@ -6,7 +6,7 @@ import (
)
func main() {
m1 := gmap.New()
m1 := gmap.New(true)
m1.Set("1", "1")
m2 := m1.Map()

View File

@ -2,5 +2,15 @@
# MySQL数据库配置
[database]
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
#[database]
# [[database.default]]
# type = "mysql"
# link = "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
#

View File

@ -0,0 +1,37 @@
package main
import (
"github.com/gogf/gf/database/gdb"
"sync"
"time"
)
var db gdb.DB
func init() {
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "12345678",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
MaxOpenConnCount: 100,
})
db, _ = gdb.New()
}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 100000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(10 * time.Second)
db.Table("user").Where("id=1").All()
}()
}
wg.Wait()
}

View File

@ -1,533 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
)
// 本文件用于gf框架的mysql数据库操作示例不作为单元测试使用
var db gdb.DB
// 初始化配置及创建数据库
func init() {
gdb.AddDefaultConfigNode(gdb.ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "12345678",
Name: "test",
Type: "mysql",
Role: "master",
Charset: "utf8",
})
db, _ = gdb.New()
//gins.Config().SetPath("/home/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/frame")
//db = g.Database()
//gdb.SetConfig(gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : 3306,
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
//})
//db, _ = gdb.Instance()
//gdb.SetConfig(gdb.Config {
// "default" : gdb.ConfigGroup {
// gdb.ConfigNode {
// Host : "127.0.0.1",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Weight : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.2",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Weight : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.3",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Weight : 100,
// },
// gdb.ConfigNode {
// Host : "127.0.0.4",
// Port : "3306",
// User : "root",
// Pass : "123456",
// Name : "test",
// Type : "mysql",
// Role : "master",
// Weight : 100,
// },
// },
//})
//db, _ = gdb.Instance()
}
// 创建测试数据库
func create() {
fmt.Println("create:")
_, err := db.Exec("CREATE DATABASE IF NOT EXISTS test")
if err != nil {
fmt.Println(err)
}
s := `
CREATE TABLE IF NOT EXISTS user (
uid INT(10) UNSIGNED AUTO_INCREMENT,
name VARCHAR(45),
PRIMARY KEY (uid)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
`
_, err = db.Exec(s)
if err != nil {
fmt.Println(err)
}
s = `
CREATE TABLE IF NOT EXISTS user_detail (
uid INT(10) UNSIGNED AUTO_INCREMENT,
site VARCHAR(255),
PRIMARY KEY (uid)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
`
_, err = db.Exec(s)
if err != nil {
fmt.Println(err)
}
fmt.Println()
}
// 数据写入
func insert() {
fmt.Println("insert:")
r, err := db.Insert("user", gdb.Map{
"name": "john",
})
if err == nil {
uid, err2 := r.LastInsertId()
if err2 == nil {
r, err = db.Insert("user_detail", gdb.Map{
"uid": uid,
"site": "http://johng.cn",
})
if err == nil {
fmt.Printf("uid: %d\n", uid)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err2)
}
} else {
fmt.Println(err)
}
fmt.Println()
}
// 基本sql查询
func query() {
fmt.Println("query:")
list, err := db.GetAll("select * from user limit 2")
if err == nil {
fmt.Println(list)
} else {
fmt.Println(err)
}
fmt.Println()
}
// replace into
func replace() {
fmt.Println("replace:")
r, err := db.Save("user", gdb.Map{
"uid": 1,
"name": "john",
})
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据保存
func save() {
fmt.Println("save:")
r, err := db.Save("user", gdb.Map{
"uid": 1,
"name": "john",
})
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 批量写入
func batchInsert() {
fmt.Println("batchInsert:")
_, err := db.BatchInsert("user", gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}, 10)
if err != nil {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update1() {
fmt.Println("update1:")
r, err := db.Update("user", gdb.Map{"name": "john1"}, "uid=?", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update2() {
fmt.Println("update2:")
r, err := db.Update("user", gdb.Map{"name": "john6"}, "uid=?", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 数据更新
func update3() {
fmt.Println("update3:")
r, err := db.Update("user", "name=?", "uid=?", "john2", 1)
if err == nil {
fmt.Println(r.LastInsertId())
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作1
func linkopSelect1() {
fmt.Println("linkopSelect1:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*, ud.site").Where("u.uid > ?", 1).Limit(0, 2).Select()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作2
func linkopSelect2() {
fmt.Println("linkopSelect2:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("u.*,ud.site").Where("u.uid=?", 1).One()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询操作3
func linkopSelect3() {
fmt.Println("linkopSelect3:")
r, err := db.Table("user u").LeftJoin("user_detail ud", "u.uid=ud.uid").Fields("ud.site").Where("u.uid=?", 1).Value()
if err == nil {
fmt.Println(r.String())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式查询数量1
func linkopCount1() {
fmt.Println("linkopCount1:")
r, err := db.Table("user u").Fields("uid").LeftJoin("user_detail ud", "u.uid=ud.uid").Where("u.uid=?", 1).Count()
if err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
fmt.Println()
}
// 错误操作
func linkopUpdate1() {
fmt.Println("linkopUpdate1:")
r, err := db.Table("henghe_setting").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过Map指针方式传参方式
func linkopUpdate2() {
fmt.Println("linkopUpdate2:")
r, err := db.Table("user").Data(gdb.Map{"name": "john2"}).Where("name=?", "john_1").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 通过字符串方式传参
func linkopUpdate3() {
fmt.Println("linkopUpdate3:")
r, err := db.Table("user").Data("name='john3'").Where("name=?", "john2").Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// Where条件使用Map
func linkopUpdate4() {
fmt.Println("linkopUpdate4:")
r, err := db.Table("user").Data(gdb.Map{"name": "john11111"}).Where(g.Map{"uid": 1}).Update()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式批量写入
func linkopBatchInsert1() {
fmt.Println("linkopBatchInsert1:")
r, err := db.Table("user").Data(gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}).Insert()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式批量写入,指定每批次写入的条数
func linkopBatchInsert2() {
fmt.Println("linkopBatchInsert2:")
r, err := db.Table("user").Data(gdb.List{
{"name": "john_1"},
{"name": "john_2"},
{"name": "john_3"},
{"name": "john_4"},
}).Batch(2).Insert()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 链式批量保存
func linkopBatchSave() {
fmt.Println("linkopBatchSave:")
r, err := db.Table("user").Data(gdb.List{
{"uid": 1, "name": "john_1"},
{"uid": 2, "name": "john_2"},
{"uid": 3, "name": "john_3"},
{"uid": 4, "name": "john_4"},
}).Save()
if err == nil {
fmt.Println(r.RowsAffected())
} else {
fmt.Println(err)
}
fmt.Println()
}
// 事务操作示例1
func transaction1() {
fmt.Println("transaction1:")
if tx, err := db.Begin(); err == nil {
r, err := tx.Save("user", gdb.Map{
"uid": 1,
"name": "john",
})
tx.Rollback()
fmt.Println(r, err)
}
fmt.Println()
}
// 事务操作示例2
func transaction2() {
fmt.Println("transaction2:")
if tx, err := db.Begin(); err == nil {
r, err := tx.Table("user").Data(gdb.Map{"uid": 1, "name": "john_1"}).Save()
tx.Commit()
fmt.Println(r, err)
}
fmt.Println()
}
// 主从io复用测试在mysql中使用 show full processlist 查看链接信息
func keepPing() {
fmt.Println("keepPing:")
for {
fmt.Println("ping...")
err := db.PingMaster()
if err != nil {
fmt.Println(err)
return
}
err = db.PingSlave()
if err != nil {
fmt.Println(err)
return
}
time.Sleep(1 * time.Second)
}
}
// like语句查询
func likeQuery() {
fmt.Println("likeQuery:")
if r, err := db.Table("user").Where("name like ?", "%john%").Select(); err == nil {
fmt.Println(r)
} else {
fmt.Println(err)
}
}
// mapToStruct
func mapToStruct() {
type User struct {
Uid int
Name string
}
fmt.Println("mapToStruct:")
if r, err := db.Table("user").Where("uid=?", 1).One(); err == nil {
u := User{}
if err := r.ToStruct(&u); err == nil {
fmt.Println(r)
fmt.Println(u)
} else {
fmt.Println(err)
}
} else {
fmt.Println(err)
}
}
// getQueriedSqls
func getQueriedSqls() {
for k, v := range db.GetQueriedSqls() {
fmt.Println(k, ":")
fmt.Println("Sql :", v.Sql)
fmt.Println("Args :", v.Args)
fmt.Println("Error:", v.Error)
fmt.Println("Func :", v.Func)
}
}
func main() {
//data := g.Map{
// "nickname" : "john",
//}
//db.SetDebug(true)
//r, err := db.Table("user").Where("id=1").Data(data).Update()
//fmt.Println(err)
//fmt.Println(r.RowsAffected())
//data2 := g.Map{
// "adsys1" : "ss",
//}
//db.SetDebug(true)
//r, err := db.Table("cd_adsys").Where("adsys0=1").Data(data2).Update()
//fmt.Println(err)
//fmt.Println(r.RowsAffected())
//return
//db.SetDebug(true)
//r, err := db.Table("test").Where("id=1").One()
//fmt.Println(r["datetime"])
//fmt.Println(r["datetime"].Time().Date())
//fmt.Println(err)
//create()
//create()
//insert()
//query()
//replace()
//save()
//batchInsert()
//update1()
//update2()
//update3()
linkopSelect1()
//linkopSelect2()
//linkopSelect3()
//linkopCount1()
//linkopUpdate1()
//linkopUpdate2()
//linkopUpdate3()
//linkopUpdate4()
//
//transaction1()
//transaction2()
//
//keepPing()
//likeQuery()
//mapToStruct()
//getQueriedSqls()
}

View File

@ -2,22 +2,26 @@ package main
import (
"fmt"
"github.com/gogf/gf/database/gdb"
"time"
"github.com/gogf/gf/frame/g"
)
func main() {
db := g.DB()
// 开启调试模式以便于记录所有执行的SQL
//db := g.DB()
gdb.AddDefaultConfigNode(gdb.ConfigNode{
LinkInfo: "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local",
Type: "mysql",
Charset: "utf8",
})
db, _ := gdb.New()
db.SetDebug(true)
r, e := db.Table("user").Data(g.Map{
"passport": "1",
"password": "1",
"nickname": "1",
"create_time": time.Now(),
}).Insert()
type User struct {
CreateTime time.Time `orm:"create_time"`
}
r, e := db.Table("user").Data(User{CreateTime: time.Now()}).Insert()
if e != nil {
panic(e)
}

View File

@ -6,6 +6,7 @@ import (
)
func main() {
gdebug.PrintStack()
fmt.Println(gdebug.CallerPackage())
fmt.Println(gdebug.CallerFunction())
}

View File

@ -14,11 +14,11 @@ import (
// User is the golang structure for table user.
type User struct {
Id int `orm:"id,primary" json:"id"`
Passport string `orm:"passport" json:"passport"`
Password string `orm:"password" json:"password"`
Nickname string `orm:"nickname,unique" json:"nickname"`
CreateTime *gtime.Time `orm:"create_time" json:"create_time"`
Id int `orm:"id,primary" json:"id"`
Passport string `orm:"passport" json:"passport"`
Password string `orm:"password" json:"password"`
Nickname string `orm:"nickname,unique" json:"nickname"`
CreateTime *gtime.Time `orm:"create_time" json:"create_time"`
}
var (
@ -57,4 +57,4 @@ func (r *User) Update() (result sql.Result, err error) {
// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table.
func (r *User) Delete() (result sql.Result, err error) {
return ModelUser.Where(gdb.GetWhereConditionOfStruct(r)).Delete()
}
}

View File

@ -1,19 +1,12 @@
package main
import (
"crypto/tls"
"fmt"
"net/http"
"github.com/gogf/gf/net/ghttp"
)
func main() {
c := ghttp.NewClient()
c.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
r, e := c.Clone().Get("https://127.0.0.1:8199")
fmt.Println(e)
fmt.Println(r.StatusCode)
r, err := ghttp.Get("http://127.0.0.1:8199/11111/11122")
fmt.Println(err)
fmt.Println(r.Header)
}

View File

@ -0,0 +1,22 @@
package main
import (
"fmt"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func main() {
path1 := "/Users/john/Pictures/logo1.png"
path2 := "/Users/john/Pictures/logo2.png"
r, e := ghttp.Post(
"http://127.0.0.1:8199/upload",
fmt.Sprintf(`upload-file=@file:%s&upload-file=@file:%s`, path1, path2),
)
if e != nil {
glog.Error(e)
} else {
fmt.Println(string(r.ReadAll()))
r.Close()
}
}

View File

@ -0,0 +1,22 @@
package main
import (
"fmt"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func main() {
path1 := "/Users/john/Pictures/logo1.png"
path2 := "/Users/john/Pictures/logo2.png"
r, e := ghttp.Post(
"http://127.0.0.1:8199/upload",
fmt.Sprintf(`upload-file[]=@file:%s&upload-file[]=@file:%s`, path1, path2),
)
if e != nil {
glog.Error(e)
} else {
fmt.Println(string(r.ReadAll()))
r.Close()
}
}

View File

@ -0,0 +1,80 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gfile"
"io"
)
// Upload uploads files to /tmp .
func Upload(r *ghttp.Request) {
saveDir := "/tmp/"
for _, item := range r.GetMultipartFiles("upload-file") {
file, err := item.Open()
if err != nil {
r.Response.Write(err)
return
}
defer file.Close()
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
if err != nil {
r.Response.Write(err)
return
}
defer f.Close()
if _, err := io.Copy(f, file); err != nil {
r.Response.Write(err)
return
}
}
r.Response.Write("upload successfully")
}
// UploadShow shows uploading simgle file page.
func UploadShow(r *ghttp.Request) {
r.Response.Write(`
<html>
<head>
<title>GF Upload File Demo</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
<input type="file" name="upload-file" />
<input type="submit" value="upload" />
</form>
</body>
</html>
`)
}
// UploadShowBatch shows uploading multiple files page.
func UploadShowBatch(r *ghttp.Request) {
r.Response.Write(`
<html>
<head>
<title>GF Upload Files Demo</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
<input type="file" name="upload-file" />
<input type="file" name="upload-file" />
<input type="submit" value="upload" />
</form>
</body>
</html>
`)
}
func main() {
s := g.Server()
s.Group("/upload", func(group *ghttp.RouterGroup) {
group.ALL("/", Upload)
group.ALL("/show", UploadShow)
group.ALL("/batch", UploadShowBatch)
})
s.SetPort(8199)
s.Run()
}

View File

@ -9,7 +9,7 @@ import (
func main() {
path := "/home/john/Workspace/Go/github.com/gogf/gf/version.go"
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "name=john&age=18&upload-file=@file:"+path)
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
if e != nil {
glog.Error(e)
} else {

View File

@ -4,28 +4,41 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gfile"
"io"
)
// 执行文件上传处理,上传到系统临时目录 /tmp
// Upload uploads files to /tmp .
func Upload(r *ghttp.Request) {
if f, h, e := r.FormFile("upload-file"); e == nil {
saveDir := "/tmp/"
for _, item := range r.GetMultipartFiles("upload-file") {
file, err := item.Open()
if err != nil {
r.Response.Write(err)
return
}
defer file.Close()
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
if err != nil {
r.Response.Write(err)
return
}
defer f.Close()
name := gfile.Basename(h.Filename)
buffer := make([]byte, h.Size)
f.Read(buffer)
gfile.PutBytes("/tmp/"+name, buffer)
r.Response.Write(name + " uploaded successly")
} else {
r.Response.Write(e.Error())
if _, err := io.Copy(f, file); err != nil {
r.Response.Write(err)
return
}
}
r.Response.Write("upload successfully")
}
// 展示文件上传页面
// UploadShow shows uploading simgle file page.
func UploadShow(r *ghttp.Request) {
r.Response.Write(`
<html>
<head>
<title>上传文件</title>
<title>GF Upload File Demo</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
@ -37,10 +50,31 @@ func UploadShow(r *ghttp.Request) {
`)
}
// UploadShowBatch shows uploading multiple files page.
func UploadShowBatch(r *ghttp.Request) {
r.Response.Write(`
<html>
<head>
<title>GF Upload Files Demo</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
<input type="file" name="upload-file" />
<input type="file" name="upload-file" />
<input type="submit" value="upload" />
</form>
</body>
</html>
`)
}
func main() {
s := g.Server()
s.BindHandler("/upload", Upload)
s.BindHandler("/upload/show", UploadShow)
s.Group("/upload", func(group *ghttp.RouterGroup) {
group.ALL("/", Upload)
group.ALL("/show", UploadShow)
group.ALL("/batch", UploadShowBatch)
})
s.SetPort(8199)
s.Run()
}

View File

@ -16,8 +16,8 @@ func Order(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v1", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareCORS)
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
g.GET("/order", Order)
})
s.SetPort(8199)

View File

@ -18,8 +18,8 @@ func Order(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v1", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareCORS)
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
g.GET("/order", Order)
})
s.SetPort(8199)

View File

@ -24,8 +24,8 @@ func Order(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v1", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareCORS)
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
g.GET("/order", Order)
})
s.SetPort(8199)

View File

@ -0,0 +1,9 @@
package main
import (
"github.com/gogf/gf/net/ghttp"
)
func main() {
ghttp.PostContent("http://127.0.0.1:8199/", "array[]=1&array[]=2")
}

View File

@ -8,7 +8,7 @@ import (
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
g.Dump(r.GetPostMap())
g.Dump(r.GetForm("array"))
r.Response.WriteTpl("form.html")
})
s.SetPort(8199)

View File

@ -3,14 +3,16 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func main() {
s := g.Server()
s.SetIndexFolder(true)
s.BindHandler("/", func(r *ghttp.Request) {
glog.Println(r.Header)
r.Response.Write("hello world")
})
s.SetPort(8199)
s.SetPort(8999)
s.Run()
}

View File

@ -11,7 +11,7 @@ func Order(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v1", func(g *ghttp.RouterGroup) {
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
g.GET("/order", Order)
})
s.SetPort(8199)

View File

@ -11,8 +11,8 @@ func Order(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v1", func(g *ghttp.RouterGroup) {
g.Hook("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
group.Hook("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.CORSDefault()
})
g.GET("/order", Order)

View File

@ -1,32 +0,0 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
// 优先调用的HOOK
func beforeServeHook1(r *ghttp.Request) {
r.SetParam("name", "GoFrame")
r.Response.Writeln("set name")
}
// 随后调用的HOOK
func beforeServeHook2(r *ghttp.Request) {
r.SetParam("site", "https://goframe.org")
r.Response.Writeln("set site")
}
// 允许对同一个路由同一个事件注册多个回调函数,按照注册顺序进行优先级调用。
// 为便于在路由表中对比查看优先级这里讲HOOK回调函数单独定义为了两个函数。
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writeln(r.Get("name"))
r.Response.Writeln(r.Get("site"))
})
s.BindHookHandler("/", ghttp.HOOK_BEFORE_SERVE, beforeServeHook1)
s.BindHookHandler("/", ghttp.HOOK_BEFORE_SERVE, beforeServeHook2)
s.SetPort(8199)
s.Run()
}

View File

@ -1,11 +1,12 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writeln("来自于HTTPS的哈喽世界")
})

View File

@ -1,11 +1,12 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := ghttp.GetServer()
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writeln("您可以同时通过HTTP和HTTPS方式看到该内容")
})

View File

@ -0,0 +1,7 @@
[server]
LogPath = "/tmp/gflog/server"
LogStdout = true
ErrorLogEnabled = true
ErrorLogPattern = "error.log"
AccessLogEnabled = true
AccessLogPattern = "access.log"

View File

@ -1,26 +1,21 @@
package main
import (
"net/http"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"net/http"
)
func main() {
s := ghttp.GetServer()
s.BindHandler("/log/handler", func(r *ghttp.Request) {
r.Response.WriteStatus(http.StatusNotFound, "文件找不到了")
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.ALL("/", func(r *ghttp.Request) {
r.Response.Write("halo world!")
})
group.ALL("/log/handler", func(r *ghttp.Request) {
r.Response.WriteStatus(http.StatusNotFound, "File Not Found!")
})
})
s.SetAccessLogEnabled(true)
s.SetErrorLogEnabled(true)
//s.SetLogHandler(func(r *ghttp.Request, error ...interface{}) {
// if len(error) > 0 {
// // 如果是错误日志
// fmt.Println("错误产生了:", error[0])
// }
// // 这里是请求日志
// fmt.Println("请求处理完成,请求地址:", r.URL.String(), "请求结果:", r.Response.Status)
//})
s.SetPort(8199)
s.Run()
}

View File

@ -23,9 +23,9 @@ func MiddlewareCORS(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v2", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareAuth, MiddlewareCORS)
g.ALL("/user/list", func(r *ghttp.Request) {
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareCORS)
group.ALL("/user/list", func(r *ghttp.Request) {
r.Response.Write("list")
})
})

View File

@ -18,7 +18,7 @@ func MiddlewareAuth(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/admin", func(g *ghttp.RouterGroup) {
s.Group("/admin", func(group *ghttp.RouterGroup) {
g.MiddlewarePattern("/*action", func(r *ghttp.Request) {
if action := r.GetRouterString("action"); action != "" {
switch action {
@ -29,10 +29,10 @@ func main() {
}
MiddlewareAuth(r)
})
g.ALL("/login", func(r *ghttp.Request) {
group.ALL("/login", func(r *ghttp.Request) {
r.Response.Write("login")
})
g.ALL("/dashboard", func(r *ghttp.Request) {
group.ALL("/dashboard", func(r *ghttp.Request) {
r.Response.Write("dashboard")
})
})

View File

@ -12,9 +12,9 @@ func MiddlewareCORS(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v2", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareCORS)
g.ALL("/user/list", func(r *ghttp.Request) {
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareCORS)
group.ALL("/user/list", func(r *ghttp.Request) {
r.Response.Write("list")
})
})

View File

@ -31,9 +31,9 @@ func MiddlewareError(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/api.v2", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareAuth, MiddlewareCORS, MiddlewareError)
g.ALL("/user/list", func(r *ghttp.Request) {
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareCORS, MiddlewareError)
group.ALL("/user/list", func(r *ghttp.Request) {
panic("db error: sql is xxxxxxx")
})
})

View File

@ -30,12 +30,12 @@ func MiddlewareLog(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareLog)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareLog)
})
s.Group("/api.v2", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareAuth, MiddlewareCORS)
g.ALL("/user/list", func(r *ghttp.Request) {
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareCORS)
group.ALL("/user/list", func(r *ghttp.Request) {
panic("custom error")
})
})

View File

@ -7,18 +7,18 @@ import (
func main() {
s := g.Server()
s.Group("/api.v2", func(g *ghttp.RouterGroup) {
g.Middleware(func(r *ghttp.Request) {
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(func(r *ghttp.Request) {
r.Response.Write("start")
r.Middleware.Next()
r.Response.Write("end")
})
g.Group("/order", func(g *ghttp.RouterGroup) {
g.Group("/order", func(group *ghttp.RouterGroup) {
g.GET("/list", func(r *ghttp.Request) {
r.Response.Write("list")
})
})
g.Group("/user", func(g *ghttp.RouterGroup) {
g.Group("/user", func(group *ghttp.RouterGroup) {
g.GET("/info", func(r *ghttp.Request) {
r.Response.Write("info")
})
@ -26,11 +26,11 @@ func main() {
r.Response.Write("edit")
})
})
g.Group("/hook", func(g *ghttp.RouterGroup) {
g.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
g.Group("/hook", func(group *ghttp.RouterGroup) {
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.Write("hook any")
})
g.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
group.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.Write("hook name")
})
})

View File

@ -0,0 +1,36 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
// 前置中间件1
func MiddlewareBefore1(r *ghttp.Request) {
r.SetParam("name", "GoFrame")
r.Response.Writeln("set name")
r.Middleware.Next()
}
// 前置中间件2
func MiddlewareBefore2(r *ghttp.Request) {
r.SetParam("site", "https://goframe.org")
r.Response.Writeln("set site")
r.Middleware.Next()
}
func main() {
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareBefore1, MiddlewareBefore2)
group.ALL("/", func(r *ghttp.Request) {
r.Response.Writefln(
"%s: %s",
r.GetParamVar("name").String(),
r.GetParamVar("site").String(),
)
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -6,7 +6,7 @@ import (
func main() {
s := ghttp.GetServer()
s.EnablePprof()
s.EnablePProf()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Writeln("哈喽世界!")
})

View File

@ -6,23 +6,35 @@ import (
"github.com/gogf/gf/util/gvalid"
)
func main() {
type User struct {
Uid int `gvalid:"uid@min:1"`
Name string `params:"username" gvalid:"username @required|length:6,30"`
Pass1 string `params:"password1" gvalid:"password1@required|password3"`
Pass2 string `params:"password2" gvalid:"password2@required|password3|same:password1#||两次密码不一致,请重新输入"`
}
type User struct {
Uid int `gvalid:"uid@min:1"`
Name string `params:"username" gvalid:"username @required|length:6,30"`
Pass1 string `params:"password1" gvalid:"password1@required|password3"`
Pass2 string `params:"password2" gvalid:"password2@required|password3|same:password1#||两次密码不一致,请重新输入"`
}
func main() {
s := g.Server()
s.BindHandler("/user", func(r *ghttp.Request) {
user := new(User)
r.GetToStruct(user)
if err := gvalid.CheckStruct(user, nil); err != nil {
r.Response.WriteJson(err.Maps())
} else {
r.Response.Write("ok")
}
s.Group("/", func(rgroup *ghttp.RouterGroup) {
rgroup.ALL("/user", func(r *ghttp.Request) {
user := new(User)
if err := r.GetToStruct(user); err != nil {
r.Response.WriteJsonExit(g.Map{
"message": err,
"errcode": 1,
})
}
if err := gvalid.CheckStruct(user, nil); err != nil {
r.Response.WriteJsonExit(g.Map{
"message": err.Maps(),
"errcode": 1,
})
}
r.Response.WriteJsonExit(g.Map{
"message": "ok",
"errcode": 0,
})
})
})
s.SetPort(8199)
s.Run()

View File

@ -12,8 +12,8 @@ import (
func main() {
gres.Dump()
v := g.View()
v.SetPath("template/layout1")
//v := g.View()
//v.SetPath("template/layout1")
s := g.Server()
s.SetIndexFolder(true)
@ -22,8 +22,8 @@ func main() {
fmt.Println(r.URL.Path, r.IsFileRequest())
})
s.BindHandler("/template", func(r *ghttp.Request) {
r.Response.WriteTpl("layout.html")
r.Response.WriteTpl("layout1/layout.html")
})
s.SetPort(8199)
s.SetPort(8198)
s.Run()
}

View File

@ -29,15 +29,15 @@ func MiddlewareLog(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group("/", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareLog)
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareLog)
})
s.Group("/api.v2", func(g *ghttp.RouterGroup) {
g.Middleware(MiddlewareAuth, MiddlewareCORS)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.Middleware(MiddlewareAuth, MiddlewareCORS)
g.GET("/test", func(r *ghttp.Request) {
r.Response.Write("test")
})
g.Group("/order", func(g *ghttp.RouterGroup) {
g.Group("/order", func(group *ghttp.RouterGroup) {
g.GET("/list", func(r *ghttp.Request) {
r.Response.Write("list")
})
@ -45,7 +45,7 @@ func main() {
r.Response.Write("update")
})
})
g.Group("/user", func(g *ghttp.RouterGroup) {
g.Group("/user", func(group *ghttp.RouterGroup) {
g.GET("/info", func(r *ghttp.Request) {
r.Response.Write("info")
})
@ -56,11 +56,11 @@ func main() {
r.Response.Write("drop")
})
})
g.Group("/hook", func(g *ghttp.RouterGroup) {
g.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
g.Group("/hook", func(group *ghttp.RouterGroup) {
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.Write("hook any")
})
g.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
group.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
r.Response.Write("hook name")
})
})

View File

@ -4,21 +4,21 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetSessionMaxAge(61 * time.Second)
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.WriteJson(r.Session.Map())
})
s.BindHandler("/clear", func(r *ghttp.Request) {
r.Session.Clear()
s.Group("/", func(group *ghttp.RouterGroup) {
g.GET("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
g.GET("/get", func(r *ghttp.Request) {
r.Response.WriteJson(r.Session.Map())
})
g.GET("/clear", func(r *ghttp.Request) {
r.Session.Clear()
})
})
s.SetPort(8199)
s.Run()

View File

@ -1,82 +0,0 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gtime"
)
// 测试SESSION写入
func SessionSet(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.WriteJson("ok")
}
// 测试SESSION读取
func SessionGet(r *ghttp.Request) {
r.Response.WriteJson(r.Session.Map())
}
// 请求处理之前将Redis中的数据读取出来并存储到SESSION对象中。
func RedisHandlerGet(r *ghttp.Request) {
if !r.IsFileRequest() {
id := r.Cookie.GetSessionId()
if id == "" {
return
}
// 应用服务器一般是多个节点构成的集群,
// 当请求中带有SESSION ID时自动从Redis读取并恢复数据。
value, err := g.Redis().DoVar("GET", id)
if err != nil {
panic(err)
}
if !value.IsNil() {
if err := r.Session.Restore(value.Bytes()); err != nil {
panic(err)
}
}
}
}
// 请求结束时将SESSION数据存储到Redis中或者在SESSION删除时也删除Redis中的数据。
func RedisHandlerSet(r *ghttp.Request) {
if !r.IsFileRequest() {
id := r.Cookie.GetSessionId()
if id == "" {
return
}
err := (error)(nil)
value := ([]byte)(nil)
if r.Session.Size() > 0 {
if value, err = r.Session.Export(); err == nil {
if len(value) == 0 {
return
} else if !r.Session.IsDirty() {
// 更新过期时间
_, err = g.Redis().Do("EXPIRE", id, r.Server.GetSessionMaxAge())
} else {
// 更新Redis数据
_, err = g.Redis().Do("SETEX", id, r.Server.GetSessionMaxAge(), value)
}
}
} else {
// 清空SESSION后自动删除Redis数据
_, err = g.Redis().Do("DEL", id)
}
if err != nil {
panic(err)
}
}
}
func main() {
s := g.Server()
s.BindHandler("/set", SessionSet)
s.BindHandler("/get", SessionGet)
s.BindHookHandlerByMap("/*", map[string]ghttp.HandlerFunc{
ghttp.HOOK_BEFORE_SERVE: RedisHandlerGet,
ghttp.HOOK_AFTER_SERVE: RedisHandlerSet,
})
s.SetPort(8199)
s.Run()
}

View File

@ -7,7 +7,9 @@ func main() {
s := g.Server()
s.SetIndexFolder(true)
s.SetServerRoot("/Users/john/Downloads")
s.AddSearchPath("/Users/john/Documents")
//s.AddSearchPath("/Users/john/Documents")
s.SetErrorLogEnabled(true)
s.SetAccessLogEnabled(true)
s.SetPort(8199)
s.Run()
}

View File

@ -5,7 +5,6 @@
<title>{{.title}}</title>
</head>
<body>
<H1>姓名 {{.name}}</H1>
12
<H1>{{.name}}: {{.score}}</H1>
</body>
</html>

View File

@ -2,25 +2,19 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/frame/gmvc"
"github.com/gogf/gf/net/ghttp"
)
type ControllerIndex struct {
gmvc.Controller
}
func (c *ControllerIndex) Info() {
c.View.Assign("title", "Go Frame 第一个网站")
c.View.Assigns(g.Map{
"name": "很开心1",
"score": 100,
})
c.View.Display("index.html")
}
func main() {
s := ghttp.GetServer()
s.BindController("/", new(ControllerIndex))
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.Write("Hello World")
r.Response.WriteTpl("index.tpl", g.Map{
"title": "Test",
"name": "John",
"score": 100,
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -11,7 +11,7 @@ func ws(r *ghttp.Request) {
ws, err := r.WebSocket()
if err != nil {
glog.Error(err)
r.Exit()
return
}
for {
msgType, msg, err := ws.ReadMessage()
@ -26,10 +26,10 @@ func ws(r *ghttp.Request) {
func main() {
s := g.Server()
s.Group().Bind([]ghttp.GroupItem{
s.Group("").Bind([]ghttp.GroupItem{
{"ALL", "/ws", ws},
})
s.SetAccessLogEnabled(true)
s.SetServerRoot(gfile.MainPkgPath())
s.SetPort(8199)
s.Run()

View File

@ -13,7 +13,7 @@ func main() {
ws, err := r.WebSocket()
if err != nil {
glog.Error(err)
r.Exit()
return
}
for {
msgType, msg, err := ws.ReadMessage()

View File

@ -8,11 +8,10 @@ import (
)
func main() {
data, err := gtcp.SendRecv("www.baidu.com:80", []byte("HEAD / HTTP/1.1\n\n"), -1)
if len(data) > 0 {
fmt.Println(string(data))
}
dstConn, err := gtcp.NewPoolConn("www.medlinker.com:80")
_, err = dstConn.Write([]byte("HEAD / HTTP/1.1\n\n"))
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err.Error())
}
fmt.Println(dstConn.RecvLine())
}

View File

@ -1,3 +1,5 @@
# redis配置
[[redis-cache]]
db = 0

View File

@ -8,5 +8,5 @@ import (
// 使用g.Config方法获取配置管理对象并指定默认的配置文件名称
func main() {
fmt.Println(g.Config("config.json").Get("viewpath"))
fmt.Println(g.Config().Get("viewpath"))
}

View File

@ -6,7 +6,6 @@ import (
"github.com/gogf/gf/frame/g"
)
// 演示在找不到配置文件时的错误提示
func main() {
fmt.Println(g.Config("none-exist-config.toml").Get("none"))
fmt.Println(g.Config().Get("none"))
}

View File

@ -0,0 +1,16 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/glog"
)
func main() {
err := glog.SetConfigWithMap(g.Map{
"prefix": "[TEST]",
})
if err != nil {
panic(err)
}
glog.Info(1)
}

View File

@ -1,11 +0,0 @@
package main
import (
"context"
"fmt"
)
func main() {
c := context.WithValue(context.Background(), "key", "value")
fmt.Printf("%v", c)
}

View File

@ -1,24 +1,27 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetSessionMaxAge(60)
s.SetConfigWithMap(g.Map{
"SessionMaxAge": time.Minute,
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("captcha", map[string]string{
"key": "value",
})
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
fmt.Println(r.Session.Get("captcha"))
r.Response.Write(r.Session.Get("captcha"))
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
s.SetPort(8199)
s.Run()

View File

@ -0,0 +1,30 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gsession"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{
"SessionMaxAge": time.Minute,
"SessionStorage": gsession.NewStorageMemory(),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,4 @@
# Redis数据库配置
[redis]
default = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"

View File

@ -0,0 +1,30 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gsession"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{
"SessionMaxAge": time.Minute,
"SessionStorage": gsession.NewStorageRedisHashTable(g.Redis()),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,4 @@
# Redis数据库配置
[redis]
default = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"

View File

@ -0,0 +1,30 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/gsession"
"github.com/gogf/gf/os/gtime"
"time"
)
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{
"SessionMaxAge": time.Minute,
"SessionStorage": gsession.NewStorageRedis(g.Redis()),
})
s.BindHandler("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Second())
r.Response.Write("ok")
})
s.BindHandler("/get", func(r *ghttp.Request) {
r.Response.Write(r.Session.Map())
})
s.BindHandler("/del", func(r *ghttp.Request) {
r.Session.Clear()
r.Response.Write("ok")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,21 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
)
func main() {
content := `{{.name}} says "a{#hello}{#world}!"`
result1, _ := g.View().ParseContent(content, g.Map{
"name": "john",
"I18nLanguage": "zh-CN",
})
fmt.Println(result1)
result2, _ := g.View().ParseContent(content, g.Map{
"name": "john",
"I18nLanguage": "ja",
})
fmt.Println(result2)
}

View File

@ -1,14 +1,3 @@
# 监控服务主动拉取监控数据配置
[active-pulling]
# 业务类别 腾讯问诊 订单总数
[[active-pulling.tencent-inquiry]]
groupId = 3533761
metricName = "OrderCount" # 监控项名称
url = "http://ylt.medlinker.com/monitor/ordercount" # 监控数据地址
interval = "1m" # d:日 h:小时 m:分 s:秒
# 业务类别 腾讯问诊 超时订单总数
[[active-pulling.tencent-inquiry]]
groupId = 3533711
metricName = "TimedOutOrderCount"
url = "http://ylt.medlinker.com/monitor/timedout-ordercount"
interval = "1m"
[database]
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"

View File

@ -5,43 +5,13 @@ import (
"github.com/gogf/gf/net/ghttp"
)
func loadRouter(domain *ghttp.Domain) {
domain.Group("/", func(g *ghttp.RouterGroup) {
g.Group("/app", func(gApp *ghttp.RouterGroup) {
// 该路由规则仅会在GET请求下有效
gApp.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
r.Response.WriteJson(r.Router)
})
// 该路由规则仅会在GET请求及localhost域名下有效
gApp.GET("/order/info/{order_id}", func(r *ghttp.Request) {
r.Response.WriteJson(r.Router)
})
// 该路由规则仅会在DELETE请求下有效
gApp.DELETE("/comment/{id}", func(r *ghttp.Request) {
r.Response.WriteJson(r.Router)
})
})
// 该路由规则仅会在GET请求下有效
g.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
r.Response.WriteJson(r.Router)
})
// 该路由规则仅会在GET请求及localhost域名下有效
g.GET("/order/info/{order_id}", func(r *ghttp.Request) {
r.Response.WriteJson(r.Router)
})
// 该路由规则仅会在DELETE请求下有效
g.DELETE("/comment/{id}", func(r *ghttp.Request) {
r.Response.WriteJson(r.Router)
})
})
}
func main() {
s := g.Server()
domain := s.Domain("localhost")
loadRouter(domain)
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
group.ALL("/test", func(r *ghttp.Request) {
r.Response.Write(r.GetRequest("nickname"))
})
})
s.SetPort(8199)
s.Run()
}

View File

@ -3,6 +3,7 @@ language: go
go:
- "1.11.x"
- "1.12.x"
- "1.13.x"
branches:
only:
@ -11,7 +12,7 @@ branches:
- staging
env:
- GO111MODULE=on
- GF_DEV=1 GO111MODULE=on
services:
- mysql
@ -32,6 +33,7 @@ install:
before_script:
- find . -name "*.go" | xargs gofmt -w
- git diff --name-only --exit-code || exit 1
- echo "UPDATE mysql.user SET authentication_string=PASSWORD('12345678') WHERE user='root';\nFLUSH PRIVILEGES;\n" | mysql -u root
- psql -c 'create database travis_ci_test;' -U postgres
script:

View File

@ -24,6 +24,11 @@
|Hades|alipay|¥66.66
|蔡蔡|wechat|¥666.00
|上海金保证网络科技|bank|¥2000.00
|[foxhack](https://github.com/foxhack)|wechat|¥20.00
|*栈|wechat|¥5.00
|*络|wechat|¥10.00
|R*s|wechat|¥18.88
|粟*e|wechat|¥50.00

View File

@ -23,7 +23,7 @@ require github.com/gogf/gf latest
# Limitation
```
golang version >= 1.10
golang version >= 1.11
```
# Documentation

View File

@ -24,7 +24,7 @@
# 安装
```html
go get -u github.com/gogf/gf
go get -u -v github.com/gogf/gf
```
推荐使用 `go.mod`:
```
@ -33,7 +33,7 @@ require github.com/gogf/gf latest
# 限制
```shell
golang版本 >= 1.10
golang版本 >= 1.11
```
# 架构

View File

@ -1,4 +1,180 @@
# `v1.9.3`
# `v1.10.0` (2019-12-05)
各位`gfer`久等了,较上一次发布时间过去已有两个多月了,这段时间`GF`也在不断地迭代改进,细节比较多,拟了个大概,以下是`release log`。
另外,`GoFrame`也参加了2019最受欢迎中国开源软件评选投票明天就结束了欢迎为`GF`投票啊https://www.oschina.net/project/top_cn_2019 网页可以投一票,微信也可以投一票。
## 新特性
1. `Web Server`新特性:
- 改进中间件及分组路由实现https://goframe.org/net/ghttp/router/middleware
- 增加文件配置管理特性https://goframe.org/net/ghttp/config
- 改进参数获取https://goframe.org/net/ghttp/request
- 改进文件上传https://goframe.org/net/ghttp/client/demo/upload
1. `Session`增加内置的多种`Storage`实现:
- 基本介绍https://goframe.org/os/gsession/index
- 文件存储https://goframe.org/os/gsession/file
- 内存存储https://goframe.org/os/gsession/memory
- `Redis`存储https://goframe.org/os/gsession/redis
1. 增加日志组件单例对象,并优化配置管理:
- https://goframe.org/frame/g/index
- https://goframe.org/os/glog/config
1. 常用的`container`容器增加`JSON`数据格式的`Marshal`/`UnMarshal`接口实现:
- https://goframe.org/container/gmap/index
- https://goframe.org/container/garray/index
- https://goframe.org/container/gset/index
- https://goframe.org/container/gvar/index
- https://goframe.org/container/gtype/index
- https://goframe.org/container/glist/index
- https://goframe.org/container/gvar/index
1. 新增`guuid`模块,用于通用的`UUID`生成https://goframe.org/util/guuid/index
## 功能改进
### `net`
1. `ghttp`
- 改进请求流程处理性能;
- `Server`增加对`Logger`日志对象的配置;
- `Server`开放了`GetRouterMap`方法,用于获得当前服务的路由列表信息,使得开发者可以更方便地实现自定义权限管理;
- `Server`配置管理优化;
- `Client`客户端对象进行了大量的改进工作;
- `Client`客户端对象增加多文件上传功能;
- `Request`对象增加`GetError`方法,用于获取当前处理错误;
- `Request`对象增加独立的视图对象及视图变量绑定功能,使得每个请求可以独立视图管理,也可以通过中间件切换请求对象的视图对象。默认情况下该功能关闭,视图解析时使用的是`Server`对象的视图对象;
- 改建`Response`对象的`CORS`功能;
- 增加`Response.WriteTplDefault`方法,用于解析并返回默认的模板内容;
- 增加更多的单元测试用例;
- 其他改进;
1. `gipv4`/`gipv6`
- 一些改进工作;
1. `gtcp`/`gudp`
- 一些改进工作;
### `database`
1. `gdb`
- 大量细节改进工作;
- 去掉查询数据为空时的`sql.ErrNoRows`错误返回,保留`Struct`/`Structs`/`Scan`方法在操作数据为空的该错误返回;
- 调试模式开启时输出的SQL语句改进为完整的带参数的SQL仅作参考
- `Where`方法增加对`gmap`数据类型支持,包括顺序性的`ListMap`/`TreeMap`等等;
- 查询缓存方法`Cache`的缓存时间参数类型修改为`time.Duration`
- 修改`Record`/`Result`的数据类型转换方法名称,原有的转换方法标记为`deprecated`
- `Record`/`Result`查询结果类型增加`IsEmpty`方法,用于判断结果集是否为空;
- `Record`类型增加`GMap`方法,用于将查询记录转换为`gmap`类型;
- 增加`Option`/`OptionOmitEmpty`方法,用于输入参数过滤,包括`Data`参数及`Where`参数https://goframe.org/database/gdb/empty
- 增加字段排除方法`FieldsEx`https://goframe.org/database/gdb/senior
- 增加日志功能特性https://goframe.org/database/gdb/senior
- 改进数据库配置管理https://goframe.org/database/gdb/config
- 增加大量单元测试;
1. `gredis`
- 返回数据类型转换改进https://github.com/gogf/gf/issues/415
- 完善单元测试;
- 其他改进;
### `os`
1. `gcache`
- 需要注意了:缓存的有效时间参数从`interface{}`类型调整为了`time.Duration`类型,因此不再兼容之前的`int`参数类型,以保证更好的性能;
1. `gfcache`
- 由于`gcache`组件的缓存时间参数类型的变更,因此该组件的时间参数也变更为了`time.Duration`类型;
1. `gcfg`
- 增加`Available`方法,用以判断配置是否有效;
1. `gfile`
- 增加`Chdir`方法,用于工作目录切换;
1. `gtime`
- 增加`JSON`数据格式的`Marshal`/`UnMarshal`接口实现;
### `container`
1. `gmap`
- 增加`MapStrAny`方法,用于常见`map`类型的转换;
- 增加`MapCopy`方法,用于底层`map`数据复制;
- 增加`FilterEmpty`方法,用于`map`空值过滤;
- 增加`Pop`/`Pops`方法,用于随机返回`map`中的数据项(并删除);
- 增加`Replace`方法,用于给定的`map`数据覆盖底层`map`数据项;
- 完善单元测试;
- 其他改进;
1. `garray`
- 增加`Interfaces`转换方法,返回`[]interface{}`类型;
- 对排序数组增加`SetComparator`方法用户自定义修改比较器;
- 完善单元测试;
- 其他改进;
1. `glist`
- 增加`NewFrom`方法,基于给定的`[]interface{}`变量创建链表;
- 增加`Join`方法,用于将链表项使用给定字符串连接为字符串返回;
- 完善单元测试;
- 其他改进;
1. `gset`
- 增加`AddIfNotExistFunc`/`AddIfNotExistFuncLock`方法;
- 完善单元测试;
- 其他改进;
1. `gtree`
- 增加`Replace`方法,用于更新现有树的数据项;
- 其他改进;
1. `gtype`
- 一些细节改进工作,不一一列出;
- 完善基准测试、单元测试;
1. `gvar`
- 增加`Ints`/`Uints`类型转换方法;
- 其他改进;
### `crypto`
1. `gmd5`
- 小细节改进;
1. `gsha1`
- 小细节改进;
### `text`
1. `gstr`
- 改进`SplitAndTrim`方法,将`SplitAndTrimSpace`标记为`deprecated`
- 增加`TrimStr`方法;
- 完善单元测试;
- 其他改进;
### `debug`
1. `gdebug`
- 增加`CallerFileLineShort`/`FuncPath`/`FuncName`方法;
- 其他改进;
### `encoding`
1. `gbase64`
- 增加`EncodeToString`/`EncodeFile`/`EncodeFileToString`/`DecodeToString`方法;
- 完善单元测试;
1. `gjson`
- 完善单元测试;
### `frame`
1. `g`/`gins`
- https://goframe.org/frame/g/index
- 增加`CreateVar`方法;
- 完善单元测试;
- 其他改进;
### `util`
1. `gconv`
- 改进优化部分类型转换方法性能;
- 增加`Uints`/`SliceUint`类型转换方法;
- 增加`UnsafeStrToBytes`/`UnsafeBytesToStr`高性能的类型转换方法;
- 增加对`MapStrAny`接口方法的支持,用于常见`map`类型的转换;
- 其他改进;
1. `gvalid`
- 改进对中国身份证号的识别校验功能;
- 增加`luhn`银行卡号的校验功能;
1. `grand`
- 一些性能改进工作;
## Bug Fix
1. 解决`WebSocket`关闭时的`hijacked`报错问题https://github.com/gogf/gf/issues/381
1. 解决静态文件服务时大文件的内存占用问题;
1. 修复前置`Nginx`后默认情况下的`Cookie`域名设置问题;
1. 修复`gconv.Struct`在属性为`[]struct`并且输入属性参数为空时的转换失败问题https://github.com/gogf/gf/issues/405
1. 其他一些修复;
# `v1.9.3` (2019-09-24)
该版本实际为`v2.0`的大版本发布,为避免`go module`机制严格要求`v2`版本以上需要修改`import`并加上`v2`后缀,因此使用了`v1.9`版本进行发布。

View File

@ -24,7 +24,7 @@ type Array struct {
}
// New creates and returns an empty array.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func New(safe ...bool) *Array {
return NewArraySize(0, 0, safe...)
@ -36,7 +36,7 @@ func NewArray(safe ...bool) *Array {
}
// NewArraySize create and returns an array with given size and cap.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewArraySize(size int, cap int, safe ...bool) *Array {
return &Array{
@ -56,7 +56,7 @@ func NewFromCopy(array []interface{}, safe ...bool) *Array {
}
// NewArrayFrom creates and returns an array with given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
return &Array{
@ -66,7 +66,7 @@ func NewArrayFrom(array []interface{}, safe ...bool) *Array {
}
// NewArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
newArray := make([]interface{}, len(array))

View File

@ -23,14 +23,14 @@ type IntArray struct {
}
// NewIntArray creates and returns an empty array.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewIntArray(safe ...bool) *IntArray {
return NewIntArraySize(0, 0, safe...)
}
// NewIntArraySize create and returns an array with given size and cap.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
return &IntArray{
@ -40,7 +40,7 @@ func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
}
// NewIntArrayFrom creates and returns an array with given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
return &IntArray{
@ -50,7 +50,7 @@ func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
}
// NewIntArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
newArray := make([]int, len(array))

View File

@ -25,14 +25,14 @@ type StrArray struct {
}
// NewStrArray creates and returns an empty array.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewStrArray(safe ...bool) *StrArray {
return NewStrArraySize(0, 0, safe...)
}
// NewStrArraySize create and returns an array with given size and cap.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
return &StrArray{
@ -42,7 +42,7 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
}
// NewStrArrayFrom creates and returns an array with given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
return &StrArray{
@ -52,7 +52,7 @@ func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
}
// NewStrArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
newArray := make([]string, len(array))

View File

@ -29,7 +29,7 @@ type SortedArray struct {
}
// NewSortedArray creates and returns an empty sorted array.
// The parameter <safe> used to specify whether using array in concurrent-safety, which is false in default.
// The parameter <safe> is used to specify whether using array in concurrent-safety, which is false in default.
// The parameter <comparator> used to compare values to sort in array,
// if it returns value < 0, means v1 < v2;
// if it returns value = 0, means v1 = v2;
@ -39,7 +39,7 @@ func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *Sorted
}
// NewSortedArraySize create and returns an sorted array with given size and cap.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
return &SortedArray{
@ -51,7 +51,7 @@ func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...
}
// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
a := NewSortedArraySize(0, comparator, safe...)
@ -63,7 +63,7 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i
}
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
newArray := make([]interface{}, len(array))

View File

@ -27,14 +27,14 @@ type SortedIntArray struct {
}
// NewSortedIntArray creates and returns an empty sorted array.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedIntArray(safe ...bool) *SortedIntArray {
return NewSortedIntArraySize(0, safe...)
}
// NewSortedIntArrayComparator creates and returns an empty sorted array with specified comparator.
// The parameter <safe> used to specify whether using array in concurrent-safety which is false in default.
// The parameter <safe> is used to specify whether using array in concurrent-safety which is false in default.
func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *SortedIntArray {
array := NewSortedIntArray(safe...)
array.comparator = comparator
@ -42,7 +42,7 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S
}
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
return &SortedIntArray{
@ -54,7 +54,7 @@ func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
}
// NewIntArrayFrom creates and returns an sorted array with given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray {
a := NewSortedIntArraySize(0, safe...)
@ -64,7 +64,7 @@ func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray {
}
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedIntArrayFromCopy(array []int, safe ...bool) *SortedIntArray {
newArray := make([]int, len(array))

View File

@ -28,14 +28,14 @@ type SortedStrArray struct {
}
// NewSortedStrArray creates and returns an empty sorted array.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedStrArray(safe ...bool) *SortedStrArray {
return NewSortedStrArraySize(0, safe...)
}
// NewSortedStrArrayComparator creates and returns an empty sorted array with specified comparator.
// The parameter <safe> used to specify whether using array in concurrent-safety which is false in default.
// The parameter <safe> is used to specify whether using array in concurrent-safety which is false in default.
func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool) *SortedStrArray {
array := NewSortedStrArray(safe...)
array.comparator = comparator
@ -43,7 +43,7 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool)
}
// NewSortedStrArraySize create and returns an sorted array with given size and cap.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
return &SortedStrArray{
@ -55,7 +55,7 @@ func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
}
// NewSortedStrArrayFrom creates and returns an sorted array with given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
a := NewSortedStrArraySize(0, safe...)
@ -65,7 +65,7 @@ func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
}
// NewSortedStrArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The parameter <safe> used to specify whether using array in concurrent-safety,
// The parameter <safe> is used to specify whether using array in concurrent-safety,
// which is false in default.
func NewSortedStrArrayFromCopy(array []string, safe ...bool) *SortedStrArray {
newArray := make([]string, len(array))

View File

@ -36,7 +36,7 @@ func New(safe ...bool) *List {
}
// NewFrom creates and returns a list from a copy of given slice <array>.
// The parameter <safe> used to specify whether using list in concurrent-safety,
// The parameter <safe> is used to specify whether using list in concurrent-safety,
// which is false in default.
func NewFrom(array []interface{}, safe ...bool) *List {
l := list.New()

View File

@ -11,33 +11,33 @@ package gmap
type Map = AnyAnyMap
type HashMap = AnyAnyMap
// New returns an empty hash map.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// New creates and returns an empty hash map.
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func New(safe ...bool) *Map {
return NewAnyAnyMap(safe...)
}
// NewFrom returns a hash map from given map <data>.
// NewFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewFrom(data map[interface{}]interface{}, safe ...bool) *Map {
return NewAnyAnyMapFrom(data, safe...)
}
// NewHashMap returns an empty hash map.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// NewHashMap creates and returns an empty hash map.
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewHashMap(safe ...bool) *Map {
return NewAnyAnyMap(safe...)
}
// NewHashMapFrom returns a hash map from given map <data>.
// NewHashMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewHashMapFrom(data map[interface{}]interface{}, safe ...bool) *Map {
return NewAnyAnyMapFrom(data, safe...)

View File

@ -22,8 +22,8 @@ type AnyAnyMap struct {
data map[interface{}]interface{}
}
// NewAnyAnyMap returns an empty hash map.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// NewAnyAnyMap creates and returns an empty hash map.
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
@ -32,7 +32,7 @@ func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
}
}
// NewAnyAnyMapFrom returns a hash map from given map <data>.
// NewAnyAnyMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
@ -75,7 +75,7 @@ func (m *AnyAnyMap) Map() map[interface{}]interface{} {
return data
}
// MapCopy returns a copy of the data of the hash map.
// MapCopy returns a copy of the underlying data of the hash map.
func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
m.mu.RLock()
defer m.mu.RUnlock()
@ -86,7 +86,7 @@ func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
@ -194,12 +194,14 @@ func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) inter
if f, ok := value.(func() interface{}); ok {
value = f()
}
m.data[key] = value
if value != nil {
m.data[key] = value
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -209,8 +211,8 @@ func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -220,8 +222,8 @@ func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interfac
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -257,7 +259,7 @@ func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
if !m.Contains(key) {
@ -267,7 +269,7 @@ func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !m.Contains(key) {
@ -277,7 +279,7 @@ func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) boo
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -366,6 +368,13 @@ func (m *AnyAnyMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *AnyAnyMap) Replace(data map[interface{}]interface{}) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
m.mu.Lock()

View File

@ -23,7 +23,7 @@ type IntAnyMap struct {
}
// NewIntAnyMap returns an empty IntAnyMap object.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewIntAnyMap(safe ...bool) *IntAnyMap {
return &IntAnyMap{
@ -32,7 +32,7 @@ func NewIntAnyMap(safe ...bool) *IntAnyMap {
}
}
// NewIntAnyMapFrom returns a hash map from given map <data>.
// NewIntAnyMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
@ -75,7 +75,7 @@ func (m *IntAnyMap) Map() map[int]interface{} {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *IntAnyMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
@ -86,7 +86,7 @@ func (m *IntAnyMap) MapStrAny() map[string]interface{} {
return data
}
// MapCopy returns a copy of the data of the hash map.
// MapCopy returns a copy of the underlying data of the hash map.
func (m *IntAnyMap) MapCopy() map[int]interface{} {
m.mu.RLock()
defer m.mu.RUnlock()
@ -201,7 +201,7 @@ func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -211,7 +211,7 @@ func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
// or sets value with returned value of callback function <f> if it does not exist and returns this value.
func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -221,7 +221,7 @@ func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
// or sets value with returned value of callback function <f> if it does not exist and returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -257,7 +257,7 @@ func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
if !m.Contains(key) {
@ -267,7 +267,7 @@ func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
if !m.Contains(key) {
@ -277,7 +277,7 @@ func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool {
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -366,6 +366,13 @@ func (m *IntAnyMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *IntAnyMap) Replace(data map[int]interface{}) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
m.mu.Lock()

View File

@ -21,7 +21,7 @@ type IntIntMap struct {
}
// NewIntIntMap returns an empty IntIntMap object.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewIntIntMap(safe ...bool) *IntIntMap {
return &IntIntMap{
@ -30,7 +30,7 @@ func NewIntIntMap(safe ...bool) *IntIntMap {
}
}
// NewIntIntMapFrom returns a hash map from given map <data>.
// NewIntIntMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewIntIntMapFrom(data map[int]int, safe ...bool) *IntIntMap {
@ -73,7 +73,7 @@ func (m *IntIntMap) Map() map[int]int {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *IntIntMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
@ -84,7 +84,7 @@ func (m *IntIntMap) MapStrAny() map[string]interface{} {
return data
}
// MapCopy returns a copy of the data of the hash map.
// MapCopy returns a copy of the underlying data of the hash map.
func (m *IntIntMap) MapCopy() map[int]int {
m.mu.RLock()
defer m.mu.RUnlock()
@ -191,7 +191,7 @@ func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *IntIntMap) GetOrSet(key int, value int) int {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -201,7 +201,7 @@ func (m *IntIntMap) GetOrSet(key int, value int) int {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
// or sets value with returned value of callback function <f> if it does not exist and returns this value.
func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -211,7 +211,7 @@ func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
// or sets value with returned value of callback function <f> if it does not exist and returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -230,7 +230,7 @@ func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntIntMap) SetIfNotExist(key int, value int) bool {
if !m.Contains(key) {
@ -240,7 +240,7 @@ func (m *IntIntMap) SetIfNotExist(key int, value int) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
if !m.Contains(key) {
@ -250,7 +250,7 @@ func (m *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool {
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -343,6 +343,13 @@ func (m *IntIntMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *IntIntMap) Replace(data map[int]int) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
m.mu.Lock()

View File

@ -21,7 +21,7 @@ type IntStrMap struct {
}
// NewIntStrMap returns an empty IntStrMap object.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewIntStrMap(safe ...bool) *IntStrMap {
return &IntStrMap{
@ -30,7 +30,7 @@ func NewIntStrMap(safe ...bool) *IntStrMap {
}
}
// NewIntStrMapFrom returns a hash map from given map <data>.
// NewIntStrMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
@ -73,7 +73,7 @@ func (m *IntStrMap) Map() map[int]string {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *IntStrMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
@ -84,7 +84,7 @@ func (m *IntStrMap) MapStrAny() map[string]interface{} {
return data
}
// MapCopy returns a copy of the data of the hash map.
// MapCopy returns a copy of the underlying data of the hash map.
func (m *IntStrMap) MapCopy() map[int]string {
m.mu.RLock()
defer m.mu.RUnlock()
@ -181,17 +181,16 @@ func (m *IntStrMap) Pops(size int) map[int]string {
// It returns value with given <key>.
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *IntStrMap) GetOrSet(key int, value string) string {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -201,7 +200,7 @@ func (m *IntStrMap) GetOrSet(key int, value string) string {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
// or sets value with returned value of callback function <f> if it does not exist and returns this value.
func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -211,7 +210,7 @@ func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string {
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
// or sets value with returned value of callback function <f> if it does not exist and returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -223,14 +222,16 @@ func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
return v
}
v = f()
m.data[key] = v
if v != "" {
m.data[key] = v
}
return v
} else {
return v
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntStrMap) SetIfNotExist(key int, value string) bool {
if !m.Contains(key) {
@ -240,7 +241,7 @@ func (m *IntStrMap) SetIfNotExist(key int, value string) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntStrMap) SetIfNotExistFunc(key int, f func() string) bool {
if !m.Contains(key) {
@ -250,7 +251,7 @@ func (m *IntStrMap) SetIfNotExistFunc(key int, f func() string) bool {
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -343,6 +344,13 @@ func (m *IntStrMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *IntStrMap) Replace(data map[int]string) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntStrMap) LockFunc(f func(m map[int]string)) {
m.mu.Lock()

View File

@ -23,7 +23,7 @@ type StrAnyMap struct {
}
// NewStrAnyMap returns an empty StrAnyMap object.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewStrAnyMap(safe ...bool) *StrAnyMap {
return &StrAnyMap{
@ -32,7 +32,7 @@ func NewStrAnyMap(safe ...bool) *StrAnyMap {
}
}
// NewStrAnyMapFrom returns a hash map from given map <data>.
// NewStrAnyMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
@ -75,12 +75,12 @@ func (m *StrAnyMap) Map() map[string]interface{} {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *StrAnyMap) MapStrAny() map[string]interface{} {
return m.Map()
}
// MapCopy returns a copy of the data of the hash map.
// MapCopy returns a copy of the underlying data of the hash map.
func (m *StrAnyMap) MapCopy() map[string]interface{} {
m.mu.RLock()
defer m.mu.RUnlock()
@ -195,7 +195,7 @@ func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -205,8 +205,8 @@ func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -216,8 +216,8 @@ func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -253,7 +253,7 @@ func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
if !m.Contains(key) {
@ -263,7 +263,7 @@ func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
if !m.Contains(key) {
@ -273,7 +273,7 @@ func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool {
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -362,6 +362,13 @@ func (m *StrAnyMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *StrAnyMap) Replace(data map[string]interface{}) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
m.mu.Lock()

View File

@ -21,7 +21,7 @@ type StrIntMap struct {
}
// NewStrIntMap returns an empty StrIntMap object.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewStrIntMap(safe ...bool) *StrIntMap {
return &StrIntMap{
@ -30,7 +30,7 @@ func NewStrIntMap(safe ...bool) *StrIntMap {
}
}
// NewStrIntMapFrom returns a hash map from given map <data>.
// NewStrIntMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
@ -73,7 +73,7 @@ func (m *StrIntMap) Map() map[string]int {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *StrIntMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
@ -84,7 +84,7 @@ func (m *StrIntMap) MapStrAny() map[string]interface{} {
return data
}
// MapCopy returns a copy of the data of the hash map.
// MapCopy returns a copy of the underlying data of the hash map.
func (m *StrIntMap) MapCopy() map[string]int {
m.mu.RLock()
defer m.mu.RUnlock()
@ -191,7 +191,7 @@ func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *StrIntMap) GetOrSet(key string, value int) int {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -201,8 +201,8 @@ func (m *StrIntMap) GetOrSet(key string, value int) int {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -212,8 +212,8 @@ func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int {
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -232,7 +232,7 @@ func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrIntMap) SetIfNotExist(key string, value int) bool {
if !m.Contains(key) {
@ -242,7 +242,7 @@ func (m *StrIntMap) SetIfNotExist(key string, value int) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool {
if !m.Contains(key) {
@ -252,7 +252,7 @@ func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool {
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -345,6 +345,13 @@ func (m *StrIntMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *StrIntMap) Replace(data map[string]int) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
m.mu.Lock()

View File

@ -21,7 +21,7 @@ type StrStrMap struct {
}
// NewStrStrMap returns an empty StrStrMap object.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewStrStrMap(safe ...bool) *StrStrMap {
return &StrStrMap{
@ -30,7 +30,7 @@ func NewStrStrMap(safe ...bool) *StrStrMap {
}
}
// NewStrStrMapFrom returns a hash map from given map <data>.
// NewStrStrMapFrom creates and returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap {
@ -73,7 +73,7 @@ func (m *StrStrMap) Map() map[string]string {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *StrStrMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
data := make(map[string]interface{}, len(m.data))
@ -84,7 +84,7 @@ func (m *StrStrMap) MapStrAny() map[string]interface{} {
return data
}
// MapCopy returns a copy of the data of the hash map.
// MapCopy returns a copy of the underlying data of the hash map.
func (m *StrStrMap) MapCopy() map[string]string {
m.mu.RLock()
defer m.mu.RUnlock()
@ -181,17 +181,16 @@ func (m *StrStrMap) Pops(size int) map[string]string {
// It returns value with given <key>.
func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *StrStrMap) GetOrSet(key string, value string) string {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -201,8 +200,8 @@ func (m *StrStrMap) GetOrSet(key string, value string) string {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -212,8 +211,8 @@ func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string {
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -225,14 +224,16 @@ func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
return v
}
v = f()
m.data[key] = v
if v != "" {
m.data[key] = v
}
return v
} else {
return v
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrStrMap) SetIfNotExist(key string, value string) bool {
if !m.Contains(key) {
@ -242,7 +243,7 @@ func (m *StrStrMap) SetIfNotExist(key string, value string) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *StrStrMap) SetIfNotExistFunc(key string, f func() string) bool {
if !m.Contains(key) {
@ -252,7 +253,7 @@ func (m *StrStrMap) SetIfNotExistFunc(key string, f func() string) bool {
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -345,6 +346,13 @@ func (m *StrStrMap) Clear() {
m.mu.Unlock()
}
// Replace the data of the map with given <data>.
func (m *StrStrMap) Replace(data map[string]string) {
m.mu.Lock()
m.data = data
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *StrStrMap) LockFunc(f func(m map[string]string)) {
m.mu.Lock()

View File

@ -31,7 +31,7 @@ type gListMapNode struct {
// NewListMap returns an empty link map.
// ListMap is backed by a hash table to store values and doubly-linked list to store ordering.
// The parameter <safe> used to specify whether using map in concurrent-safety,
// The parameter <safe> is used to specify whether using map in concurrent-safety,
// which is false in default.
func NewListMap(safe ...bool) *ListMap {
return &ListMap{
@ -92,7 +92,22 @@ func (m *ListMap) Clear() {
m.mu.Unlock()
}
// Map returns a copy of the data of the map.
// Replace the data of the map with given <data>.
func (m *ListMap) Replace(data map[interface{}]interface{}) {
m.mu.Lock()
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
for key, value := range data {
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
}
m.mu.Unlock()
}
// Map returns a copy of the underlying data of the map.
func (m *ListMap) Map() map[interface{}]interface{} {
m.mu.RLock()
node := (*gListMapNode)(nil)
@ -106,7 +121,7 @@ func (m *ListMap) Map() map[interface{}]interface{} {
return data
}
// MapStrAny returns a copy of the data of the map as map[string]interface{}.
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *ListMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
node := (*gListMapNode)(nil)
@ -246,12 +261,14 @@ func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interfa
if f, ok := value.(func() interface{}); ok {
value = f()
}
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
if value != nil {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (m *ListMap) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
@ -261,8 +278,8 @@ func (m *ListMap) GetOrSet(key interface{}, value interface{}) interface{} {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (m *ListMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
@ -272,8 +289,8 @@ func (m *ListMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the map.
@ -309,7 +326,7 @@ func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gv
return gvar.New(m.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *ListMap) SetIfNotExist(key interface{}, value interface{}) bool {
if !m.Contains(key) {
@ -319,7 +336,7 @@ func (m *ListMap) SetIfNotExist(key interface{}, value interface{}) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *ListMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !m.Contains(key) {
@ -329,7 +346,7 @@ func (m *ListMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that

View File

@ -14,7 +14,7 @@ import (
type TreeMap = gtree.RedBlackTree
// NewTreeMap instantiates a tree map with the custom comparator.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewTreeMap(comparator func(v1, v2 interface{}) int, safe ...bool) *TreeMap {
return gtree.NewRedBlackTree(comparator, safe...)
@ -23,7 +23,7 @@ func NewTreeMap(comparator func(v1, v2 interface{}) int, safe ...bool) *TreeMap
// NewTreeMapFrom instantiates a tree map with the custom comparator and <data> map.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewTreeMapFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *TreeMap {
return gtree.NewRedBlackTreeFrom(comparator, data, safe...)

View File

@ -44,7 +44,7 @@ type ExpireFunc func(interface{})
// New returns a new object pool.
// To ensure execution efficiency, the expiration time cannot be modified once it is set.
//
// Expiration logistics:
// Expiration logic:
// expire = 0 : not expired;
// expire < 0 : immediate expired after use;
// expire > 0 : timeout expired;

View File

@ -129,6 +129,8 @@ func (q *Queue) Close() {
}
// Len returns the length of the queue.
// Note that the result might not be accurate as there's a
// asynchronize channel reading the list constantly.
func (q *Queue) Len() (length int) {
if q.list != nil {
length += q.list.Len()

View File

@ -10,6 +10,7 @@ package gqueue_test
import (
"testing"
"time"
"github.com/gogf/gf/container/gqueue"
"github.com/gogf/gf/test/gtest"
@ -50,6 +51,7 @@ func TestQueue_Close(t *testing.T) {
q1 := gqueue.New()
q1.Push(1)
q1.Push(2)
time.Sleep(time.Millisecond)
gtest.Assert(q1.Len(), 2)
q1.Close()
}

View File

@ -21,7 +21,7 @@ type Set struct {
}
// New create and returns a new set, which contains un-repeated items.
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// The parameter <safe> is used to specify whether using set in concurrent-safety,
// which is false in default.
func New(safe ...bool) *Set {
return NewSet(safe...)
@ -109,7 +109,9 @@ func (set *Set) doAddWithLockCheck(item interface{}, value interface{}) interfac
item = value
}
}
set.data[item] = struct{}{}
if item != nil {
set.data[item] = struct{}{}
}
return item
}

View File

@ -20,7 +20,7 @@ type IntSet struct {
}
// New create and returns a new set, which contains un-repeated items.
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// The parameter <safe> is used to specify whether using set in concurrent-safety,
// which is false in default.
func NewIntSet(safe ...bool) *IntSet {
return &IntSet{

View File

@ -21,7 +21,7 @@ type StrSet struct {
}
// New create and returns a new set, which contains un-repeated items.
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// The parameter <safe> is used to specify whether using set in concurrent-safety,
// which is false in default.
func NewStrSet(safe ...bool) *StrSet {
return &StrSet{
@ -103,7 +103,9 @@ func (set *StrSet) doAddWithLockCheck(item string, value interface{}) string {
item = value.(string)
}
}
set.data[item] = struct{}{}
if item != "" {
set.data[item] = struct{}{}
}
return item
}

View File

@ -34,7 +34,7 @@ type AVLTreeNode struct {
}
// NewAVLTree instantiates an AVL tree with the custom key comparator.
// The parameter <safe> used to specify whether using tree in concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewAVLTree(comparator func(v1, v2 interface{}) int, safe ...bool) *AVLTree {
return &AVLTree{
@ -44,7 +44,7 @@ func NewAVLTree(comparator func(v1, v2 interface{}) int, safe ...bool) *AVLTree
}
// NewAVLTreeFrom instantiates an AVL tree with the custom key comparator and data map.
// The parameter <safe> used to specify whether using tree in concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *AVLTree {
tree := NewAVLTree(comparator, safe...)
@ -130,12 +130,14 @@ func (tree *AVLTree) doSetWithLockCheck(key interface{}, value interface{}) inte
if f, ok := value.(func() interface{}); ok {
value = f()
}
tree.put(key, value, nil, &tree.root)
if value != nil {
tree.put(key, value, nil, &tree.root)
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (tree *AVLTree) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, value)
@ -145,8 +147,8 @@ func (tree *AVLTree) GetOrSet(key interface{}, value interface{}) interface{} {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (tree *AVLTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f())
@ -156,8 +158,8 @@ func (tree *AVLTree) GetOrSetFunc(key interface{}, f func() interface{}) interfa
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -193,7 +195,7 @@ func (tree *AVLTree) GetVarOrSetFuncLock(key interface{}, f func() interface{})
return gvar.New(tree.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *AVLTree) SetIfNotExist(key interface{}, value interface{}) bool {
if !tree.Contains(key) {
@ -203,7 +205,7 @@ func (tree *AVLTree) SetIfNotExist(key interface{}, value interface{}) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *AVLTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
@ -213,7 +215,7 @@ func (tree *AVLTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bo
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -384,6 +386,17 @@ func (tree *AVLTree) Clear() {
tree.size = 0
}
// Replace the data of the tree with given <data>.
func (tree *AVLTree) Replace(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
for key, value := range data {
tree.put(key, value, nil, &tree.root)
}
}
// String returns a string representation of container
func (tree *AVLTree) String() string {
tree.mu.RLock()

View File

@ -41,7 +41,7 @@ type BTreeEntry struct {
}
// NewBTree instantiates a B-tree with <m> (maximum number of children) and a custom key comparator.
// The parameter <safe> used to specify whether using tree in concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
// Note that the <m> must be greater or equal than 3, or else it panics.
func NewBTree(m int, comparator func(v1, v2 interface{}) int, safe ...bool) *BTree {
@ -56,7 +56,7 @@ func NewBTree(m int, comparator func(v1, v2 interface{}) int, safe ...bool) *BTr
}
// NewBTreeFrom instantiates a B-tree with <m> (maximum number of children), a custom key comparator and data map.
// The parameter <safe> used to specify whether using tree in concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *BTree {
tree := NewBTree(m, comparator, safe...)
@ -128,12 +128,14 @@ func (tree *BTree) doSetWithLockCheck(key interface{}, value interface{}) interf
if f, ok := value.(func() interface{}); ok {
value = f()
}
tree.doSet(key, value)
if value != nil {
tree.doSet(key, value)
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (tree *BTree) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, value)
@ -143,8 +145,8 @@ func (tree *BTree) GetOrSet(key interface{}, value interface{}) interface{} {
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (tree *BTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f())
@ -154,8 +156,8 @@ func (tree *BTree) GetOrSetFunc(key interface{}, f func() interface{}) interface
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -191,7 +193,7 @@ func (tree *BTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *g
return gvar.New(tree.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *BTree) SetIfNotExist(key interface{}, value interface{}) bool {
if !tree.Contains(key) {
@ -201,7 +203,7 @@ func (tree *BTree) SetIfNotExist(key interface{}, value interface{}) bool {
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *BTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
@ -211,7 +213,7 @@ func (tree *BTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -322,6 +324,17 @@ func (tree *BTree) Clear() {
tree.size = 0
}
// Replace the data of the tree with given <data>.
func (tree *BTree) Replace(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
for k, v := range data {
tree.doSet(k, v)
}
}
// Height returns the height of the tree.
func (tree *BTree) Height() int {
tree.mu.RLock()

View File

@ -41,7 +41,7 @@ type RedBlackTreeNode struct {
}
// NewRedBlackTree instantiates a red-black tree with the custom key comparator.
// The parameter <safe> used to specify whether using tree in concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewRedBlackTree(comparator func(v1, v2 interface{}) int, safe ...bool) *RedBlackTree {
return &RedBlackTree{
@ -51,7 +51,7 @@ func NewRedBlackTree(comparator func(v1, v2 interface{}) int, safe ...bool) *Red
}
// NewRedBlackTreeFrom instantiates a red-black tree with the custom key comparator and <data> map.
// The parameter <safe> used to specify whether using tree in concurrent-safety,
// The parameter <safe> is used to specify whether using tree in concurrent-safety,
// which is false in default.
func NewRedBlackTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, safe ...bool) *RedBlackTree {
tree := NewRedBlackTree(comparator, safe...)
@ -170,12 +170,14 @@ func (tree *RedBlackTree) doSetWithLockCheck(key interface{}, value interface{})
if f, ok := value.(func() interface{}); ok {
value = f()
}
tree.doSet(key, value)
if value != nil {
tree.doSet(key, value)
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
// or sets value with given <value> if it does not exist and then returns this value.
func (tree *RedBlackTree) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, value)
@ -185,8 +187,8 @@ func (tree *RedBlackTree) GetOrSet(key interface{}, value interface{}) interface
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
func (tree *RedBlackTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := tree.Search(key); !ok {
return tree.doSetWithLockCheck(key, f())
@ -196,8 +198,8 @@ func (tree *RedBlackTree) GetOrSetFunc(key interface{}, f func() interface{}) in
}
// GetOrSetFuncLock returns the value by key,
// or sets value with return value of callback function <f> if not exist
// and returns this value.
// or sets value with returned value of callback function <f> if it does not exist
// and then returns this value.
//
// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function <f>
// with mutex.Lock of the hash map.
@ -233,7 +235,7 @@ func (tree *RedBlackTree) GetVarOrSetFuncLock(key interface{}, f func() interfac
return gvar.New(tree.GetOrSetFuncLock(key, f))
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// SetIfNotExist sets <value> to the map if the <key> does not exist, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *RedBlackTree) SetIfNotExist(key interface{}, value interface{}) bool {
if !tree.Contains(key) {
@ -243,7 +245,7 @@ func (tree *RedBlackTree) SetIfNotExist(key interface{}, value interface{}) bool
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
// SetIfNotExistFunc sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
func (tree *RedBlackTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool {
if !tree.Contains(key) {
@ -253,7 +255,7 @@ func (tree *RedBlackTree) SetIfNotExistFunc(key interface{}, f func() interface{
return false
}
// SetIfNotExistFuncLock sets value with return value of callback function <f>, then return true.
// SetIfNotExistFuncLock sets value with return value of callback function <f>, and then returns true.
// It returns false if <key> exists, and <value> would be ignored.
//
// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that
@ -603,6 +605,17 @@ func (tree *RedBlackTree) Clear() {
tree.size = 0
}
// Replace the data of the tree with given <data>.
func (tree *RedBlackTree) Replace(data map[interface{}]interface{}) {
tree.mu.Lock()
defer tree.mu.Unlock()
tree.root = nil
tree.size = 0
for k, v := range data {
tree.doSet(k, v)
}
}
// String returns a string representation of container.
func (tree *RedBlackTree) String() string {
tree.mu.RLock()

View File

@ -88,6 +88,7 @@ type DB interface {
GetLastSql() *Sql
PrintQueriedSqls()
SetLogger(logger *glog.Logger)
GetLogger() *glog.Logger
SetMaxIdleConnCount(n int)
SetMaxOpenConnCount(n int)
SetMaxConnLifetime(d time.Duration)
@ -199,7 +200,7 @@ func New(name ...string) (db DB, err error) {
debug: gtype.NewBool(),
cache: gcache.New(),
schema: gtype.NewString(),
logger: glog.Default(),
logger: glog.New(),
maxConnLifetime: gDEFAULT_CONN_MAX_LIFE_TIME,
}
switch node.Type {

View File

@ -50,16 +50,16 @@ func (bs *dbBase) GetQueriedSqls() []*Sql {
if bs.sqls == nil {
return nil
}
sqls := make([]*Sql, 0)
array := make([]*Sql, 0)
bs.sqls.Prev()
bs.sqls.RLockIteratorPrev(func(value interface{}) bool {
if value == nil {
return false
}
sqls = append(sqls, value.(*Sql))
array = append(array, value.(*Sql))
return true
})
return sqls
return array
}
// 打印已经执行的SQL列表(仅在debug=true时有效)
@ -219,6 +219,9 @@ func (bs *dbBase) GetStruct(pointer interface{}, query string, args ...interface
if err != nil {
return err
}
if len(one) == 0 {
return sql.ErrNoRows
}
return one.Struct(pointer)
}
@ -228,6 +231,9 @@ func (bs *dbBase) GetStructs(pointer interface{}, query string, args ...interfac
if err != nil {
return err
}
if len(all) == 0 {
return sql.ErrNoRows
}
return all.Structs(pointer)
}
@ -597,7 +603,7 @@ func (bs *dbBase) getCache() *gcache.Cache {
// 将数据查询的列表数据*sql.Rows转换为Result类型
func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
if !rows.Next() {
return nil, sql.ErrNoRows
return nil, nil
}
// 列信息列表, 名称与类型
columnTypes, err := rows.ColumnTypes()
@ -631,7 +637,6 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
// 由于 sql.RawBytes 是slice类型, 这里必须使用值复制
v := make([]byte, len(column))
copy(v, column)
//fmt.Println(columns[i], types[i], string(v), v, bs.db.convertValue(v, types[i]))
row[columns[i]] = gvar.New(bs.db.convertValue(v, types[i]))
}
}

View File

@ -119,6 +119,11 @@ func (bs *dbBase) SetLogger(logger *glog.Logger) {
bs.logger = logger
}
// 获得数据库Logger对象
func (bs *dbBase) GetLogger() *glog.Logger {
return bs.logger
}
// 设置数据库连接池中空闲链接的大小
func (bs *dbBase) SetMaxIdleConnCount(n int) {
bs.maxIdleConnCount = n
@ -140,7 +145,9 @@ func (node *ConfigNode) String() string {
if node.LinkInfo != "" {
return node.LinkInfo
}
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d`, node.User, node.Host, node.Port,
return fmt.Sprintf(
`%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d`,
node.User, node.Host, node.Port,
node.Name, node.Type, node.Role, node.Charset, node.Debug,
node.MaxIdleConnCount, node.MaxOpenConnCount, node.MaxConnLifetime,
)

View File

@ -12,6 +12,7 @@ import (
"errors"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/os/gtime"
"reflect"
"strings"
"time"
@ -260,10 +261,8 @@ func handlerSliceArguments(query string, args []interface{}) (newQuery string, n
switch kind {
// '?'占位符支持slice类型, 这里会将slice参数拆散并更新原有占位符'?'为多个'?',使用','符号连接。
case reflect.Slice, reflect.Array:
if rv.Len() == 0 {
continue
}
// 不拆分[]byte类型
// 不拆分[]byte类型(当做字符串处理)
// Eg: table.Where("name = ?", []byte("john"))
if _, ok := arg.([]byte); ok {
newArgs = append(newArgs, arg)
continue
@ -273,6 +272,7 @@ func handlerSliceArguments(query string, args []interface{}) (newQuery string, n
}
// 如果参数直接传递slice并且占位符数量与slice长度相等
// 那么不用替换扩展占位符数量直接使用该slice作为查询参数
// Eg: db.Query("SELECT ?+?", g.Slice{1, 2})
if len(args) == 1 && gstr.Count(newQuery, "?") == rv.Len() {
break
}
@ -364,6 +364,11 @@ func bindArgsToQuery(query string, args []interface{}) string {
switch kind {
case reflect.String, reflect.Map, reflect.Slice, reflect.Array:
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
case reflect.Struct:
if t, ok := args[index].(time.Time); ok {
return `'` + gtime.NewFromTime(t).String() + `'`
}
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
}
return gconv.String(args[index])
}

View File

@ -30,7 +30,7 @@ type Model struct {
tablesInit string // 初始化Model时的表名称(可以是多个)
tables string // 数据库操作表
fields string // 操作字段
where string // 操作条件
fieldsEx string // 操作字段(排除)
whereArgs []interface{} // 操作条件参数
whereHolder []*whereHolder // 操作条件预处理
groupBy string // 分组语句
@ -113,6 +113,15 @@ func (md *Model) Clone() *Model {
newModel = md.db.Table(md.tablesInit)
}
*newModel = *md
// Deep copy slice attributes.
if n := len(md.whereArgs); n > 0 {
newModel.whereArgs = make([]interface{}, n)
copy(newModel.whereArgs, md.whereArgs)
}
if n := len(md.whereHolder); n > 0 {
newModel.whereHolder = make([]*whereHolder, n)
copy(newModel.whereHolder, md.whereHolder)
}
return newModel
}
@ -183,6 +192,26 @@ func (md *Model) Fields(fields string) *Model {
return model
}
// 链式操作,查询字段(排除)
func (md *Model) FieldsEx(fields string) *Model {
model := md.getModel()
model.fieldsEx = fields
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
if m, err := md.db.TableFields(md.tables); err == nil {
model.fields = ""
for k, _ := range m {
if fieldsExSet.Contains(k) {
continue
}
if len(model.fields) > 0 {
model.fields += ","
}
model.fields += k
}
}
return model
}
// 链式操作,选项设置
func (md *Model) Option(option int) *Model {
model := md.getModel()
@ -306,13 +335,9 @@ func (md *Model) Batch(batch int) *Model {
// 2. expire参数类型为interface{},这是一个兼容旧版本的方式,该参数支持 int/time.Duration 类型当传递类型为int时表示缓存多少秒。
// 3. name表示自定义的缓存名称注意不要出现重复便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称)
// 例如:查询缓存时设置名称,在特定的业务逻辑中清理缓存时可以给定缓存名称进行精准清理。
func (md *Model) Cache(expire interface{}, name ...string) *Model {
func (md *Model) Cache(expire time.Duration, name ...string) *Model {
model := md.getModel()
if d, ok := expire.(time.Duration); ok {
model.cacheExpire = d
} else {
model.cacheExpire = gconv.Duration(expire) * time.Second
}
model.cacheExpire = expire
if len(name) > 0 {
model.cacheName = name[0]
}
@ -393,17 +418,20 @@ func (md *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
m.FilterEmpty()
data = m.Map()
}
// Keep specified fields.
if len(md.fields) > 0 && md.fields != "*" {
set := gset.NewStrSet()
for _, v := range gstr.SplitAndTrimSpace(md.fields, ",") {
set.Add(v)
}
for k, _ := range data {
// Keep specified fields.
set := gset.NewStrSetFrom(gstr.SplitAndTrim(md.fields, ","))
for k := range data {
if !set.Contains(k) {
delete(data, k)
}
}
} else if len(md.fieldsEx) > 0 {
// Filter specified fields.
for _, v := range gstr.SplitAndTrim(md.fieldsEx, ",") {
delete(data, v)
}
}
return data
}
@ -526,12 +554,13 @@ func (md *Model) Update() (result sql.Result, err error) {
if md.data == nil {
return nil, errors.New("updating table with empty data")
}
condition, conditionArgs := md.formatCondition()
return md.db.doUpdate(
md.getLink(),
md.tables,
md.filterDataForInsertOrUpdate(md.data),
md.getConditionSql(),
md.whereArgs...,
condition,
conditionArgs...,
)
}
@ -542,7 +571,8 @@ func (md *Model) Delete() (result sql.Result, err error) {
md.checkAndRemoveCache()
}
}()
return md.db.doDelete(md.getLink(), md.tables, md.getConditionSql(), md.whereArgs...)
condition, conditionArgs := md.formatCondition()
return md.db.doDelete(md.getLink(), md.tables, condition, conditionArgs...)
}
// 链式操作select
@ -552,7 +582,8 @@ func (md *Model) Select() (Result, error) {
// 链式操作,查询所有记录
func (md *Model) All() (Result, error) {
return md.getAll(fmt.Sprintf("SELECT %s FROM %s%s", md.fields, md.tables, md.getConditionSql()), md.whereArgs...)
condition, conditionArgs := md.formatCondition()
return md.getAll(fmt.Sprintf("SELECT %s FROM %s%s", md.fields, md.tables, condition), conditionArgs...)
}
// 链式操作,查询单条记录
@ -585,16 +616,22 @@ func (md *Model) Struct(pointer interface{}) error {
if err != nil {
return err
}
if len(one) == 0 {
return sql.ErrNoRows
}
return one.Struct(pointer)
}
// 链式操作查询多条记录并自动转换为指定的slice对象, 如: []struct/[]*struct。
func (md *Model) Structs(pointer interface{}) error {
r, err := md.All()
all, err := md.All()
if err != nil {
return err
}
return r.Structs(pointer)
if len(all) == 0 {
return sql.ErrNoRows
}
return all.Structs(pointer)
}
// 链式操作将结果转换为指定的struct/*struct/[]struct/[]*struct,
@ -627,11 +664,12 @@ func (md *Model) Count() (int, error) {
} else {
md.fields = fmt.Sprintf(`COUNT(%s)`, md.fields)
}
s := fmt.Sprintf("SELECT %s FROM %s %s", md.fields, md.tables, md.getConditionSql())
condition, conditionArgs := md.formatCondition()
s := fmt.Sprintf("SELECT %s FROM %s %s", md.fields, md.tables, condition)
if len(md.groupBy) > 0 {
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
}
list, err := md.getAll(s, md.whereArgs...)
list, err := md.getAll(s, conditionArgs...)
if err != nil {
return 0, err
}
@ -691,17 +729,19 @@ func (md *Model) checkAndRemoveCache() {
}
}
// 格式化当前输入参数返回SQL条件语句不带参数
func (md *Model) getConditionSql() string {
// formatCondition formats where arguments of the model and returns a new condition sql and its arguments.
// Note that this function does not change any of the attribute value of the mode.
func (md *Model) formatCondition() (condition string, conditionArgs []interface{}) {
var where string
if len(md.whereHolder) > 0 {
for _, v := range md.whereHolder {
switch v.operator {
case gWHERE_HOLDER_WHERE:
if md.where == "" {
if where == "" {
newWhere, newArgs := formatWhere(md.db, v.where, v.args, md.option&OPTION_OMITEMPTY > 0)
if len(newWhere) > 0 {
md.where = newWhere
md.whereArgs = newArgs
where = newWhere
conditionArgs = newArgs
}
continue
}
@ -710,48 +750,47 @@ func (md *Model) getConditionSql() string {
case gWHERE_HOLDER_AND:
newWhere, newArgs := formatWhere(md.db, v.where, v.args, md.option&OPTION_OMITEMPTY > 0)
if len(newWhere) > 0 {
if md.where[0] == '(' {
md.where = fmt.Sprintf(`%s AND (%s)`, md.where, newWhere)
if where[0] == '(' {
where = fmt.Sprintf(`%s AND (%s)`, where, newWhere)
} else {
md.where = fmt.Sprintf(`(%s) AND (%s)`, md.where, newWhere)
where = fmt.Sprintf(`(%s) AND (%s)`, where, newWhere)
}
md.whereArgs = append(md.whereArgs, newArgs...)
conditionArgs = append(conditionArgs, newArgs...)
}
case gWHERE_HOLDER_OR:
newWhere, newArgs := formatWhere(md.db, v.where, v.args, md.option&OPTION_OMITEMPTY > 0)
if len(newWhere) > 0 {
if md.where[0] == '(' {
md.where = fmt.Sprintf(`%s OR (%s)`, md.where, newWhere)
if where[0] == '(' {
where = fmt.Sprintf(`%s OR (%s)`, where, newWhere)
} else {
md.where = fmt.Sprintf(`(%s) OR (%s)`, md.where, newWhere)
where = fmt.Sprintf(`(%s) OR (%s)`, where, newWhere)
}
md.whereArgs = append(md.whereArgs, newArgs...)
conditionArgs = append(conditionArgs, newArgs...)
}
}
}
}
s := ""
if md.where != "" {
s += " WHERE " + md.where
if where != "" {
condition += " WHERE " + where
}
if md.groupBy != "" {
s += " GROUP BY " + md.groupBy
condition += " GROUP BY " + md.groupBy
}
if md.orderBy != "" {
s += " ORDER BY " + md.orderBy
condition += " ORDER BY " + md.orderBy
}
if md.limit != 0 {
if md.start >= 0 {
s += fmt.Sprintf(" LIMIT %d,%d", md.start, md.limit)
condition += fmt.Sprintf(" LIMIT %d,%d", md.start, md.limit)
} else {
s += fmt.Sprintf(" LIMIT %d", md.limit)
condition += fmt.Sprintf(" LIMIT %d", md.limit)
}
}
if md.offset >= 0 {
s += fmt.Sprintf(" OFFSET %d", md.offset)
condition += fmt.Sprintf(" OFFSET %d", md.offset)
}
return s
return
}
// 组块结果集。

View File

@ -24,7 +24,7 @@ func (db *dbMysql) Open(config *ConfigNode) (*sql.DB, error) {
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
}
if db, err := sql.Open("gf-mysql", source); err == nil {

View File

@ -20,7 +20,7 @@ import (
"github.com/gogf/gf/util/gconv"
)
// 字段类型转换将数据库字段类型转换为golang变量类型
// convertValue converts field value from database type to golang variable type.
func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} {
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
@ -45,7 +45,7 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{}
case "bit":
s := string(fieldValue)
// 这里的字符串判断是为兼容不同的数据库类型,如: mssql
// mssql is true|false string.
if strings.EqualFold(s, "true") {
return 1
}
@ -57,16 +57,17 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{}
case "bool":
return gconv.Bool(fieldValue)
case "datetime":
case "date":
t, _ := gtime.StrToTime(string(fieldValue))
return t.Format("Y-m-d")
case "datetime", "timestamp":
t, _ := gtime.StrToTime(string(fieldValue))
return t.String()
default:
// 自动识别类型, 以便默认支持更多数据库类型
// Auto detect field type, using key match.
switch {
case strings.Contains(t, "int"):
return gconv.Int(string(fieldValue))
case strings.Contains(t, "text") || strings.Contains(t, "char"):
return string(fieldValue)
@ -79,6 +80,17 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{}
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
return fieldValue
case strings.Contains(t, "int"):
return gconv.Int(string(fieldValue))
case strings.Contains(t, "time"):
t, _ := gtime.StrToTime(string(fieldValue))
return t.String()
case strings.Contains(t, "date"):
t, _ := gtime.StrToTime(string(fieldValue))
return t.Format("Y-m-d")
default:
return string(fieldValue)
}
@ -87,7 +99,7 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{}
// 将map的数据按照fields进行过滤只保留与表字段同名的数据
func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[string]interface{} {
// Must use data copy avoiding change the origin data map.
// It must use data copy here to avoid changing the origin data map.
newDataMap := make(map[string]interface{}, len(data))
if fields, err := bs.db.TableFields(table); err == nil {
for k, v := range data {

View File

@ -46,3 +46,8 @@ func (r Record) Struct(pointer interface{}) error {
}
return mapToStruct(r.Map(), pointer)
}
// IsEmpty checks and returns whether <r> is empty.
func (r Record) IsEmpty() bool {
return len(r) == 0
}

View File

@ -131,3 +131,8 @@ func (r Result) Structs(pointer interface{}) (err error) {
reflect.ValueOf(pointer).Elem().Set(array)
return nil
}
// IsEmpty checks and returns whether <r> is empty.
func (r Result) IsEmpty() bool {
return len(r) == 0
}

Some files were not shown because too many files have changed in this diff Show More