前言
最近写业务用到xorm操作postgreSQL,抽空写了一些平时会用到的常用的操作,好脑筋不如好笔记。
xorm
参考文档
相关技术博客
上面那个作者的每日一库系列跟Go的相关文章挺不错:Go每日一库、Go系列文章
操作总结
“初始化引擎操作”
下面使用的 model.DB.Walk 实际上是在项目启动时就已经启动的引擎,自己做本地测试的话可以在本地做初始化的工作:
// init string
const OnlineConn = "postgres://bytepower_rw:xxxxxx@zzzxxx.com.cn:5432/xxx_db_namexxx?sslmode=disable;"
// init db
model.DB.Walk, err = xorm.NewEngine("postgres", OnlineConn)
if err != nil {
panic("初始化数据库错误:" + err.Error())
}
defer model.DB.Walk.Close()
定义结构体
type StudentTest struct {
// TODO 注意xorm的注释暂时只支持MySQL引擎,postgreSQL即使设置了也不生效
// 主键 字段名设置为 sid
Id string `json:"id" xorm:"varchar(255) pk 'sid' comment('学生ID')"`
// TODO 注意如果设置为 "非空且唯一" 的话会自动创建一个索引!但是 Sync2方法会报错,尽量不要设置非空且唯一字段!
Name string `json:"name" xorm:"varchar(25) notnull 'name' comment('学生姓名')"`
Age int `json:"age" xorm:"notnull 'age' comment('学生年龄')"`
// 索引字段 TODO 使用orm内置的方法创建索引会失败!需要手动执行SQL语句
Score float64 `json:"score" xorm:"notnull 'score' comment('学生成绩')"`
ClassId string `json:"class_id" xorm:"notnull 'class_id' comment('学生所在班级')"`
// 创建时间与修改时间
CreatedTime time.Time `json:"created_time" xorm:"created notnull"`
UpdatedTime time.Time `json:"updated_time" xorm:"updated notnull"`
}
func (s *StudentTest) TableName() string {
return "student_test"
}
// 新建一个班级表,用于连表查询的测试
type SClassTestModel struct {
Id string `json:"id" xorm:"varchar(255) pk 'cid' comment('班级id')"`
Name string `json:"name" xorm:"varchar(255) notnull 'name' comment('班级名称')"`
}
func (sc *SClassTestModel) TableName() string {
return "sclass_test"
}
创建表
// 创建班级表
func TestCreateClassTable(t *testing.T) {
err := test.Session.Sync2(aaa_module.SClassTestModel{})
require.Equal(t, nil, err)
}
// 创建学生表
func TestCreateTable(t *testing.T) {
// 创建学生表
// TODO 使用 Sync2 方法可以在修改model结构体后直接修改表字段,建议使用这个方法
err := test.Session.Sync2(aaa_module.StudentTest{})
require.Equal(t, nil, err)
}
“批量插入数据”
// 批量插入数据
func InsertStudentSlice(studentSlice []*StudentTest) (int64, error) {
affected, err := model.DB.Walk.Table("student_test").Insert(studentSlice)
if err != nil {
return affected, errors.New("insert student slice raise error!" + err.Error())
}
return affected, nil
}
// 具体实现
// 批量插入多条数据
func TestInsertStudentSlice(t *testing.T) {
stu1 := aaa_module.StudentTest{Id: "1", Name: "whw1", Age: 12, Score: 99, ClassId: "223"}
stu2 := aaa_module.StudentTest{Id: "2", Name: "whw2", Age: 13, Score: 98, ClassId: "222"}
stu3 := aaa_module.StudentTest{Id: "3", Name: "whw3", Age: 14, Score: 97, ClassId: "221"}
stu4 := aaa_module.StudentTest{Id: "4", Name: "whw4", Age: 15, Score: 96, ClassId: "222"}
stu5 := aaa_module.StudentTest{Id: "5", Name: "whw5", Age: 16, Score: 95, ClassId: "221"}
stuSlice := []*aaa_module.StudentTest{&stu1, &stu2, &stu3, &stu4, &stu5}
// 创建
if affected, err := aaa_module.InsertStudentSlice(stuSlice); err != nil {
fmt.Println("affected: ", affected)
fmt.Println("err: ", err)
} else {
fmt.Println("写入成功!affected: ", affected)
}
}
“插入单条数据”
// 插入单条数据
func InsertStudentObj(stuObj *StudentTest) (int64, error) {
if ok, err := model.DB.Walk.Table("student_test").Insert(stuObj); err != nil {
return ok, err
} else {
return ok, nil
}
}
// 具体实现
func TestInsertStuObj(t *testing.T) {
stuObj := aaa_module.StudentTest{Id: "6", Name: "naruto", Age: 22, Score: 99, ClassId: "231"}
if ok, err := aaa_module.InsertStudentObj(&stuObj); err != nil {
fmt.Println("insert stuObj raise error! ", err.Error())
} else {
fmt.Println("insert stuObj successfully! ok: ", ok)
}
}
查询符合条件的记录的数量
func GetCount(session *xorm.Session, query interface{}, args ...interface{}) (count int64, err error) {
return session.Where(query, args...).Count(&WithdrawRecordModel{})
}
// 具体使用
cashId := "123"
userId := "2"
query := "cash_id = ? and user_id = ?"
cashCount, err := GetCount(session, query, cashId, userId)
判读记录是否存在
// 判断记录是否存在
func ExistStu(stu *StudentTest) (bool, error) {
has, err := model.DB.Walk.Table("student_test").Exist(stu)
return has, err
}
// 具体实现
// 判断记录是否存在 —— 效率更高
func TestExistStuObj(t *testing.T) {
stuObj := aaa_module.StudentTest{Name: "naruto"}
has, err := aaa_module.ExistStu(&stuObj)
if err != nil {
fmt.Println("err: ", err.Error())
} else if !has {
fmt.Println("不存在这条记录!")
} else {
fmt.Println("存在这条记录!")
}
}
用主键ID最效率的查询
// 通过id查找学生对象 高效的查询方式!
func GetStudentById(sid string) (*StudentTest, bool, error) {
stuObj := StudentTest{}
// 使用这种查询效率高
// TODO 注意这里需要将地址传进去
has, err := model.DB.Walk.Table("student_test").Id(sid).Get(&stuObj)
// TODO 也可以指定返回的字段
// has, err := model.DB.Walk.Table("student_test").Id(sid).Cols("sid", "name", "score")
return &stuObj, has, err
}
// 具体实现
// 根据主键id查询单条数据 TODO 效率高的方式
func TestGetStudentById(t *testing.T) {
sid := "2"
stuObj, has, err := aaa_module.GetStudentById(sid)
if err != nil {
fmt.Println("get student by i·d raise error! ", err.Error())
} else {
if !has {
fmt.Println("can't get stuObj by that sid!")
} else {
fmt.Println(fmt.Sprintf("get the stuObj: sid: %s, Name: %s, Age: %d, Score: %.3f, ClassId %s ", stuObj.Id, stuObj.Name, stuObj.Age, stuObj.Score, stuObj.ClassId))
}
}
}
根据条件查询单条数据
// 根据条件查询单条数据, 接收一个StudentTest结构体指针作为条件
func GetStuObj(stu *StudentTest) (*StudentTest, bool, error) {
has, err := model.DB.Walk.Table("student_test").Get(stu)
return stu, has, err
}
// 具体实现
// 根据不同的条件查询单条数据
// TODO: 这种方式很强大,不需要构建繁杂的查询,有的话直接将结果存入 "条件结构体即可"
func TestGetStudent(t *testing.T) {
stu := &aaa_module.StudentTest{Name: "naruto"}
stu, has, err := aaa_module.GetStuObj(stu)
// 判断有没有出错
if err != nil {
fmt.Println("get student raises error! ", err.Error())
// 判断有没有查到数据
} else if !has {
fmt.Println("can't get stuObj!")
// 拿到结果的话直接将结果放在定义好的结构体中
} else {
fmt.Println("stuObj: ", stu.Id, stu.Name, stu.Score)
}
}
返回所有符合条件的记录
// 返回所有符合条件的记录 ———— 条件是固定死的
func FindSlice() ([]StudentTest, error) {
stuObjSlice := make([]StudentTest, 1)
err := model.DB.Walk.Table("student_test").Where("age > ? and score < ?", 10, 100).Find(&stuObjSlice)
return stuObjSlice, err
}
// 具体实现
// 返回所有符合条件的记录 ———— 条件是固定死的
func TestFind(t *testing.T) {
stuObjSlice, err := aaa_module.FindSlice()
if err != nil {
fmt.Println("err>>> ", err)
} else {
fmt.Println("stuObjSlice: ", stuObjSlice)
}
}
* 自定义条件查询所有记录
// 返回所有符合条件的记录 —— 自定义条件查询
// 接收一个 map[string]interface{} key是查询的字段,value是查询的值
func FindSliceBySelfCondition(queryMap map[string]interface{}) ([]*StudentTest, error) {
var retSlice []*StudentTest
// ****** 其实本质上就是一个map[string]interface{} ******
err := model.DB.Walk.Table("student_test").Where(queryMap).Find(&retSlice)
return retSlice, err
}
// 具体实现
// 返回所有符合条件的记录 —— 自定义条件查询
func TestFindSliceBySelfCondition(t *testing.T){
// ****** 构建查询条件查询的条件 ******
queryMap := map[string]interface{}{
"name": "naruto",
"age": 22,
}
retSlice, err := aaa_module.FindSliceBySelfCondition(queryMap)
if err != nil{
fmt.Println("err: ", err)
}else{
fmt.Println("retSlice: ", retSlice)
// 遍历结果
for _, obj := range retSlice{
fmt.Println("obj: ", obj)
}
}
}
连表查询+执行原生SQL
func JoinQuery() error {
// 连表查询语句
sqlStr := "select t1.sid as stu_id, t1.name as stu_name,t1.age as stu_age, t1.score as stu_score, t2.name as class_name, t2.cid as class_id " +
"from student_test as t1 " +
"left join s_class_test_model as t2 " +
"on t1.class_id = t2.cid"
// 1、QueryInterface TODO: 左表中有但右表中没有的会显示nil ———— 推荐这种方式
rets, err := model.DB.Walk.Table("student_test").QueryInterface(sqlStr)
fmt.Println("QueryInterface: ret: ", rets)
for _, mp := range rets {
fmt.Println("mp: ", mp["class_id"], mp["class_name"], mp["stu_name"])
}
// 2、QueryString TODO 左表中有但右表中没有的不会显示
results, err := model.DB.Walk.Table("student_test").QueryString(sqlStr)
fmt.Println("QueryString ret: ", results)
for _, mp := range results {
fmt.Println("mp: ", mp["class_id"], mp["class_name"], mp["stu_name"])
}
return err
}
// 具体实现
// 连表查询 TODO 执行原生SQL的方式!
func TestJoinQuery(t *testing.T){
err := aaa_module.JoinQuery()
if err != nil{
fmt.Println("err: ", err)
}else{
fmt.Println("join Query successfully")
}
}
更新记录
// 更新记录
/*
更新通过engine.Update()实现,可以传入结构指针或map[string]interface{}。对于传入结构体指针的情况,xorm只会更新非空的字段。
如果一定要更新空字段,需要使用Cols()方法显示指定更新的列。使用Cols()方法指定列后,即使字段为空也会更新
*/
func UpdateData() (int64, error){
affected, err := model.DB.Walk.Table("student_test").Where("name=?", "naruto").Update(&StudentTest{Score: 123})
// 或者
// affected, err := model.DB.Walk.Table("student_test").ID("2").Update(&StudentTest{Score: 666})
return affected, err
}
// 具体实现
func TestUpdate(t *testing.T){
affected, err := aaa_module.UpdateData()
if err != nil{
fmt.Println("err: ", err)
}else{
fmt.Println("修改成功!affected: ", affected)
}
}
删除记录
// 删除记录
func DeleteData() (int64, error){
affected, err := model.DB.Walk.Table("student_test").Where("name=?", "sasuke").Delete(&StudentTest{})
return affected, err
}