Golang Gorm实现自定义多态模型关联查询

一、表结构

CREATE TABLE `orders` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `order_no` varchar(32) NOT NULL DEFAULT '',
  `orderable_id` int unsigned NOT NULL DEFAULT '0',
  `orderable_type` char(30) NOT NULL DEFAULT '',
  `status` tinyint unsigned NOT NULL DEFAULT '0',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
CREATE TABLE `phone` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `phone_name` varchar(50) NOT NULL DEFAULT '',
  `phone_model` varchar(30) NOT NULL DEFAULT '',
  `status` tinyint unsigned NOT NULL DEFAULT '0',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
 
CREATE TABLE `cars` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `car_name` varchar(50) NOT NULL DEFAULT '',
  `car_model` varchar(30) NOT NULL DEFAULT '',
  `status` tinyint unsigned NOT NULL DEFAULT '0',
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  `deleted_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

二、接口定义

// LoaderAble 接口定义数据加载行为
type LoaderAble interface {
	LoadAble(IDs []int) (map[int]any, error)
}
 
// LoadAbleItem 接口定义了可加载项的通用方法
type LoadAbleItem interface {
	GetAbleType() string        // 获取类型键值
	GetAbleID() int             // 获取ID
	SetLoadedAbleData(data any) // 设置加载的数据
}

三、模型定义并实现接口

type Order struct {
	Id            int            `json:"id"`
	OrderNo       string         `json:"order_no"`
	OrderableId   int            `json:"orderable_id"`
	OrderableType string         `json:"orderable_type"`
	Orderable     any            `json:"orderable" gorm:"-"`
	Status        uint8          `json:"status"`
	CreatedAt     *time.Time     `json:"created_at"`
	UpdatedAt     *time.Time     `json:"updated_at"`
	DeletedAt     gorm.DeletedAt `json:"deleted_at"`
}
 
func (tb *Order) TableName() string {
	return "orders"
}
 
func (tb *Order) GetAbleType() string {
	return tb.OrderableType
}
 
func (tb *Order) GetAbleID() int {
	return tb.OrderableId
}
 
func (tb *Order) SetLoadedAbleData(data any) {
	tb.Orderable = data
}
 
 
 
//--------------------------------------分割线--------------------------------------
 
 
type Car struct {
	Id        int            `json:"id"`
	CarName   string         `json:"car_name"`
	CarModel  string         `json:"car_model"`
	Status    uint8          `json:"status"`
	CreatedAt *time.Time     `json:"created_at"`
	UpdatedAt *time.Time     `json:"updated_at"`
	DeletedAt gorm.DeletedAt `json:"deleted_at"`
}
 
func (tb *Car) TableName() string {
	return "cars"
}
 
// CarLoaderAble 实现 Loader 接口
type CarLoaderAble struct{}
 
// LoadAble 具体实现加载多态关联逻辑
// IDs 多态关联类型ID(主要参数)
func (loader *CarLoaderAble) LoadAble(IDs []int) (resultMap map[int]any, err error) {
	IDsLen := len(IDs)
	if IDsLen == 0 {
		return
	}
 
	car := make([]*Car, 0, IDsLen)
	err = mysql.DefaultMysql.Db.Debug().Where("id IN (?) AND status = ?", IDs, 1).Find(&car).Error
	if err != nil {
		return
	}
 
	resultMap = make(map[int]any, IDsLen)
	for _, item := range car {
		resultMap[item.Id] = item
	}
	return
}
 
 
//--------------------------------------分割线--------------------------------------
 
 
type Phone struct {
	Id         int            `json:"id"`
	PhoneName  string         `json:"phone_name"`
	PhoneModel string         `json:"phone_model"`
	Status     uint8          `json:"status" gorm:"column:status"`
	StatusNew  uint8          `json:"status_new" gorm:"-"`
	CreatedAt  *time.Time     `json:"created_at"`
	UpdatedAt  *time.Time     `json:"updated_at"`
	DeletedAt  gorm.DeletedAt `json:"deleted_at"`
}
 
func (tb *Phone) TableName() string {
	return "phone"
}
 
func (tb *Phone) AfterFind(tx *gorm.DB) (err error) {
	tb.StatusNew = tb.Status
	return
}
 
// PhoneLoaderAble 实现 Loader 接口
type PhoneLoaderAble struct{}
 
// LoadAble 具体实现加载多态关联逻辑
// IDs 多态关联类型ID(主要参数)
func (loader *PhoneLoaderAble) LoadAble(IDs []int) (resultMap map[int]any, err error) {
	IDsLen := len(IDs)
	if IDsLen == 0 {
		return
	}
 
	phone := make([]*Phone, 0, IDsLen)
	err = mysql.DefaultMysql.Db.Debug().Where("id IN (?) AND status = ?", IDs, 1).Find(&phone).Error
	if err != nil {
		return
	}
 
	resultMap = make(map[int]any, IDsLen)
	for _, item := range phone {
		resultMap[item.Id] = item
	}
	return
}

四、创建loader预加载器

// LoaderAbleFactory 用于管理不同类型的加载器
type LoaderAbleFactory struct {
	loaders map[string]LoaderAble
}
 
func NewLoaderAbleFactory() *LoaderAbleFactory {
	return &LoaderAbleFactory{
		loaders: make(map[string]LoaderAble),
	}
}
 
func (f *LoaderAbleFactory) RegisterLoader(typeName string, loader LoaderAble) {
	f.loaders[typeName] = loader
}

五、注册loader预加载器服务

var (
	loaderAbleFactory *LoaderAbleFactory
)
 
// init 选择在项目启动初始化时进行全局加载
func init() {
	loaderAbleFactory = NewLoaderAbleFactory()
	loaderAbleFactory.RegisterLoader("phone", &PhoneLoaderAble{})
	loaderAbleFactory.RegisterLoader("car", &CarLoaderAble{})
	log.Println("多态模型关系注册成功...")
}

六、实现LoadAble通用的加载函数

// LoadAble 通用的加载函数,可以处理任何实现了 LoadableItem 接口的切片
func LoadAble[T LoadAbleItem](items []T) error {
	if len(items) == 0 {
		return nil
	}
 
	// 按类型分组收集ID
	typeIDsMap := make(map[string][]int)
	for _, item := range items {
		typeKey := item.GetAbleType()
		typeIDsMap[typeKey] = append(typeIDsMap[typeKey], item.GetAbleID())
	}
 
	// 使用对应的loader加载数据
	typeDataMap := make(map[string]map[int]any)
	for typeName, ids := range typeIDsMap {
		loader, ok := loaderAbleFactory.loaders[typeName]
		if !ok {
			continue
		}
 
		resultMap, err := loader.LoadAble(ids)
		if err != nil {
			return err
		}
		typeDataMap[typeName] = resultMap
	}
 
	// 填充数据
	for _, item := range items {
		if dataMap, ok := typeDataMap[item.GetAbleType()]; ok {
			if data, exists := dataMap[item.GetAbleID()]; exists {
				item.SetLoadedAbleData(data)
			}
		}
	}
	return nil
}

七、调试

  • 准备数据
--orders表
INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1, '202411010001', 1002, 'car', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL);
INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (2, '202411010002', 1001, 'phone', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL);
INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (3, '202411010003', 1000, 'car', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL);
INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (4, '202411010004', 1001, 'car', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL);
INSERT INTO `orders` (`id`, `order_no`, `orderable_id`, `orderable_type`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (5, '202411010005', 1002, 'phone', 1, '2024-11-01 12:03:03', '2024-11-01 12:03:06', NULL);
 
--phone表
INSERT INTO `phone` (`id`, `phone_name`, `phone_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1000, 'XiaoMi', '2S', 2, '2024-11-01 11:59:37', '2024-11-01 11:59:40', NULL);
INSERT INTO `phone` (`id`, `phone_name`, `phone_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1001, 'HUAWEI', 'mate60', 1, '2024-11-01 11:59:54', '2024-11-01 11:59:57', NULL);
INSERT INTO `phone` (`id`, `phone_name`, `phone_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1002, 'Apple', 'iPhone 12 Pro Max', 1, '2024-11-01 12:00:26', '2024-11-01 12:00:28', NULL);
 
