From d85332aca1d5c1cc79a5f7e714150575ffe35ee0 Mon Sep 17 00:00:00 2001 From: wenzi1 Date: Mon, 19 Nov 2018 11:38:57 +0800 Subject: [PATCH] =?UTF-8?q?ORM=E6=96=B0=E5=A2=9E=E5=AF=B9MSSQL=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/database/gdb/gdb.go | 1 + g/database/gdb/gdb_mssql.go | 159 +++++++ g/database/gdb/gdb_oracle.go | 7 +- geg/database/orm/mssql/gdb_sqlserver.go | 582 ++++++++++++++++++++++++ geg/database/orm/oracle/gdb.go | 2 +- 5 files changed, 747 insertions(+), 4 deletions(-) create mode 100644 g/database/gdb/gdb_mssql.go create mode 100644 geg/database/orm/mssql/gdb_sqlserver.go diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index ebe3fb211..cba38e569 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -133,6 +133,7 @@ func init() { driverMap["oracle"] = linkOracle driverMap["sqlite"] = linkSqlite driverMap["pgsql"] = linkPgsql + driverMap["mssql"] = linkMssql } // 使用默认/指定分组配置进行连接,数据库集群配置项:default diff --git a/g/database/gdb/gdb_mssql.go b/g/database/gdb/gdb_mssql.go new file mode 100644 index 000000000..6cda653db --- /dev/null +++ b/g/database/gdb/gdb_mssql.go @@ -0,0 +1,159 @@ +// 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 20181109 +说明: + 1.需要导入sqlserver驱动: github.com/denisenkom/go-mssqldb + 2.不支持save/replace方法 + 3.不支持LastInsertId方法 +*/ +package gdb + +import ( + "database/sql" + "fmt" + "gitee.com/johng/gf/g/util/gregex" + "strconv" + "strings" +) + + +var linkMssql = &dbmssql{} + +// 数据库链接对象 +type dbmssql struct { + Db +} + +// 创建SQL操作对象 +func (db *dbmssql) Open(c *ConfigNode) (*sql.DB, error) { + var source string + if c.Linkinfo != "" { + source = c.Linkinfo + } else { + source = fmt.Sprintf("uid=%s;pwd=%s;server=%s;port=%s;database=%s;encrypt=disable", c.User, c.Pass, c.Host, c.Port, c.Name) + } + if db, err := sql.Open("sqlserver", source); err == nil { + return db, nil + } else { + return nil, err + } +} + +// 获得关键字操作符 - 左 +func (db *dbmssql) getQuoteCharLeft() string { + return "\"" +} + +// 获得关键字操作符 - 右 +func (db *dbmssql) getQuoteCharRight() string { + return "\"" +} + +// 在执行sql之前对sql进行进一步处理 +func (db *dbmssql) handleSqlBeforeExec(q *string) *string { + index := 0 + str, _ := gregex.ReplaceStringFunc("\\?", *q, func(s string) string { + index++ + return fmt.Sprintf("@p%d", index) + }) + + str, _ = gregex.ReplaceString("\"", "", str) + + return db.parseSql(&str) +} + +//将MYSQL的SQL语法转换为MSSQL的语法 +//1.由于mssql不支持limit写法所以需要对mysql中的limit用法做转换 +func (db *dbmssql) parseSql(sql *string) *string { + //下面的正则表达式匹配出SELECT和INSERT的关键字后分别做不同的处理,如有LIMIT则将LIMIT的关键字也匹配出 + patten := `^\s*(?i)(SELECT)|(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 + } + + //判断SQL中是否含有order by + selectStr := "" + orderbyStr := "" + haveOrderby := gregex.IsMatchString("((?i)SELECT)(.+)((?i)ORDER BY)", *sql) + if haveOrderby { + //取order by 前面的字符串 + queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)ORDER BY)", *sql) + + if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false{ + break + } + selectStr = queryExpr[2] + + //取order by表达式的值 + orderbyExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", *sql) + if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false{ + break + } + orderbyStr = orderbyExpr[2] + } else { + queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", *sql) + if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false{ + break + } + selectStr = queryExpr[2] + } + + + + //取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 + } + } + + if haveOrderby { + *sql = fmt.Sprintf("SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d", orderbyStr, selectStr, first, limit) + } else { + if first == 0 { + first = limit + } else { + first = limit - first + } + *sql = fmt.Sprintf("SELECT * FROM (SELECT TOP %d * FROM (SELECT TOP %d %s) as TMP1_ ) as TMP2_ ", first, limit, selectStr) + } + default: + } + return sql +} \ No newline at end of file diff --git a/g/database/gdb/gdb_oracle.go b/g/database/gdb/gdb_oracle.go index 9e1b99409..93ad1ac56 100644 --- a/g/database/gdb/gdb_oracle.go +++ b/g/database/gdb/gdb_oracle.go @@ -99,8 +99,9 @@ func (db *dboracle) parseSql(sql *string) *string { } queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", *sql) - queryExpr[0] = strings.TrimRight(queryExpr[0], "LIMIT") - queryExpr[0] = strings.TrimRight(queryExpr[0], "limit") + if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false{ + break + } //取limit后面的取值范围 first, limit := 0, 0 @@ -117,7 +118,7 @@ func (db *dboracle) parseSql(sql *string) *string { } //也可以使用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) + *sql = fmt.Sprintf("SELECT * FROM (SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d) WHERE ROWNUM_ >= %d", queryExpr[1], queryExpr[2], limit, first) case "INSERT": //获取VALUE的值,匹配所有带括号的值,会将INSERT INTO后的值匹配到,所以下面的判断语句会判断数组长度是否小于3 valueExpr, err := gregex.MatchAllString(`(\s*\(([^\(\)]*)\))`, *sql) diff --git a/geg/database/orm/mssql/gdb_sqlserver.go b/geg/database/orm/mssql/gdb_sqlserver.go new file mode 100644 index 000000000..1649bf958 --- /dev/null +++ b/geg/database/orm/mssql/gdb_sqlserver.go @@ -0,0 +1,582 @@ +package main + +import ( + "fmt" + "time" + //_ "github.com/denisenkom/go-mssqldb" + "gitee.com/johng/gf/g/database/gdb" + "gitee.com/johng/gf/g" +) + +// 本文件用于gf框架的mssql数据库操作示例,不作为单元测试使用 + +var db *gdb.Db + +// 初始化配置及创建数据库 +func init () { + gdb.AddDefaultConfigNode(gdb.ConfigNode { + Host : "127.0.0.1", + Port : "1433", + User : "test", + Pass : "test", + Name : "test", + Type : "mssql", + 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 int not null, + name VARCHAR(60), + age int, + addr varchar(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 int not null, + site VARCHAR(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 where id='1'") + 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(3, 5).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":6, "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) + }*/ + //insert(2) + 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 diff --git a/geg/database/orm/oracle/gdb.go b/geg/database/orm/oracle/gdb.go index 4accdc246..ffdcf8bc7 100644 --- a/geg/database/orm/oracle/gdb.go +++ b/geg/database/orm/oracle/gdb.go @@ -3,7 +3,7 @@ package main import ( "fmt" "time" - _ "github.com/mattn/go-oci8" + //_ "github.com/mattn/go-oci8" "gitee.com/johng/gf/g/database/gdb" "gitee.com/johng/gf/g" )