用 Go 开发接口服务--灵活写 Dao 数据层函数

栏目: 数据库 · 发布时间: 5年前

内容简介:上一章节把实体层的结构体都准备就绪了,dao 数据层主要是进行数据操作,以实体层结构体为载体返回给上层使用。数据层的函数,我们尽量提高它们的可复用性和扩展性。数据层的函数根据功能性一般可以抽象几个分类出来,读和写相关的两种类型函数,读函数再细分读取一条记录、读取多条记录、读取聚合统计单列数据;而写函数可以细分 插入、修改、删除三种类型函数。从可复用性出发,我们可以从获取多条记录函数入手,函数参数接收结构体实例,结构体一些重要的属性如果不是非默认值,即可作为 SQL 过滤条件,参与动态条件查询。同样,聚合统计

上一章节把实体层的结构体都准备就绪了,dao 数据层主要是进行数据操作,以实体层结构体为载体返回给上层使用。数据层的函数,我们尽量提高它们的可复用性和扩展性。数据层的函数根据功能性一般可以抽象几个分类出来,读和写相关的两种类型函数,读函数再细分读取一条记录、读取多条记录、读取聚合统计单列数据;而写函数可以细分 插入、修改、删除三种类型函数。

从可复用性出发,我们可以从获取多条记录函数入手,函数参数接收结构体实例,结构体一些重要的属性如果不是非默认值,即可作为 SQL 过滤条件,参与动态条件查询。同样,聚合统计、插入、修改、删除也都以同样的方式过滤数据和执行操作。

另外数据层的函数,尽量保持函数的独立,原子性,单一性,尽量不要把业务逻辑放进来,把它们交给 service 服务层处理就好,否则会混乱。另外我们习惯每个表对应一个 go 文件,作为 dao 数据层的一项约定规则。

数据层的每个函数,不在本层实例化 DB 数据库对象,这个工作由 service 服务层来完成,dao 数据层只接收服务层传进来的,因为数据层的函数适合放可复用性高的函数,遇到细而零散的数据需求,还需要在 service 层按需获取,这样更加灵活。所以我们会发现 dao 数据层,每个函数都有一个参数 sqlxDB *sqlx.DB 实例。

下面我们列举一些具有代表性的函数来讲解,首先从 product 产品的数据操作函数开始。

*代码清单 - 产品数据层部分操作*

package dao

import (
	"errors"
	"time"

	sq "github.com/elgris/sqrl"
    "github.com/jmoiron/sqlx"

	"chapter01/src/model"
)

// SelectProductByID 根据 ID 获取单个 Product 对象
func SelectProductByID(sqlxDB *sqlx.DB, id int64) (*model.Product, error) {
	var product model.Product
	err := sqlxDB.Get(&product, "select * from product where id=? limit 1", id)
	if err != nil {
		return nil, err
	}

	return &product, nil
}

// SelectProductListByModel 根据实体(动态条件)获取多个 Product 对象集合
func SelectProductListByModel(sqlxDB *sqlx.DB, params model.Product, orderBy string, pageIndex, pageSize uint64) ([]model.Product, error) {
	sqlBuild := sq.Select("*").From("product")
	if params.BaseModel.ID > 0 {
		sqlBuild.Where("id=?", params.ID)
	}

	if params.Status > 0 {
		sqlBuild.Where("status=?", params.Status)
	}

	if params.Category > 0 {
		sqlBuild.Where("category=?", params.Category)
	}

	if params.Name != "" && params.Intro != "" {
		sqlBuild.Where(sq.Expr(
            "name like ? or intro like ?", 
            `%`+params.Name+`%`, 
            `%`+params.Intro+`%`,
        ))
	} else {
		if params.Name != "" {
			sqlBuild.Where("name LIKE ?", `%`+params.Name+`%`)
		}

		if params.Intro != "" {
			sqlBuild.Where("intro LIKE ?", `%`+params.Intro+`%`)
		}
	}

	if orderBy != "" {
		sqlBuild.OrderBy(orderBy)
	} else {
		sqlBuild.OrderBy("id desc")
	}

	if pageIndex > 0 && pageSize > 0 {
		sqlBuild.Offset(pageIndex).Limit(pageSize)
	}

	query, args, err := sqlBuild.ToSql()
	if err != nil {
		return nil, err
	}
    
	var list []model.Product
	err = sqlxDB.Select(&list, query, args...)
	if err != nil {
		return list, err
	}

	return list, nil
}

