用golang操作数据库的方法很多。golang自带databse/sql包,直接引用包里面的方法就可以操作数据库。使用beego框架时,beego也自带了一套orm模型。而gorm则是基于database/sql封装了一套完备的数据库操作方法。
进行数据库编程,至少要了解数据库的基础知识。比如表、字段、字段类型的定义,增删改查的动作。本文不再赘述这些基础知识,有需要了解的自行搜索了解。
与数据库交互,第一步动作即是建立与数据库的连接。以mysql为例:
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?allowNativePasswords=true&charset=utf8mb4&parseTime=true&loc=Local", dbUser, dbPwd, dbAddr, dbPort, dbName)
fmt.Println("数据库链接:", dbAddr)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("初始化数据库连接错误:", err.Error())
return
} else {
fmt.Println("初始化数据库连接成功!")
}
连接建立成功后,变量db就是gorm提供的数据库连接对象,我们不必关注其内部是如何连接数据库的,实际上在gorm内部是有一个连接池,db对象在操作数据的时候,会从连接池中选择一个空闲的连接与数据库进行交互。
如同我们去4S店给车做保养一样,我们得先把车开到4S店,将车交给前台的售后人员(db),他来负责将车开到保养工房,再安排一个维修工程师给我们的汽车做保养。排在我们后面的客人再来找这位售人员(db),如果还有空闲的维修保养工程师,则立即安排对后面客人的车进行保养。诸如此类,直到所有的维修保养工程师都在工作了,后面再来客人,就需要等待前面的车完成维保把位置空出来,或者后面的客人离开该4S店,另寻他处进行维保。至于售后人员(db)具体找的是哪位维保人员给我们车做维保,事实上我们根本无需关心。
在这里,我们要注意的是db是一个gorm对象,而gorm完成了对dababase/sql的封装,真正操作数据,实际上是在sql.DB中完成的。如果要对数据库的连接进行一些设置,需要在sql.DB中完成,无法直接用db对象来操作,比如设置连接的空闲最大时长,空闲最大连接数等。
SqlDB, err = db.DB()
if err != nil {
fmt.Println("获取数据库连接出错:", err.Error())
return
}
SqlDB.SetConnMaxIdleTime(time.Hour * time.Duration(attr.DbMaxOpenConns))
SqlDB.SetMaxIdleConns(int(attr.DbMaxIdleConns))
gorm为我们做了一些额外的封装,简化了我们的很多动作。比如当我们定义或者修改数据模型后,对应的数据库的表结构也需要调整,才能与数据模型保持一致。gorm提供了AutoMigrate方法,该方法的功能是将数据模型同步到数据库对应的表结构中,模型中的字段类型如果被改动,或者新增了字段,对应的表结构也会相应的调整。如果数据库中还没有该表,则直接按模型描述创建新表。(描述模型后面再细说)
//自动根据结构体创建表结构
e := db.AutoMigrate(
&Order{}, //订单表
&RequestInfo{}, //请求信息表
&Mobile{}, //手机号表
)
gorm是如何知道我们要操作的表的表名呢?总不可能我们全部手写sql吧。这个不用担心,在gorm中,每个模型有一个TableName的方法,该方法让gorm知道我们要操作的数据库表具体是哪一张。
func (self *Order) TableName() string {
return "t_order"
}
这里分享一个小技巧,我们建表的时候,通常会根据某些原则设置前缀,这在编写TableName方法的时候,也可以这样来做。比我们希望我们所有表的表名都是"t_“开头,其中订单表的表名为"t_order",则可以如下操作。其中公共的TableName方法放在公共文件中,Order的TableName方法在存放在自己文件中。
func TableName(tableName string) string {
return "t_" + tableName
}
func (self *Order) TableName() string {
return TableName("order")
}
用gorm方法对表数据进行增、删、改、查的操作非常方便。
//添加一条记录
func (self *Order) Insert() (int64, error) {
self.CreateTime = time.Now()
self.UpdateTime = time.Now()
tx := db.Create(self)
return tx.RowsAffected, tx.Error
}
//按条件查询记录
func (self *Order) Select(cols ...string) ([]Order, error) {
data := make([]Order, 0)
strQuery := " 1=1 "
fields := make([]interface{}, 0)
for _, col := range cols {
switch col {
case "id":
strQuery += " AND id = ?"
fields = append(fields, self.Id)
case "tracer_id":
strQuery += " AND tracer_id = ?"
fields = append(fields, self.TraceId)
case "channel":
strQuery += " AND channel = ?"
fields = append(fields, self.Channel)
case "gds_type":
strQuery += " AND gds_type = ?"
fields = append(fields, self.GdsType)
case "tc_order_id":
strQuery += " AND tc_order_id = ?"
fields = append(fields, self.TcOrderId)
case "unit_key":
strQuery += " AND unit_key = ?"
fields = append(fields, self.UnitKey)
case "airline_code":
strQuery += " AND airline_code = ?"
fields = append(fields, self.AirlineCode)
case "contact_name":
strQuery += " AND contact_name = ?"
fields = append(fields, self.ContactName)
case "contact_phone":
strQuery += " AND contact_phone = ?"
fields = append(fields, self.ContactName)
case "status":
strQuery += " AND status = ?"
fields = append(fields, self.Status)
}
}
tx := db.Where(strQuery, fields...).Find(&data)
return data, tx.Error
}
//按主键id修改指定字段
func (self *Order) Update(cols ...string) (int64, error) {
tx := db.Model(self).Where("id=?", self.Id)
if tx.Error != nil {
return 0, tx.Error
}
updateFields := make(map[string]interface{})
for _, col := range cols {
switch col {
case "status":
updateFields["status"] = self.Status
case "channel":
updateFields["channel"] = self.Channel
case "contact_name":
updateFields["contact_name"] = self.ContactName
case "contact_phone":
updateFields["contact_phone"] = self.ContactPhone
case "contact_email":
updateFields["contact_email"] = self.ContactEmail
case "contact_address":
updateFields["contact_address"] = self.ContactAddress
case "remarks":
updateFields["remarks"] = self.Remarks
case "async":
updateFields["async"] = self.Async
}
}
self.UpdateTime = time.Now()
updateFields["update_time"] = self.UpdateTime
tx = tx.Updates(updateFields)
return tx.RowsAffected, tx.Error
}
//根据主键id删除记录
func (self *Order) Delete() (int64, error) {
tx := db.Delete("id=?", self.Id)
return tx.RowsAffected, tx.Error
}