golang的数据库操作xorm使用起来非常方便,不用再自己写SQl语句,而且xorm自己给我们做了SQL防注入等操作,用起来既方便又安全。此次文章我不会记录xorm的基本操作,我值记录一些特殊用法问题,包括动态创建表单、基于xorm的联合查询、基于xorm的跨表查询。
首先是基于xorm的数据库连接的创建:代码如下
- import (
- "github.com/go-xorm/xorm"
- )
-
- engine, err := xorm.NewEngineGroup("mysql","root:password@tcp(127.0.0.1:3306)/dbname?charset=utf8")
- if err != nil {
- log.Fatal("创建lgoxorm引擎失败", err)
- return nil
- }
- engine.SetMaxIdleConns(dbMaxIdleConns) //连接池的空闲数大小
- engine.SetMaxOpenConns(dbMaxOpenConns) //设置最大打开连接数
- engine.SetConnMaxLifetime(dbMaxLiftTime) //设置连接的最大生存时间
- engine.ShowSQL(true) //显示每次xorm操作数据库打印SQL语句,可以注释掉
后面的操作就完全使用engine这个数据库句柄来操作数据库
我目前遇到的需求是这样的,在开发过程中,有这么一种情况,数据库表单的键值是完全一样的,用一个结构体表示就可以了,但是表需要多个,而且名字需要动态的添加,有一个资源对应的请求来,然后平台根据资源的信息创建一个专属的表,这样就形成了不同表名,表键值完全一样的情况。经过多次尝试,最后找到了如下方法:
- type tablestruct struct {
- Id int `json:"id" xorm:"pk autoincr comment('自动生成id')"`
- Name string `json:"name" xorm:" comment('姓名')"`
- Info string `json:"info" xorm:"varchar(1024) comment('信息,长度为1024,不指定默认长度为255')"`
- }
-
-
- //动态创建数据table方法1:
- engine.Table("newtable1").CreateTable(&tablestruct{})
-
- //动态创建数据table方法2:
- engine.Table("newtable2").Sync2(&tablestruct{})
-
- //查询表是否存在,不存在则使用上面两种方法进行动态创建数据库,表名字可以自定义
- has, _ := engine.IsTableExist("newtable1")
- if has{
- //已经存在表
- }
CreateTable新建数据库新建的时候如果名字一样,则会报错,这种如果tablestruct有更新,也不会更新到后台;Sync2方法是同步或者新建数据库表,就算存在表名也不会出错,而且如果tablestruct有更新,会同步更新字段到表单,所以我一般用第二种方法来动态新建表。
联合查询在mysql里面的关键字是join,连表查询存在两种情况,一种就是我只取其中一个表的数据,联表查询的另一个表只是作为查询条件作用;还有一种就是联表查询需要查询两个表的数据。
join含有三个参数:第一个参数是INNER, LEFT中的一个值, 第二个参数被联合查询的表名,可以为字符串或者bean, 第三个参数为连接条件
我下面只记录两种常见也是我用的比较多的情况,inner和left,我建了两张表,一张是叫resource,表示存储的资源信息,另一一张表叫whitelistdata,表示存储的IP白名单,下面我将用这两个表来展示join的用法:目前两个表的内容如下所示:
表whitelistdata内容:
表resource内容:
首先是inner join(等值连接) 只返回两个表中联结字段相等的行
inner的意思是将满足条件的两个表的数据组合成新的数据成为一个新的表,最后的功能是获取资源的ip在白名单的ip里面的数据,操作代码如下所示:
- engine.Table("resource").Join("inner", "whitelistdata", "resource.ip=whitelistdata.ip").select("*")
-
- //等价于执行如下mysql语句,最后返回的结果是两个表的组合的数据:
- //SELECT * FROM resource inner join whitelistdata on resource.ip=whitelistdata.ip;
-
-
- //如果只想通过筛选获取resource的数据
- engine.Table("resource").Join("inner", "whitelistdata", "resource.ip=whitelistdata.ip").select("resource.*")
- //等价于mysql语句
- //SELECT resource.* FROM resource inner join whitelistdata on resource.ip=whitelistdata.ip;
-
数据库的执行结果如下所示如下两条数据:
然后再使用Find指令将查询的数据保存在数组当中。
然后是left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
left是返回包括左表中的所有记录和右表中联结字段相等的记录 ,真实逻辑也是将两个表组合根据查询条件查询出数据,左表数据是全部返回的,如果有不满足条件,则右边用null来填充,操作代码如下:
- engine.Table("resource").Join("left", "whitelistdata", "resource.ip=whitelistdata.ip").select("*")
-
- //等价于执行如下mysql语句,最后返回的结果是两个表的组合的数据:
- //SELECT * FROM resource left join whitelistdata on resource.ip=whitelistdata.ip;
-
-
- //如果只想通过筛选获取resource的数据
- engine.Table("resource").Join("left", "whitelistdata", "resource.ip=whitelistdata.ip").select("resource.*")
- //等价于mysql语句
- //SELECT resource.* FROM resource left join whitelistdata on resource.ip=whitelistdata.ip;
-
数据库的执行结果如下所示如下图所示:
然后left还有一种用法就是:需要查询左边数据,不满足那个条件查询的数据,即查询满足条件查询的剩余不满足条件的数据,操作命令如下:
- engine.Table("resource").Join("left", "whitelistdata", "resource.ip=whitelistdata.ip").select("*").where("whitelistdata.ip=?",nil)
-
- //等价于执行如下mysql语句,最后返回的结果是两个表的组合的数据:
- //SELECT * FROM resource left join whitelistdata on resource.ip=whitelistdata.ip where whitelistdata.ip is null;
查询结果如下:
因为之前的数据库查询用的是xorm操作,但是我们遇到个需求需要联表查询,项目里面防止数据太,做了分表处理,所以查询的时候就存在跨表查询。但是xorm这个插件不存在直接的跨表查询,最后查阅了资料,发现可以使用xorm的builder模块来实现跨表查询,即mysql原生语句里面的union。这种情况是针对那些两个表的数据类型完全一致的情况适用。例子如下:
他的原理是先使用builder生成原生的sql语句,然后再使用xorm来执行语句,使用起来还是很方便
- import (
- "xorm.io/builder"
- "fmt"
- )
-
- //使用builder组合union的查询mysql语句
- sqlBuilder = builder.Select("*").From(agentlogstable+startMonth).Union("all", builder.Select("*").From(agentlogstable+endMonth))
-
- //转换成sql原生语句
- sql, _, err := sqlBuilder.ToSQL()
- if err != nil {
- //转换mysql语句错误
- fmt.Printfn("err = ",err.Error())
- }
-
- //生成最终builder格式的mysql查询语句
- sqlBuilder = builder.Dialect(builder.MYSQL).Select("*").From("(" + sql + ") as newtb")
-
- //getdata是定义的存储数据的数组或者切片,获取跨表查询的数据
- err = engine.SQL(sqlBuilder).Find(&getdata)
备注:查询的时候需要用:builder.Dialect(builder.MYSQL),不能用builder,否则会报错