不灭的焱

加密类型:SHA/AES/RSA下载Go
复合类型:切片(slice)、映射(map)、指针(pointer)、函数(function)、通道(channel)、接口(interface)、数组(array)、结构体(struct) Go类型+零值nil
引用类型:切片(slice)、映射(map)、指针(pointer)、函数(function)、通道(channel) Go引用

作者:AlbertWen  添加时间:2025-12-28 02:08:57  修改时间:2025-12-28 13:24:38  分类:02.Go语言编程  编辑

一、先明确核心定义(一句话概括)

  1. entity(实体):数据结构的纯数据载体,仅定义数据字段(对应数据库表字段),无任何业务逻辑 / 数据库操作,是三者中最 “纯净” 的层。
  2. do(Data Object,数据操作对象):面向数据库操作的封装,专注于数据库的 CRUD 条件构造、字段赋值,是衔接dao和数据库的操作载体。
  3. dao(Data Access Object,数据访问对象):数据访问层,提供直接可调用的数据库操作方法,封装了与数据库的交互逻辑,对外屏蔽数据库操作细节。

二、核心区别(清晰对比)

维度 entity do dao
核心定位 纯数据载体(数据模型) 数据库操作载体(条件 / 赋值封装) 数据访问层(提供操作方法)
包含内容 仅字段定义(对应数据库表)、结构体标签 字段赋值、查询条件、更新条件、关联操作封装 具体的 CRUD 方法(查询、新增、更新、删除)
有无业务 / 数据库逻辑 无(纯结构体,无任何方法) 仅数据库操作相关逻辑(条件构造) 封装数据库交互逻辑(与数据库直接通信)
使用者 上层业务逻辑(接收 / 返回数据) dao层(构造操作条件)、上层业务(辅助赋值) 上层业务逻辑(直接调用完成数据库操作)
灵活性 低(结构固定,仅承载数据) 高(支持动态构造查询 / 更新条件) 中(方法封装,直接调用即可)

三、三者的联系

  1. 依赖关系:dao 依赖 do 完成数据库操作的条件构造,entity 可作为 dao 操作的输入 / 输出数据格式(do 也可与 entity 相互转换)。
  2. 数据流转:上层业务 → (可选)构造 entity 承载数据 → 转换为 do 构造操作条件 → 调用 dao 方法执行数据库操作 → dao 返回结果 → (可选)转换为 entity 供上层业务使用。
  3. 整体目标:三者协同实现 “数据与操作分离”,降低耦合,提高代码的可维护性和可复用性。

四、实操示例(基于 GoFrame v2)

前提准备

假设我们有一个数据库表 user,结构如下:

