diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index 1805c879f9e1adca4aaf37b6c87942c5eafb8d65..a01ba4cd053a1b14c4406f88d4cbd19d37b6ed3a 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -122,19 +122,20 @@ type Map = map[string]interface{} // 关联数组列表(索引从0开始的数组),绑定多条记录(使用别名) type List = []Map -// MySQL接口对象 -var linkMysql = &dbmysql{} - -// PostgreSQL接口对象 -var linkPgsql = &dbpgsql{} - -// Sqlite接口对象 -// @author wxkj -var linkSqlite = &dbsqlite{} +var driverLink map[string]interface{} // 数据库查询缓存对象map,使用数据库连接名称作为键名,键值为查询缓存对象 var dbCaches = gmap.NewStringInterfaceMap() + +func init() { + driverLink = make(map[string]interface{}) + driverLink["mysql"] = linkMysql + driverLink["oracle"] = linkOracle + driverLink["sqllite"] = linkSqlite + driverLink["pgsql"] = linkPgsql +} + // 使用默认/指定分组配置进行连接,数据库集群配置项:default func New(groupName ...string) (*Db, error) { group := config.d @@ -239,15 +240,10 @@ func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode { // 根据配置的数据库;类型获得Link接口对象 func getLinkByType(dbType string) (Link, error) { - switch dbType { - case "mysql": - return linkMysql, nil - case "pgsql": - return linkPgsql, nil - case "sqlite": - return linkSqlite, nil - default: - return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", dbType)) + if dblink, ok := driverLink[dbType]; ok == false { + return nil, errors.New(fmt.Sprintf("unsupported db type '%s'", dbType)) + } else { + return dblink.(Link), nil } } diff --git a/g/database/gdb/gdb_mysql.go b/g/database/gdb/gdb_mysql.go index d6b539a5946d35f5c84a0cf95acd32b8d7a05faf..76775312bb89e6f71d06238844c529ec77a693ab 100644 --- a/g/database/gdb/gdb_mysql.go +++ b/g/database/gdb/gdb_mysql.go @@ -12,6 +12,10 @@ import ( "database/sql" ) +// MySQL接口对象 +var linkMysql = &dbmysql{} + + // 数据库链接对象 type dbmysql struct { Db diff --git a/g/database/gdb/gdb_oracle.go b/g/database/gdb/gdb_oracle.go new file mode 100644 index 0000000000000000000000000000000000000000..9e1b99409e24970f6e2a7a15fd21c70323bdacac --- /dev/null +++ b/g/database/gdb/gdb_oracle.go @@ -0,0 +1,149 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gf. +/* +@author wenzi1 +@date 20181026 +说明: + 1.需要导入oracle驱动: github.com/mattn/go-oci8 + 2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究) + 3.不支持LastInsertId方法 +*/ +package gdb + +import ( + "database/sql" + "fmt" + "gitee.com/johng/gf/g/util/gregex" + "strconv" + "strings" +) + +var linkOracle = &dboracle{} + +// 数据库链接对象 +type dboracle struct { + Db +} + +// 创建SQL操作对象 +func (db *dboracle) Open(c *ConfigNode) (*sql.DB, error) { + var source string + if c.Linkinfo != "" { + source = c.Linkinfo + } else { + source = fmt.Sprintf("%s/%s@%s", c.User, c.Pass, c.Name) + } + if db, err := sql.Open("oci8", source); err == nil { + return db, nil + } else { + return nil, err + } +} + +// 获得关键字操作符 - 左 +func (db *dboracle) getQuoteCharLeft() string { + return "\"" +} + +// 获得关键字操作符 - 右 +func (db *dboracle) getQuoteCharRight() string { + return "\"" +} + +// 在执行sql之前对sql进行进一步处理 +func (db *dboracle) handleSqlBeforeExec(q *string) *string { + index := 0 + str, _ := gregex.ReplaceStringFunc("\\?", *q, func(s string) string { + index++ + return fmt.Sprintf(":%d", index) + }) + + str, _ = gregex.ReplaceString("\"", "", str) + + return db.parseSql(&str) +} + +//由于ORACLE中对LIMIT和批量插入的语法与MYSQL不一致,所以这里需要对LIMIT和批量插入做语法上的转换 +func (db *dboracle) parseSql(sql *string) *string { + //下面的正则表达式匹配出SELECT和INSERT的关键字后分别做不同的处理,如有LIMIT则将LIMIT的关键字也匹配出 + patten := `^\s*(?i)(SELECT)|(INSERT)|(LIMIT\s*(\d+)\s*,\s*(\d+))` + if gregex.IsMatchString(patten, *sql) == false { + fmt.Println("not matched..") + return sql + } + + res, err := gregex.MatchAllString(patten, *sql) + if err != nil { + fmt.Println("MatchString error.", err) + return nil + } + + index := 0 + keyword := strings.TrimSpace(res[index][0]) + keyword = strings.ToUpper(keyword) + + index++ + switch keyword { + case "SELECT": + //不含LIMIT关键字则不处理 + if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) { + break + } + + //取limit前面的字符串 + if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", *sql) == false { + break + } + + queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", *sql) + queryExpr[0] = strings.TrimRight(queryExpr[0], "LIMIT") + queryExpr[0] = strings.TrimRight(queryExpr[0], "limit") + + //取limit后面的取值范围 + first, limit := 0, 0 + for i := 1; i < len(res[index]); i++ { + if len(strings.TrimSpace(res[index][i])) == 0 { + continue + } + + if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") { + first, _ = strconv.Atoi(res[index][i+1]) + limit, _ = strconv.Atoi(res[index][i+2]) + break + } + } + + //也可以使用between,据说这种写法的性能会比between好点,里层SQL中的ROWNUM_ >= limit可以缩小查询后的数据集规模 + *sql = fmt.Sprintf("SELECT * FROM (SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s) GFORM WHERE ROWNUM <= %d) WHERE ROWNUM_ >= %d", queryExpr[0], limit, first) + case "INSERT": + //获取VALUE的值,匹配所有带括号的值,会将INSERT INTO后的值匹配到,所以下面的判断语句会判断数组长度是否小于3 + valueExpr, err := gregex.MatchAllString(`(\s*\(([^\(\)]*)\))`, *sql) + if err != nil { + return sql + } + + //判断VALUE后的值是否有多个,只有在批量插入的时候才需要做转换,如只有1个VALUE则不需要做转换 + if len(valueExpr) < 3 { + break + } + + //获取INTO后面的值 + tableExpr, err := gregex.MatchString(`(?i)\s*(INTO\s+\w+\(([^\(\)]*)\))`, *sql) + if err != nil { + return sql + } + tableExpr[0] = strings.TrimSpace(tableExpr[0]) + + *sql = "INSERT ALL" + for i := 1; i < len(valueExpr); i++ { + *sql += fmt.Sprintf(" %s VALUES%s", tableExpr[0], strings.TrimSpace(valueExpr[i][0])) + } + *sql += " SELECT 1 FROM DUAL" + + default: + } + return sql +} diff --git a/g/database/gdb/gdb_pgsql.go b/g/database/gdb/gdb_pgsql.go index e370541fa4106a2c0878c3917d42287e4c873369..a16a1efe4032ec739e1c4ba181aef177d3e37c91 100644 --- a/g/database/gdb/gdb_pgsql.go +++ b/g/database/gdb/gdb_pgsql.go @@ -18,6 +18,10 @@ import ( // _ "gitee.com/johng/gf/third/github.com/lib/pq" // @todo 需要完善replace和save的操作覆盖 +// PostgreSQL接口对象 +var linkPgsql = &dbpgsql{} + + // 数据库链接对象 type dbpgsql struct { Db @@ -57,5 +61,4 @@ func (db *dbpgsql) handleSqlBeforeExec(q *string) *string { return fmt.Sprintf("$%d", index) }) return &str -} - +} \ No newline at end of file diff --git a/g/database/gdb/gdb_sqlite.go b/g/database/gdb/gdb_sqlite.go index 6f5d91da9cb2d8377a83757558803f0a5bd9517b..0a92972bb2cd876d3a4d5ee4052741bf48f4b802 100644 --- a/g/database/gdb/gdb_sqlite.go +++ b/g/database/gdb/gdb_sqlite.go @@ -14,6 +14,11 @@ import ( // 使用时需要import: // _ "gitee.com/johng/gf/third/github.com/mattn/go-sqlite3" +// Sqlite接口对象 +// @author wxkj +var linkSqlite = &dbsqlite{} + + // 数据库链接对象 type dbsqlite struct { Db @@ -49,4 +54,4 @@ func (db *dbsqlite) getQuoteCharRight() string { func (db *dbsqlite) handleSqlBeforeExec(q *string) *string { return q -} +} \ No newline at end of file diff --git a/geg/database/orm/oracle/gdb.go b/geg/database/orm/oracle/gdb.go new file mode 100644 index 0000000000000000000000000000000000000000..4accdc246cb8ef5e72abbed8f66ba64f44cb30b8 --- /dev/null +++ b/geg/database/orm/oracle/gdb.go @@ -0,0 +1,580 @@ +package main + +import ( + "fmt" + "time" + _ "github.com/mattn/go-oci8" + "gitee.com/johng/gf/g/database/gdb" + "gitee.com/johng/gf/g" +) + +// 本文件用于gf框架的mysql数据库操作示例,不作为单元测试使用 + +var db *gdb.Db + +// 初始化配置及创建数据库 +func init () { + gdb.AddDefaultConfigNode(gdb.ConfigNode { + Host : "192.168.146.0", + Port : "1521", + User : "test", + Pass : "test", + Name : "orcl", + Type : "oracle", + Role : "master", + Charset : "utf8", + }) + db, _= gdb.New() + + //gins.Config().SetPath("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/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", + // Priority : 100, + // }, + // gdb.ConfigNode { + // Host : "127.0.0.2", + // Port : "3306", + // User : "root", + // Pass : "123456", + // Name : "test", + // Type : "mysql", + // Role : "master", + // Priority : 100, + // }, + // gdb.ConfigNode { + // Host : "127.0.0.3", + // Port : "3306", + // User : "root", + // Pass : "123456", + // Name : "test", + // Type : "mysql", + // Role : "master", + // Priority : 100, + // }, + // gdb.ConfigNode { + // Host : "127.0.0.4", + // Port : "3306", + // User : "root", + // Pass : "123456", + // Name : "test", + // Type : "mysql", + // Role : "master", + // Priority : 100, + // }, + // }, + //}) + //db, _ = gdb.Instance() +} + + + +// 创建测试数据库 +func create() error { + fmt.Println("drop table aa_user:") + _, err := db.Exec("drop table aa_user") + if err != nil { + fmt.Println("drop table aa_user error.",err) + } + + s := ` + CREATE TABLE aa_user ( + id number(10) not null, + name VARCHAR2(45), + age number(8), + addr varchar2(60), + PRIMARY KEY (id) + ) + ` + fmt.Println("create table aa_user:") + _, err = db.Exec(s) + if err != nil { + fmt.Println("create table error.",err) + return err + } + + _, err = db.Exec("drop sequence id_seq") + if err != nil { + fmt.Println("drop sequence id_seq", err) + } + + /*fmt.Println("create sequence id_seq") + _, err = db.Exec("create sequence id_seq increment by 1 start with 1 maxvalue 9999999999 cycle cache 10") + if err != nil { + fmt.Println("create sequence id_seq error.", err) + return err + } + + s = ` + CREATE TRIGGER id_trigger before insert on aa_user for each row + begin + select id_seq.nextval into :new.id from dual; + end; + ` + _, err = db.Exec(s) + if err != nil { + fmt.Println("create trigger error.", err) + return err + }*/ + + _, err = db.Exec("drop table user_detail") + if err != nil { + fmt.Println("drop table user_detail", err) + } + + s = ` + CREATE TABLE user_detail ( + id number(10) not null, + site VARCHAR2(255), + PRIMARY KEY (id) + ) + ` + fmt.Println("create table user_detail:") + _, err = db.Exec(s) + if err != nil { + fmt.Println("create table user_detail error.",err) + return err + } + fmt.Println("create table success.") + return nil +} + +// 数据写入 +func insert(id int) { + fmt.Println("insert:") + r, err := db.Insert("aa_user", gdb.Map { + "id": id, + "name": "john", + "age": id, + }) + fmt.Println(r.LastInsertId()) + fmt.Println(r.RowsAffected()) + if err == nil { + r, err = db.Insert("user_detail", gdb.Map { + "id" : id, + "site" : "http://johng.cn", + }) + if err == nil { + fmt.Printf("id: %d\n", id) + } else { + fmt.Println(err) + } + + } else { + fmt.Println(err) + } + fmt.Println() +} + + +// 基本sql查询 +func query() { + fmt.Println("query:") + list, err := db.GetAll("select * from aa_user") + if err == nil { + fmt.Println(list) + } else { + fmt.Println(err) + } + + list, err = db.Table("aa_user").OrderBy("id").Limit(0,2).Select() + if err == nil { + fmt.Println(list) + } else { + fmt.Println(err) + } + fmt.Println() +} + +// replace into +func replace() { + fmt.Println("replace:") + r, err := db.Save("aa_user", gdb.Map { + "id" : 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("aa_user", gdb.Map { + "id" : 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("aa_user", gdb.List { + {"id":11,"name": "batchInsert_john_1", "age": 11}, + {"id":12,"name": "batchInsert_john_2", "age": 12}, + {"id":13,"name": "batchInsert_john_3", "age": 13}, + {"id":14,"name": "batchInsert_john_4", "age": 14}, + }, 10) + if err != nil { + fmt.Println(err) + } + fmt.Println() +} + +// 数据更新 +func update1() { + fmt.Println("update1:") + r, err := db.Update("aa_user", gdb.Map {"name": "john1","age":1}, "id=?", 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("aa_user", gdb.Map{"name" : "john6","age":6}, "id=?", 2) + 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("aa_user", "name=?", "id=?", "john2", 3) + 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("aa_user u").LeftJoin("user_detail ud", "u.id=ud.id").Fields("u.*, ud.site").Where("u.id > ?", 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("aa_user u").LeftJoin("user_detail ud", "u.id=ud.id").Fields("u.*,ud.site").Where("u.id=?", 1).One() + if err == nil { + fmt.Println(r) + } else { + fmt.Println(err) + } + fmt.Println() +} + +// 链式查询操作3 +func linkopSelect3() { + fmt.Println("linkopSelect3:") + r, err := db.Table("aa_user u").LeftJoin("user_detail ud", "u.id=ud.id").Fields("ud.site").Where("u.id=?", 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("aa_user u").LeftJoin("user_detail ud", "u.id=ud.id").Where("name like ?", "john").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("error",err) + } + fmt.Println() +} + +// 通过Map指针方式传参方式 +func linkopUpdate2() { + fmt.Println("linkopUpdate2:") + r, err := db.Table("aa_user").Data(gdb.Map{"name" : "john2"}).Where("name=?", "john").Update() + if err == nil { + fmt.Println(r.RowsAffected()) + } else { + fmt.Println(err) + } + fmt.Println() +} + +// 通过字符串方式传参 +func linkopUpdate3() { + fmt.Println("linkopUpdate3:") + r, err := db.Table("aa_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("aa_user").Data(gdb.Map{"name" : "john11111"}).Where(g.Map{"id" : 1}).Update() + if err == nil { + fmt.Println(r.RowsAffected()) + } else { + fmt.Println(err) + } + fmt.Println() +} + +// 链式批量写入 +func linkopBatchInsert1() { + fmt.Println("linkopBatchInsert1:") + r, err := db.Table("aa_user").Data(gdb.List{ + {"id":21,"name": "linkopBatchInsert1_john_1"}, + {"id":22,"name": "linkopBatchInsert1_john_2"}, + {"id":23,"name": "linkopBatchInsert1_john_3"}, + {"id":24,"name": "linkopBatchInsert1_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("aa_user").Data(gdb.List{ + {"id":25,"name": "linkopBatchInsert2john_1"}, + {"id":26,"name": "linkopBatchInsert2john_2"}, + {"id":27,"name": "linkopBatchInsert2john_3"}, + {"id":28,"name": "linkopBatchInsert2john_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("aa_user").Data(gdb.List{ + {"id":1, "name": "john_1"}, + {"id":2, "name": "john_2"}, + {"id":3, "name": "john_3"}, + {"id":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.Insert("aa_user", gdb.Map{ + "id" : 30, + "name" : "transaction1", + }) + 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_detail").Data(gdb.Map{"id":5, "site": "www.baidu.com哈哈哈*?~!@#$%^&*()"}).Insert() + tx.Commit() + fmt.Println(r, err) + } + fmt.Println() +} + +// 主从io复用测试,在mysql中使用 show full processlist 查看链接信息 +func keepPing() { + fmt.Println("keepPing:") + for i := 0; i < 30; i++ { + fmt.Println("ping...",i) + 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("aa_user").Where("name like ?", "%john%").Select(); err == nil { + fmt.Println(r) + } else { + fmt.Println(err) + } +} + + +// mapToStruct +func mapToStruct() { + type User struct { + Id int + Name string + Age int + Addr string + } + fmt.Println("mapToStruct:") + if r, err := db.Table("aa_user").Where("id=?", 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() { + + db.PingMaster() + db.SetDebug(true) + /*err := create() + if err != nil { + return + }*/ + + //test1 + /*for i := 1; i < 5; i++ { + insert(i) + } + query() + */ + + //batchInsert() + //query() + + //replace() + //save() + + //update1() + //update2() + //update3() + + /*linkopSelect1() + linkopSelect2() + linkopSelect3() + linkopCount1() + */ + + + /*linkopUpdate1() + linkopUpdate2() + linkopUpdate3() + linkopUpdate4() + */ + + //linkopBatchInsert1() + //linkopBatchInsert2() + + //transaction1() + //transaction2() + // + //keepPing() + //likeQuery() + mapToStruct() + //getQueriedSqls() +} \ No newline at end of file