当前位置: 首页 > news >正文

DaoSingle相关的结构,整体生成一个说明开发文档

DaoSingle是基于GoWebFrame3框架实现的数据访问门面单例,采用门面模式统一封装多个DAO组件,提供简洁的数据访问入口。其核心职责包括统一访问入口、单例管理、缓存支持和简化调用。架构上位于业务层与数据访问层之间,通过DaoQuery结构体聚合多个Facade组件。文档详细介绍了其生命周期管理、公共API、使用示例和扩展指南,并提供了最佳实践建议。DaoSingle通过统一门面设计简化调用、解耦业务、支持缓存扩展,适用于GoTrain项目的数据访问层开发。

https://gitee.com/gowebframe3/webframe.git

DaoSingle 开发文档


1. 概述

1.1 定义

DaoSingle是基于GoWebFrame3框架实现的数据访问门面单例,采用门面模式(Facade Pattern)将多个数据访问层(DAO)组件统一封装,提供简洁、统一的数据访问入口。

1.2 核心职责

职责说明
统一入口聚合多个 DAO Facade,提供单一访问点
单例管理通过依赖注入容器管理生命周期
缓存支持提供用户信息等缓存查询能力
简化调用屏蔽底层复杂的 DAO 调用链

1.3 架构定位

┌─────────────────────────────────────────────────────────────┐ │ Controller / Service │ │ (业务层) │ └───────────────────────────┬─────────────────────────────────┘ │ 调用 ▼ ┌─────────────────────────────────────────────────────────────┐ │ DaoSingle │ │ (门面层 - 单例) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ DaoQuery │ │ │ │ CreditFacade | OpcFacade | PlanFacade | ... │ │ │ └─────────────────────────────────────────────────────┘ │ └───────────────────────────┬─────────────────────────────────┘ │ 委托调用 ▼ ┌─────────────────────────────────────────────────────────────┐ │ DAO Facades (数据访问层) │ │ CreditFacade | OpcFacade | PlanFacade | StudentFacade ... │ └─────────────────────────────────────────────────────────────┘

2. 核心结构

2.1 DaoSingle 结构体

go