--cars表
INSERT INTO `cars` (`id`, `car_name`, `car_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1000, '奥迪', 'A6L', 1, '2024-11-01 11:57:53', '2024-11-01 11:57:55', NULL);
INSERT INTO `cars` (`id`, `car_name`, `car_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1001, '宝马', '5系', 1, '2024-11-01 11:58:12', '2024-11-01 11:58:15', NULL);
INSERT INTO `cars` (`id`, `car_name`, `car_model`, `status`, `created_at`, `updated_at`, `deleted_at`) VALUES (1002, '奔驰', 'E300', 1, '2024-11-01 11:58:53', '2024-11-01 11:58:56', NULL);
  • 编写代码
// Gin框架+Gorm为例
api.GET("/orders", controller.GetOrderList)
 
// GetOrderList 获取订单列表接口
func GetOrderList(c *gin.Context) {
	orders := make([]*model.Order, 0)
	err := mysql.DefaultMysql.Db.Debug().Find(&orders).Error
	if err != nil {
		c.JSON(200, gin.H{"error": err.Error()})
		return
	}
	err = model.LoadAble(orders)
	if err != nil {
		c.JSON(200, gin.H{"error": err.Error()})
		return
	}
	c.JSON(200, gin.H{"data": orders})
}
  • 发起请求
