xorm相关操作小结

前言

最近写业务用到xorm操作postgreSQL,抽空写了一些平时会用到的常用的操作,好脑筋不如好笔记。

xorm

参考文档

中文文档-方便滤清xorm所有的知识体系

英文文档-有各种各样的实际操作

项目地址

相关技术博客

Go每日一库之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
}
原文地址:https://www.cnblogs.com/paulwhw/p/14471286.html