// DeleteProductByID 根据 ID 删除一个 Product 记录
func DeleteProductByID(sqlxDB *sqlx.DB, id int64) (rowsAffected int64, err error) {
	result, err := sqlxDB.Exec("delete from product where id=?", id)
	if err != nil {
		return 0, err
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return 0, err
	}
	return
}

//InsertProduct 新增一条产品记录
func InsertProduct(sqlxDB *sqlx.DB, params model.Product) (id int64, err error) {
	result, err := sqlxDB.Exec(
		`insert into product 
		(category,name,intro,price,status,created,updated) 
		values 
		(?,?,?,?,?,?,?)`,
		params.Category,
		params.Name,
		params.Intro,
		params.Price,
		params.Status,
		time.Now(),
		time.Now(),
	)
	if err != nil {
		return 0, err
	}

	id, err = result.LastInsertId()
	if err != nil {
		return 0, err
	}
	return
}

//UpdateProduct 更新一个产品信息,params.ID 是必须的
func UpdateProduct(sqlxDB *sqlx.DB, params model.Product) (rowsAffected int64, err error) {
	if params.ID <= 0 {
		return 0, errors.New("id le zero")
	}

	sqlBuild := sq.Update("product").Set("updated", time.Now())
	if params.Category > 0 {
		sqlBuild.Set("category", params.Category)
	}

	if params.Status > 0 {
		sqlBuild.Set("status", params.Status)
	}

	if params.Price > 0 {
		sqlBuild.Set("price", params.Price)
	}

	if params.Name != "" {
		sqlBuild.Set("name", params.Name)
	}

	if params.Intro != "" {
		sqlBuild.Set("intro", params.Intro)
	}

	query, args, err := sqlBuild.Where("id=?", params.ID).ToSql()
	if err != nil {
		return 0, err
	}

	result, err := sqlxDB.Exec(query, args...)
	if err != nil {
		return 0, err
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return 0, err
	}
	return
}
以上代码,除了获取一条数据 SelectProductByID 和删除一条记录 DeleteProductByID,没有使用动态条件之外,其他都使用了 sqrl 这个第三方库实现动态条件传入。这样函数可实现可复用性,比如 SelectProductListByModel 的参数 params model.Product,我们可以查询 status category 等于某值,name intro 类似某值的条件进行单个或多个条件串行查询。

然后是 product_photo 产品图片对应的 dao 数据层部分函数,和其他的很相似,结构上是一致的:
*代码清单 - 产品图片数据层部分操作*
package dao

import (
	"errors"
	"github.com/jmoiron/sqlx"
	"time"

	sq "github.com/elgris/sqrl"

	"chapter01/src/model"
)

// SelectProductPhotoByID 根据 ID 获取单个 ProductPhoto 对象
func SelectProductPhotoByID(sqlxDB *sqlx.DB, id int64) (*model.ProductPhoto, error) {
	var ProductPhoto model.ProductPhoto
	err := sqlxDB.Get(&ProductPhoto, "select * from product_photo where id=? limit 1", id)
	if err != nil {
		return nil, err
	}

	return &ProductPhoto, nil
}