curl '127.0.0.1:16888/api/orders'
{
    "data": [
        {
            "id": 1,
            "order_no": "202411010001",
            "orderable_id": 1002,
            "orderable_type": "car",
            "orderable": {
                "id": 1002,
                "car_name": "奔驰",
                "car_model": "E300",
                "status": 1,
                "created_at": "2024-11-01T11:58:53+08:00",
                "updated_at": "2024-11-01T11:58:56+08:00",
                "deleted_at": null
            },
            "status": 1,
            "created_at": "2024-11-01T12:03:03+08:00",
            "updated_at": "2024-11-01T12:03:06+08:00",
            "deleted_at": null
        },
        {
            "id": 2,
            "order_no": "202411010002",
            "orderable_id": 1001,
            "orderable_type": "phone",
            "orderable": {
                "id": 1001,
                "phone_name": "HUAWEI",
                "phone_model": "mate60",
                "status": 1,
                "status_new": 1,
                "created_at": "2024-11-01T11:59:54+08:00",
                "updated_at": "2024-11-01T11:59:57+08:00",
                "deleted_at": null
            },
            "status": 1,
            "created_at": "2024-11-01T12:03:03+08:00",
            "updated_at": "2024-11-01T12:03:06+08:00",
            "deleted_at": null
        },
        {
            "id": 3,
            "order_no": "202411010003",
            "orderable_id": 1000,
            "orderable_type": "car",
            "orderable": {
                "id": 1000,
                "car_name": "奥迪",
                "car_model": "A6L",
                "status": 1,
                "created_at": "2024-11-01T11:57:53+08:00",
                "updated_at": "2024-11-01T11:57:55+08:00",
                "deleted_at": null
            },
            "status": 1,
            "created_at": "2024-11-01T12:03:03+08:00",
            "updated_at": "2024-11-01T12:03:06+08:00",
            "deleted_at": null
        },
        {
            "id": 4,
            "order_no": "202411010004",
            "orderable_id": 1001,
            "orderable_type": "car",
            "orderable": {
                "id": 1001,
                "car_name": "宝马",
                "car_model": "5系",
                "status": 1,
                "created_at": "2024-11-01T11:58:12+08:00",
                "updated_at": "2024-11-01T11:58:15+08:00",
                "deleted_at": null
            },
            "status": 1,
            "created_at": "2024-11-01T12:03:03+08:00",
            "updated_at": "2024-11-01T12:03:06+08:00",
            "deleted_at": null
        },
        {
            "id": 5,
            "order_no": "202411010005",
            "orderable_id": 1002,
            "orderable_type": "phone",
            "orderable": {
                "id": 1002,
                "phone_name": "Apple",
                "phone_model": "iPhone 12 Pro Max",
                "status": 1,
                "status_new": 1,
                "created_at": "2024-11-01T12:00:26+08:00",
                "updated_at": "2024-11-01T12:00:28+08:00",
                "deleted_at": null
            },
            "status": 1,
            "created_at": "2024-11-01T12:03:03+08:00",
            "updated_at": "2024-11-01T12:03:06+08:00",
            "deleted_at": null
        }
    ]
}