type DaoSingle struct { basedto.BaseEntitySingle // 继承基础单例实体 *daoquery.DaoQuery // 组合 DAO 查询组件 }

字段说明

字段类型说明
BaseEntitySinglebasedto.BaseEntitySingle提供单例基础能力
DaoQuery*daoquery.DaoQuery聚合多个 DAO Facade

2.2 DaoQuery 结构体

go

type DaoQuery struct { basedto.BaseEntity *daofacade.CreditFacade // 积分系统 *daofacade.OpcFacade // 操作日志 *daofacade.PlanFacade // 训练计划 *daofacade.StudentFacade // 学生管理 *daofacade.UserDaoFacade // 用户管理 *daofacade.WordDaoFacade // 单词管理 *daofacade.KbaseFacade // 知识库 }

包含的 Facade 组件

Facade职责
CreditFacade积分查询、积分变更记录
OpcFacade操作日志记录与查询
PlanFacade训练计划 CRUD 操作
StudentFacade学生信息管理
UserDaoFacade用户信息查询、缓存
WordDaoFacade单词库查询、词表管理
KbaseFacade知识库管理

3. 生命周期管理

3.1 单例注册流程

plainText

┌─────────────────────────────────────────────────────┐ │ init() │ │ 调用 registerBeanDaoSingle() │ └───────────────────────────┬─────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────┐ │ basedi.RegisterLoadBean() │ │ 注册单例名称和加载函数 LoadDaoSingle │ └───────────────────────────┬─────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────┐ │ LoadDaoSingle() │ │ 1. NewDaoSingle() 创建实例 │ │ 2. InjectDaoSingle() 依赖注入 │ │ 3. 返回单例实例 │ └─────────────────────────────────────────────────────┘

3.2 初始化代码

go

// 单例名称常量 var singleNameDaoSingle = "*daosingle.DaoSingle-98055d62-ef8f-4464-a518-57ddd65c92d9" // 注册单例 func registerBeanDaoSingle() { err := basedi.RegisterLoadBean(singleNameDaoSingle, LoadDaoSingle) if err != nil { logrus.Error("register bean error!", err) } } // 加载单例 func LoadDaoSingle() baseiface.ISingleton { var inst = NewDaoSingle() InjectDaoSingle(inst) return inst }

3.3 获取单例实例

go

func FindBeanDaoSingle() *DaoSingle { if bean, ok := basedi.FindBeanOk(singleNameDaoSingle); ok { return bean.(*DaoSingle) } logrus.Error("not find bean!") return nil }

4. 公共 API

4.1 Facade 获取方法

方法返回类型说明
FindBeanPlanFacade()*daofacade.PlanFacade获取训练计划 Facade

go

func FindBeanPlanFacade() *daofacade.PlanFacade { return FindBeanDaoSingle().PlanFacade }

4.2 用户缓存查询方法

方法参数返回类型说明
CacheFillName()nameField *string,userId int64*pagemodel.IchubResult[*userentity.Users]根据用户ID填充用户名
CacheFindUser()userId int64*pagemodel.IchubResult[*userentity.Users]从缓存查询用户信息

go

// 从缓存查询用户信息 func CacheFindUser(userId int64) *pagemodel.IchubResult[*userentity.Users] { return FindBeanDaoSingle().UserDaoFacade.FindByIdAtCache(userId) } // 填充用户名称到指定字段 func CacheFillName(nameField *string, userId int64) *pagemodel.IchubResult[*userentity.Users] { return FindBeanDaoSingle().UserDaoFacade.CacheFillName(nameField, userId) }

5. 使用示例

5.1 获取计划 Facade

go

// 获取训练计划 Facade planFacade := daosingle.FindBeanPlanFacade() // 使用 Facade 执行查询 plans, err := planFacade.FindByStudentId(studentId) if err != nil { // 处理错误 }

5.2 查询用户缓存

go

// 从缓存查询用户 result := daosingle.CacheFindUser(userId) if result.IsSuccess() { user := result.Data fmt.Printf("用户名: %s\n", user.Name) } // 填充用户名到字段 var userName string result := daosingle.CacheFillName(&userName, userId) if result.IsSuccess() { fmt.Printf("用户名已填充: %s\n", userName) }

5.3 直接使用 DaoSingle

go

// 获取 DaoSingle 实例 dao := daosingle.FindBeanDaoSingle() if dao == nil { log.Error("DaoSingle 初始化失败") return } // 调用各个 Facade // 1. 查询训练计划 plans, _ := dao.PlanFacade.FindAll() // 2. 查询用户 user, _ := dao.UserDaoFacade.FindById(123) // 3. 查询单词 words, _ := dao.WordDaoFacade.Search("apple") // 4. 记录操作日志 dao.OpcFacade.Record("train", "start", "用户开始训练")

6. 扩展指南

6.1 添加新的 Facade

步骤 1:在DaoQuery中添加新字段

go

type DaoQuery struct { basedto.BaseEntity *daofacade.CreditFacade *daofacade.OpcFacade *daofacade.PlanFacade // ... 现有字段 ... *daofacade.NewFacade // 添加新 Facade }

步骤 2:在Init()方法中初始化

go

func (self *DaoQuery) Init() { // ... 现有初始化 ... self.NewFacade = daofacade.FindBeanNewFacade() }

步骤 3:在ShutDown()方法中添加清理

go

func (self *DaoQuery) ShutDown() { // ... 现有清理 ... self.NewFacade.Shutdown() }

步骤 4(可选):在daosingle包中添加便捷方法

go

func FindBeanNewFacade() *daofacade.NewFacade { return FindBeanDaoSingle().NewFacade }

7. 最佳实践

7.1 调用方式选择

场景推荐方式说明
频繁使用单一 Facade使用便捷方法(如FindBeanPlanFacade()代码更简洁
需要多个 Facade获取DaoSingle实例后直接调用减少多次获取开销
性能敏感场景缓存DaoSingle引用避免重复查找

7.2 错误处理

go

// 正确:检查返回结果 result := daosingle.CacheFindUser(userId) if !result.IsSuccess() { log.Warnf("查询用户失败: %v", result.Error) return nil } // 正确:检查单例是否为空 dao := daosingle.FindBeanDaoSingle() if dao == nil { log.Error("DaoSingle 未初始化") return }

7.3 避免滥用

  • 不要在 DAO 层调用DaoSingle是门面层,不应在 DAO 内部使用,避免循环依赖
  • 不要存储到结构体字段:单例应通过FindBeanDaoSingle()获取,避免持有过期引用
  • 注意并发安全:虽然单例本身是线程安全的,但具体 Facade 的方法需要确认是否线程安全

8. 依赖关系

plainText

DaoSingle ├── basedto.BaseEntitySingle (基础单例能力) └── DaoQuery ├── basedto.BaseEntity (基础实体能力) ├── CreditFacade (积分系统) ├── OpcFacade (操作日志) ├── PlanFacade (训练计划) ├── StudentFacade (学生管理) ├── UserDaoFacade (用户管理) ├── WordDaoFacade (单词管理) └── KbaseFacade (知识库)

9. 总结

DaoSingle作为数据访问层的统一门面,提供了以下价值:

价值说明
简化调用统一入口,无需关注底层 DAO 实现
解耦业务业务层只依赖门面,不依赖具体 DAO
单例管理通过依赖注入容器统一管理生命周期
缓存支持内置用户缓存查询能力
扩展性强新增 Facade 只需修改DaoQuery

文档版本:v1.0

创建时间:2026-05-20

适用范围:GoTrain 项目数据访问层开发

import ( "gitea.super-study.com/ys-study/gotrain/beapi/facade/daoquery" "gitee.com/gowebframe3/webframe.git/goconfig/base/basedto" ) type DaoSingle struct { basedto.BaseEntitySingle *daoquery.DaoQuery } func NewDaoSingle() *DaoSingle { return &DaoSingle{ DaoQuery: daoquery.FindBeanDaoQuery(), } } package daosingle import ( "gitee.com/gowebframe3/webframe.git/goconfig/base/baseiface" "gitee.com/gowebframe3/webframe.git/goconfig/basedi" "github.com/sirupsen/logrus" ) /* @Title 文件名称: dao_single_init.go @Desp 描述: 依赖自动注入 @Company 公司: www.learn.com @Author 作者: raymond@163.com 时间: 2026-05-17 01:23:23 @Update 作者: raymond@163.com 时间: 2026-05-17 01:23:23 */ var singleNameDaoSingle = "*daosingle.DaoSingle-98055d62-ef8f-4464-a518-57ddd65c92d9" // init register load func init() { registerBeanDaoSingle() } // register DaoSingle func registerBeanDaoSingle() { err := basedi.RegisterLoadBean(singleNameDaoSingle, LoadDaoSingle) if err != nil{ logrus.Error("register bean error!", err) } } // FindBeanDaoSingle func FindBeanDaoSingle() *DaoSingle { if bean, ok := basedi.FindBeanOk(singleNameDaoSingle); ok { return bean.(*DaoSingle) } logrus.Error("not find bean!") return nil } func LoadDaoSingle() baseiface.ISingleton { var inst = NewDaoSingle() InjectDaoSingle(inst) return inst } func InjectDaoSingle(s *DaoSingle) { // s.Init() }
package daosingle import "gitea.super-study.com/ys-study/gotrain/beapi/facade/daofacade" func FindBeanPlanFacade() *daofacade.PlanFacade { return FindBeanDaoSingle().PlanFacade } package daosingle import ( "gitea.super-study.com/ys-study/gotrain/beapi/db/dbentity/userentity" "gitee.com/gowebframe3/webframe.git/goweb/pagemodel" ) func CacheFillName(nameField *string, userId int64) *pagemodel.IchubResult[*userentity.Users] { return FindBeanDaoSingle().UserDaoFacade.CacheFillName(nameField, userId) } func CacheFindUser(userId int64) *pagemodel.IchubResult[*userentity.Users] { return FindBeanDaoSingle().UserDaoFacade.FindByIdAtCache(userId) }
package daoquery import ( "gitea.super-study.com/ys-study/gotrain/beapi/facade/daofacade" "gitee.com/gowebframe3/webframe.git/goconfig/base/basedto" ) type DaoQuery struct { basedto.BaseEntity *daofacade.CreditFacade *daofacade.OpcFacade *daofacade.PlanFacade *daofacade.StudentFacade *daofacade.UserDaoFacade *daofacade.WordDaoFacade *daofacade.KbaseFacade } func NewDaoQuery() *DaoQuery { return &DaoQuery{} } func (self *DaoQuery) Init() { self.CreditFacade = daofacade.FindBeanCreditFacade() self.OpcFacade = daofacade.FindBeanOpcFacade() self.PlanFacade = daofacade.FindBeanPlanFacade() self.StudentFacade = daofacade.FindBeanStudentFacade() self.UserDaoFacade = daofacade.FindBeanUserDaoFacade() self.WordDaoFacade = daofacade.FindBeanWordDaoFacade() self.KbaseFacade = daofacade.FindBeanKbaseFacade() } func (self *DaoQuery) AutoInit() bool { return true } func (self *DaoQuery) ShutDown() { self.CreditFacade.Shutdown() self.OpcFacade.Shutdown() self.PlanFacade.Shutdown() self.StudentFacade.Shutdown() self.UserDaoFacade.Shutdown() self.WordDaoFacade.Shutdown() self.KbaseFacade.Shutdown() }
package daofacade import ( "gitea.super-study.com/ys-study/gotrain/beapi/db/dbdao/creditdao" "gitea.super-study.com/ys-study/gotrain/beapi/gotool/dbframe/dbsequence" "gitee.com/gowebframe3/webframe.git/goconfig/base/basedto" ) // 多例使用区 type CreditFacade struct { basedto.BaseEntity CreditSchemeDao *creditdao.CreditSchemeDao CreditTransactionsDao *creditdao.CreditTransactionsDao } func NewCreditFacade() *CreditFacade { return &CreditFacade{} } func (self *CreditFacade) Init() { self.CreditSchemeDao = creditdao.FindBeanCreditSchemeDao() self.CreditTransactionsDao = creditdao.FindBeanCreditTransactionsDao() } func (self *CreditFacade) AutoInit() bool { return true } func (self *CreditFacade) InitAllTransNoWhenEmptty() *basedto.IchubResult { var dao = self.CreditTransactionsDao.NewDao() dao.DbOrFieldsCurrent().DbEq("trans_no", "").DbIsNull("trans_no") var ret = dao.QueryModelMax() if ret.ExistRecord() { for _, item := range ret.Data { if item.TransNo == "" { item.TransNo, _ = dbsequence.FindBeanDbFacade().FindSeqTransNo() dao.UpdateMap2Result(item.Id, map[string]any{ "trans_no": item.TransNo, }) } } } return &ret.IchubResult }
package daofacade import ( "gitea.super-study.com/ys-study/gotrain/beapi/common" "gitea.super-study.com/ys-study/gotrain/beapi/db/dbdao/userdao" "gitea.super-study.com/ys-study/gotrain/beapi/db/dbentity/userentity" "gitea.super-study.com/ys-study/gotrain/beapi/db/dbparam" "gitee.com/gowebframe3/webframe.git/goconfig/base/basedto" "gitee.com/gowebframe3/webframe.git/goweb/pagefacade" "gitee.com/gowebframe3/webframe.git/goweb/pagemodel" "github.com/gogf/gf/v2/util/gconv" ) type UserDaoFacade struct { basedto.BaseEntity common.BaseService UsersDao *userdao.UsersDao PagePtrFacade *pagefacade.PagePtrFacade[*userentity.Users, *dbparam.StudentParam] UserExtendDao *userdao.UserExtendDao } func NewUserDaoFacade() *UserDaoFacade { return &UserDaoFacade{} } func (self *UserDaoFacade) Init() { self.UsersDao = userdao.FindBeanUsersDao() self.UserExtendDao = userdao.FindBeanUserExtendDao() } func (self *UserDaoFacade) AutoInit() bool { return true } func (self *UserDaoFacade) UpdateStudent(param *dbparam.StudentParam) *basedto.IchubResult { //var p,err=self.PagePtrFacade.From(param) var ret = self.CheckPhone(param) if ret.ExistRecord() { return basedto.ResultFailMsg("手机号已存在") } return self.UsersDao.UpdateMap2Result(param.Id, param.Stru2MapFields("id")) } func (self *UserDaoFacade) CheckPhone(param *dbparam.StudentParam) *pagemodel.PageResult[*userentity.Users] { var dao = self.UsersDao.NewDao() dao.DbNe("id", param.Id) dao.DbEq("phone", param.Phone) var ret = dao.QueryModelOne() return ret } func (self *UserDaoFacade) FindByIdAtCache(userIds any) *pagemodel.IchubResult[*userentity.Users] { var userId = gconv.Int64(userIds) if userId == 0 { return pagemodel.ResultOk(&userentity.Users{}, false) } var rets, exist = self.GetCache(userentity.FindBeanUsers().ObjectKeyOfPkey(userId)) if exist { return rets.(*pagemodel.IchubResult[*userentity.Users]) } var dao = userdao.FindBeanUsersDao() var ret = dao.FindByIdResult(userId) if ret.ExistRecord() { self.SetCache(ret.Data.ObjectKey(), ret) } return ret } func (self *UserDaoFacade) CacheFillName(nameField *string, userId int64) *pagemodel.IchubResult[*userentity.Users] { if userId == 0 { return pagemodel.ResultOk(&userentity.Users{}, false) } var ret = self.FindByIdAtCache(userId) if ret.ExistRecord() { *nameField = ret.Data.Name } return ret }

DBQueryPool

package daopool import ( "gitea.super-study.com/ys-study/gotrain/beapi/facade/daoquery" "gitee.com/gowebframe3/webframe.git/goconfig/base/basedto" "gitee.com/gowebframe3/webframe.git/gomini/gopool" ) type DaoQueryPool struct { basedto.BaseEntitySingle *gopool.GeneralObjectPool[*daoquery.DaoQuery] } func NewDaoQueryPool() *DaoQueryPool { return &DaoQueryPool{ GeneralObjectPool: gopool.NewGeneralObjectPool[*daoquery.DaoQuery](), } }
package daopool import ( "gitee.com/gowebframe3/webframe.git/goconfig/base/baseiface" "gitee.com/gowebframe3/webframe.git/goconfig/basedi" "github.com/sirupsen/logrus" ) /* @Title 文件名称: dao_query_pool_init.go @Desp 描述: 依赖自动注入 @Company 公司: www.learn.com @Author 作者: raymond@163.com 时间: 2026-05-17 00:32:37 @Update 作者: raymond@163.com 时间: 2026-05-17 00:32:37 */ var singleNameDaoQueryPool = "*daopool.DaoQueryPool-c397125c-7d8b-42a7-a44f-23773919d71c" // init register load func init() { registerBeanDaoQueryPool() } // register DaoQueryPool func registerBeanDaoQueryPool() { err := basedi.RegisterLoadBean(singleNameDaoQueryPool, LoadDaoQueryPool) if err != nil{ logrus.Error("register bean error!", err) } } // FindBeanDaoQueryPool func FindBeanDaoQueryPool() *DaoQueryPool { if bean, ok := basedi.FindBeanOk(singleNameDaoQueryPool); ok { return bean.(*DaoQueryPool) } logrus.Error("not find bean!") return nil } func LoadDaoQueryPool() baseiface.ISingleton { var inst = NewDaoQueryPool() InjectDaoQueryPool(inst) return inst } func InjectDaoQueryPool(s *DaoQueryPool) { // s.Init() }
func (suite *TestViewUiSuite) Test007_borrowDaoQuery() { var pool = daopool.FindBeanDaoQueryPool() var daoQuery, err = pool.BorrowObject(context.Background()) if err != nil { golog.Error("borrow object error!", err) return } daoQuery.WordsDao.DbEq("id", 101552) var ret = daoQuery.WordsDao.QueryModel() golog.Info(ret) pool.ReturnObject(context.Background(), daoQuery) daoQuery, err = pool.BorrowObject(context.Background()) daoQuery.WordsDao.DbEq("id", 7125) ret = daoQuery.WordsDao.QueryModel() golog.Info(ret, err) }
http://www.cnnetsun.cn/news/2473230.html

相关文章:

  • MSP430新手避坑指南:CCS里driverlib.h库找不到?手把手教你从TI官网下载MSPWare搞定
  • HoRain云--skill技能依赖管理全攻略
  • 从CPU到密码学:揭秘异或(XOR)与非门(NAND)如何构建现代数字世界
  • 5个实战技巧:用ta4j构建专业Java量化交易系统
  • 5分钟快速上手WuWa-Mod:解锁《鸣潮》游戏无限潜能的终极指南
  • 2026年新手电钢琴怎么选?8款高性价比88键重锤推荐与避坑指南
  • 基于STM32U5与LVGL的智能大棚温控系统:从传感器到MQTT的物联网实战
  • 手把手实战!用Multisim剖析运算放大器噪声谱与关键贡献源
  • 跨平台B站下载神器BiliTools:一站式解决你的离线观看需求
  • AI应用的安全防护:从输入到输出的全链路安全
  • FFmpeg Batch AV Converter:告别命令行,批量视频转换从未如此简单
  • 告别虚拟机!用DosBox在Win10/Win11上重温经典DOS汇编开发环境
  • RT-Thread文件系统实战:从VFS原理到FAT/LittleFS选型与OTA应用
  • Agentic Design Patterns-模式3:并行化(Parallelization)的代码实现
  • 索尼X8566F电视过保即坏?拆解分析SR260二极管背后的设计疑云与低成本自救方案
  • ZLUDA深度解析:突破CUDA生态壁垒的异构GPU计算解决方案
  • DayZ单机模组终极指南:打造专属末日世界的5个关键步骤
  • 从HS0038到智能遥控:基于STM32的红外信号解码与云台控制实战
  • 从Middlebury霸榜到商业落地:手把手拆解PatchMatch Stereo的C++/Python实现核心
  • 用FreeRTOS消息队列+栈管理LVGL页面,我在STM32F7上实现手表按键切换的完整流程
  • 为什么你的DeepSeek服务P99延迟飙升300ms?——基于nvidia-smi+dcgm-exporter的GPU资源争用实时诊断指南
  • CentOS 7.9 虚拟机图形化实战:GParted 磁盘分区、挂载与扩容全流程
  • BGP状态机详解:从邻居建立到故障排查的完整指南
  • LabVIEW生产者消费者模式:队列操作与多线程架构实战
  • 深入解析LuaJIT反编译器v2:从字节码到可读代码的专业转换工具
  • 别再让WSL2吃光C盘了!手把手教你迁移Ubuntu 22.04到D盘(附VSCode无缝连接)
  • 别再只扫描端口了!手把手教你用HFish蜜罐捕获SSH爆破和Web目录扫描(Windows管理端+CentOS节点)
  • 终极Moonlight流媒体指南:5个技巧实现iOS/tvOS跨平台游戏串流
  • SPOD频谱正交分解:3步掌握流体动力学模态分析的核心技术
  • 初创公司如何借助TaoToken快速原型开发并精细化控制AI成本