CREATE TABLE `user` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` VARCHAR(50) NOT NULL COMMENT '用户名',
  `age` TINYINT(3) UNSIGNED DEFAULT 0 COMMENT '年龄',
  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

1. entity 定义(纯数据载体)

通常放在 entity/user_entity.go 文件中,仅定义结构体,无任何方法,字段与数据库表一一对应。

package entity

import (
	"time"
)

// User 对应数据库user表的纯数据实体
// 仅承载数据,无任何业务/数据库操作逻辑
type User struct {
    Id         int64     `json:"id"        orm:"column:id" description:"用户ID"`            // 用户ID
    Username   string    `json:"username"  orm:"column:username" description:"用户名"`      // 用户名
    Age        uint8     `json:"age"       orm:"column:age" description:"年龄"`             // 年龄
    CreateTime time.Time `json:"createTime" orm:"column:create_time" description:"创建时间"`// 创建时间
}

// 可选:定义列表类型,方便承载批量数据
type UserList []*User

2. do 定义(数据库操作载体)

通常放在 do/user_do.go 文件中,基于 GoFrame 的gdb封装,专注于条件构造,可通过gf gen工具自动生成,也可手动定义。

package do

import (
	"time"
	"github.com/gogf/gf/v2/database/gdb"
)

// UserDo 用户表的数据操作对象
// 用于构造数据库查询、更新、新增等操作的条件和字段赋值
type UserDo struct {
	gdb.Model `gorm:"table:user,alias:u"` // 绑定数据库表和别名

	Id         int64     `json:"id"        gorm:"column:id"`          // 与entity字段一致
	Username   string    `json:"username"  gorm:"column:username"`    // 与entity字段一致
	Age        uint8     `json:"age"       gorm:"column:age"`         // 与entity字段一致
	CreateTime time.Time `json:"createTime" gorm:"column:create_time"`// 与entity字段一致
}

// --------------- 以下是常用的条件构造方法(示例)---------------
// WhereId 构造ID等于条件
func (d *UserDo) WhereId(id int64) *UserDo {
	d.Where("id = ?", id)
	return d
}

// WhereUsernameLike 构造用户名模糊查询条件
func (d *UserDo) WhereUsernameLike(username string) *UserDo {
	d.Where("username LIKE ?", "%"+username+"%")
	return d
}

// WhereAgeGt 构造年龄大于条件
func (d *UserDo) WhereAgeGt(age uint8) *UserDo {
	d.Where("age > ?", age)
	return d
}

// WithCreateTimeBetween 构造创建时间区间条件
func (d *UserDo) WithCreateTimeBetween(start, end time.Time) *UserDo {
	d.Where("create_time BETWEEN ? AND ?", start, end)
	return d
}

// --------------- 字段赋值方法(示例)---------------
func (d *UserDo) SetUsername(username string) *UserDo {
	d.Username = username
	d.Set("username", username)
	return d
}

func (d *UserDo) SetAge(age uint8) *UserDo {
	d.Age = age
	d.Set("age", age)
	return d
}

3. dao 定义(数据访问层,提供操作方法)

通常放在 dao/user_dao.go 文件中,封装具体的数据库 CRUD 方法,对外提供简洁的调用接口,内部使用do构造条件。

package dao

import (
	"context"
	"time"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"

	"your-project/do"
	"your-project/entity"
)

// UserDao 用户表的数据访问对象
// 封装所有与user表相关的数据库操作,对外屏蔽实现细节
var UserDao = NewUserDao()

func NewUserDao() *userDao {
	return &userDao{}
}

type userDao struct{}

// --------------- 新增操作 ---------------
// Insert 新增单个用户
func (d *userDao) Insert(ctx context.Context, userDo *do.UserDo) (int64, error) {
	result, err := userDo.Save(ctx)
	if err != nil {
		return 0, err
	}
	return result.LastInsertId(ctx)
}

// --------------- 查询操作 ---------------
// GetById 根据ID查询单个用户,返回entity(供上层业务使用)
func (d *userDao) GetById(ctx context.Context, id int64) (*entity.User, error) {
	var userDo = do.NewUserDo() // 实例化do对象
	// 使用do构造查询条件,执行查询
	userEntity := &entity.User{}
	err := userDo.WhereId(id).Scan(ctx, userEntity)
	if err != nil {
		return nil, err
	}
	return userEntity, nil
}

// GetListByUsername 模糊查询用户列表,返回entity列表
func (d *userDao) GetListByUsername(ctx context.Context, username string) (entity.UserList, error) {
	var userDo = do.NewUserDo()
	userList := entity.UserList{}
	err := userDo.WhereUsernameLike(username).Scan(ctx, &userList)
	if err != nil {
		return nil, err
	}
	return userList, nil
}

// --------------- 更新操作 ---------------
// UpdateAgeById 根据ID更新用户年龄
func (d *userDao) UpdateAgeById(ctx context.Context, id int64, age uint8) (int64, error) {
	var userDo = do.NewUserDo()
	// 使用do构造更新条件和赋值
	result, err := userDo.WhereId(id).SetAge(age).Update(ctx)
	if err != nil {
		return 0, err
	}
	return result.RowsAffected(ctx)
}

// --------------- 删除操作 ---------------
// DeleteById 根据ID删除用户
func (d *userDao) DeleteById(ctx context.Context, id int64) (int64, error) {
	var userDo = do.NewUserDo()
	result, err := userDo.WhereId(id).Delete(ctx)
	if err != nil {
		return 0, err
	}
	return result.RowsAffected(ctx)
}

4. 上层业务调用示例(如何协同使用三者)

package service

import (
	"context"
	"your-project/dao"
	"your-project/do"
	"your-project/entity"
)

type UserService struct{}

var UserServiceIns = &UserService{}

// CreateUser 新增用户(业务层调用)
func (s *UserService) CreateUser(ctx context.Context, username string, age uint8) (int64, error) {
	// 1. 构造do对象(用于数据库操作)
	userDo := do.NewUserDo().SetUsername(username).SetAge(age)
	// 2. 调用dao层方法执行新增
	return dao.UserDao.Insert(ctx, userDo)
}

// GetUserInfo 根据ID查询用户信息(业务层调用)
func (s *UserService) GetUserInfo(ctx context.Context, id int64) (*entity.User, error) {
	// 直接调用dao层方法,返回entity(纯数据)供上层接口使用
	return dao.UserDao.GetById(ctx, id)
}

五、补充说明

  1. 实际开发中,do 和 dao 通常可以通过 GoFrame 的代码生成工具 gf gen dao 自动生成,无需手动编写大量重复代码,仅需补充自定义的条件构造和业务相关方法。
  2. entity 可根据业务需求灵活调整,比如忽略部分数据库字段、添加 json 标签适配接口返回,与数据库表字段无需完全一一对应(但核心业务字段需保持一致)。
  3. 三者的分层设计遵循 “单一职责原则”:entity 只管数据,do 只管操作条件,dao 只管数据访问,便于后续维护和扩展(比如更换数据库时,仅需修改daodo,无需改动上层业务和entity)。

总结

  1. entity 是 “数据容器”,纯结构体无逻辑,负责承载数据在各层之间流转。
  2. do 是 “操作工具”,负责构造数据库 CRUD 的条件和赋值,为dao提供支撑。
  3. dao 是 “访问入口”,封装数据库交互逻辑,对外提供简洁的操作方法,是上层业务与数据库之间的桥梁。
  4. 三者协同工作,实现了数据、操作、访问的分层解耦,是 GoFrame 框架中数据操作的核心最佳实践。