以上就是Golang Gorm实现自定义多态模型关联查询的详细内容,更多关于Golang Gorm关联查询的资料请关注恩蓝小号其它相关文章!

原创文章,作者:XOTBH,如若转载,请注明出处:http://www.wangzhanshi.com/n/5661.html

(0)
XOTBH的头像XOTBH
上一篇 2024年12月17日 19:27:52
下一篇 2024年12月17日 19:27:55

相关推荐

  • 浅析Go语言中的逃逸分析

    逃逸分析算是go语言的特色之一,编译器自动分析变量/内存应该分配在栈上还是堆上,程序员不需要主动关心这些事情,保证了内存安全的同时也减轻了程序员的负担。 然而这个“减轻…

    Golang 2024年12月17日
  • Go语言中如何进行包管理

    Go语言是一种编译型、静态类型、并发型、并具有垃圾回收功能的编程语言。在Go语言中,包(package)是函数和数据的集合,用于组织代码,实现模块化开发。通过包,开发者可以将相关的…

    Golang 2024年12月17日
  • Go语言实现本地缓存的策略详解

    1. Go语言本地缓存的实现 Go语言实现本地缓存是非常容易的,考虑到语言本身的特性,只要解决了“并发安全”问题,基本就可以在生产环境中使用了,常见的解决方…

    2024年12月26日
  • Golang实现简易的命令行功能

    前言 一次偶然的想法,想知道为什么在终端输入那些命令行后,就执行了对应的操作,这转化为代码,应该怎么实现呢? 既然有了问题,那我们就来解决问题吧! 首先我认为想做命令行操作,那就得…

    2024年12月26日
  • Go语言制作svg格式树形图的示例代码

    最近一直在刷二叉树题目,但在要验证结果时,通常用中序遍历、层序遍历查看结果,验证起来没有画图来得直观,所有想到自己动手制作二叉树的树形图。 直接开干,先从svg入手: 什么是SVG…

    2024年12月26日
  • Golang并发编程中Context包的使用与并发控制

    一、简介 在并发编程中,任务管理和资源控制是非常重要的,而 Golang 的 context 包 为我们提供了一种优雅的方式来传递取消信号和…

    Golang 2024年12月17日
  • Go语言使用漏桶算法和令牌桶算法来实现API限流

    在现代 Web 应用程序中,流量的突增是不可避免的。为防止服务器被过多的请求压垮,限流(Rate Limiting) 是一个至关重要的技术手段。 本文将通过 Go 语言的 Gin …

    Golang 2024年12月17日
  • 如何使用go实现创建WebSocket服务器

    使用Go语言创建WebSocket服务器可以利用现有的库来简化开发过程。gorilla/websocket 是一个非常流行且功能强大的库,适用于Go语言的WebSocket应用。下…

    Golang 2024年12月17日
  • go开发过程中mapstructure使用示例详解

    mapstructure用法 mapstructure 是一个流行的 Go 库,主要用于将映射(如 map 或 struct)解码为结构体。它通常用于从配置文件(如 JSON、YA…

    Golang 2024年12月17日
  • Golang调用FFmpeg转换视频流的实现

    问题背景 问题背景是在,由于视频采集端使用的是H264编码采集的裸流,而网络流媒体大多是以FLV为主的直播方式进行的,为了实现实时直播,当前是打算直接使用FFmpeg将H264裸流…

    Golang 2024年12月29日

发表回复

登录后才能评论