内容简介:上一章节把实体层的结构体都准备就绪了,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 服务层来做。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Java8 函数接口
- java8的函数式接口
- 深入理解 lambda表达式 与 函数式编程 函数式接口源码解析(二)
- Go指南-谈谈Go的接口与函数
- 用 Go 开发接口服务--公共类关键函数
- Java8新特性学习-函数式接口
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。