// SelectProductPhotoListByModel 根据实体(动态条件)获取多个 ProductPhoto 对象集合
func SelectProductPhotoListByModel(sqlxDB *sqlx.DB, params model.ProductPhoto, orderBy string, pageIndex, pageSize uint64) ([]model.ProductPhoto, error) {
	sqlBuild := sq.Select("*").From("product_photo")
	if params.BaseModel.ID > 0 {
		sqlBuild.Where("id=?", params.ID)
	}

	if params.ProductID > 0 {
		sqlBuild.Where("product_id=?", params.ProductID)
	}

	if orderBy != "" {
		sqlBuild.OrderBy(orderBy)
	} else {
		sqlBuild.OrderBy("id desc")
	}

	if pageIndex > 0 && pageSize > 0 {
		sqlBuild.Offset(pageIndex).Limit(pageSize)
	}

	query, args, err := sqlBuild.ToSql()
	if err != nil {
		return nil, err
	}

	list := []model.ProductPhoto{}
	err = sqlxDB.Select(&list, query, args...)
	if err != nil {
		return list, err
	}

	return list, nil
}

// SelectProductPhotoListByProductID 根据实体(动态条件)获取多个 ProductPhoto 对象集合
func SelectProductPhotoListByProductID(sqlxDB *sqlx.DB, productID int64) ([]model.ViewPhotoRespArgs, error) {
	list := []model.ViewPhotoRespArgs{}
	err := sqlxDB.Select(&list, "select id,path,seq from product_photo where product_id=? order by seq asc,id desc ", productID)
	if err != nil {
		return list, err
	}

	return list, nil
}

// DeleteProductPhotoByID 根据 ID 删除一个 ProductPhoto 记录
func DeleteProductPhotoByID(sqlxDB *sqlx.DB, id int64) (rowsAffected int64, err error) {
	result, err := sqlxDB.Exec("delete from product_photo where id=?", id)
	if err != nil {
		return 0, err
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return 0, err
	}
	return
}

//InsertProductPhoto 新增一条产品记录
func InsertProductPhoto(sqlxDB *sqlx.DB, params model.ProductPhoto) (id int64, err error) {
	result, err := sqlxDB.Exec(
		`insert into product_photo (product_id,path,seq,created,updated) values (?,?,?,?,?)`,
		params.ProductID,
		params.Path,
		params.Seq,
		time.Now(),
		time.Now(),
	)
	if err != nil {
		return 0, err
	}

	id, err = result.LastInsertId()
	if err != nil {
		return 0, err
	}
	return
}

//UpdateProductPhoto 更新一个产品信息,params.ID 是必须的
func UpdateProductPhoto(sqlxDB *sqlx.DB, params model.ProductPhoto) (rowsAffected int64, err error) {
	if params.ID <= 0 {
		return 0, errors.New("id le zero")
	}

	sqlBuild := sq.Update("product_photo").Set("updated", time.Now())
	if params.ProductID > 0 {
		sqlBuild.Set("product_id", params.ProductID)
	}

	if params.Path != "" {
		sqlBuild.Set("path", params.Path)
	}

	if params.Seq > 0 {
		sqlBuild.Set("seq", params.Seq)
	}

	query, args, err := sqlBuild.Where("id=?", params.ID).ToSql()
	if err != nil {
		return 0, err
	}

	result, err := sqlxDB.Exec(query, args...)
	if err != nil {
		return 0, err
	}

	rowsAffected, err = result.RowsAffected()
	if err != nil {
		return 0, err
	}
	return
}

小结

dao 数据层函数,要保持原子性、单一性,要避免一切业务逻辑的东西,这样利于增强程序的可读性,也利于定位突发的底层问题。实际项目中,数据层函数解决了服务层所需要的绝大部分的数据需求,我们尽量降低它的复杂度,遵循原子性、单一性和可复用性。其他零散的数据查询和操作,可以交给 service 服务层来做。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Probability and Computing

Probability and Computing

Michael Mitzenmacher、Eli Upfal / Cambridge University Press / 2005-01-31 / USD 66.00

Assuming only an elementary background in discrete mathematics, this textbook is an excellent introduction to the probabilistic techniques and paradigms used in the development of probabilistic algori......一起来看看 《Probability and Computing》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具