Golang Gorm實現(xiàn)自定義多態(tài)模型關(guān)聯(lián)查詢
更新時間:2024年11月04日 09:07:03 作者:erternalKing
GORM 是一個流行的開源 ORM (Object-Relational Mapping) 庫,專為 Go 語言設(shè)計,它簡化了與 SQL 數(shù)據(jù)庫的交互,GORM 封裝了數(shù)據(jù)庫操作,使得開發(fā)者能夠通過簡單的鏈?zhǔn)秸{(diào)用來執(zhí)行 CRUD,本文給大家介紹了Golang Gorm實現(xiàn)自定義多態(tài)模型關(guān)聯(lián)查詢,需要的朋友可以參考下
一、表結(jié)構(gòu)
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 接口定義數(shù)據(jù)加載行為
type LoaderAble interface {
LoadAble(IDs []int) (map[int]any, error)
}
// LoadAbleItem 接口定義了可加載項的通用方法
type LoadAbleItem interface {
GetAbleType() string // 獲取類型鍵值
GetAbleID() int // 獲取ID
SetLoadedAbleData(data any) // 設(shè)置加載的數(shù)據(jù)
}三、模型定義并實現(xiàn)接口
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 實現(xiàn) Loader 接口
type CarLoaderAble struct{}
// LoadAble 具體實現(xiàn)加載多態(tài)關(guān)聯(lián)邏輯
// IDs 多態(tài)關(guān)聯(lián)類型ID(主要參數(shù))
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 實現(xiàn) Loader 接口
type PhoneLoaderAble struct{}
// LoadAble 具體實現(xiàn)加載多態(tài)關(guān)聯(lián)邏輯
// IDs 多態(tài)關(guān)聯(lián)類型ID(主要參數(shù))
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
}四、創(chuàng)建loader預(yù)加載器
// 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預(yù)加載器服務(wù)
var (
loaderAbleFactory *LoaderAbleFactory
)
// init 選擇在項目啟動初始化時進(jìn)行全局加載
func init() {
loaderAbleFactory = NewLoaderAbleFactory()
loaderAbleFactory.RegisterLoader("phone", &PhoneLoaderAble{})
loaderAbleFactory.RegisterLoader("car", &CarLoaderAble{})
log.Println("多態(tài)模型關(guān)系注冊成功...")
}六、實現(xiàn)LoadAble通用的加載函數(shù)
// LoadAble 通用的加載函數(shù),可以處理任何實現(xiàn)了 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())
}
// 使用對應(yīng)的loader加載數(shù)據(jù)
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
}
// 填充數(shù)據(jù)
for _, item := range items {
if dataMap, ok := typeDataMap[item.GetAbleType()]; ok {
if data, exists := dataMap[item.GetAbleID()]; exists {
item.SetLoadedAbleData(data)
}
}
}
return nil
}七、調(diào)試
- 準(zhǔn)備數(shù)據(jù)
--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})
}- 發(fā)起請求
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實現(xiàn)自定義多態(tài)模型關(guān)聯(lián)查詢的詳細(xì)內(nèi)容,更多關(guān)于Golang Gorm關(guān)聯(lián)查詢的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解go語言 make(chan int, 1) 和 make (chan int) 的區(qū)別
這篇文章主要介紹了go語言 make(chan int, 1) 和 make (chan int) 的區(qū)別,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01
golang1.23版本之前 Timer Reset方法無法正確使用
在Go 1.23之前,使用`time.Reset`函數(shù)時需要先調(diào)用`Stop`并明確從timer的channel中抽取出東西,以避免潛在的問題,然而,這在實際代碼中難以實現(xiàn),因為設(shè)置定時器狀態(tài)和發(fā)送channel的操作并不是原子的,在某些情況下,這會導(dǎo)致timer在不應(yīng)該觸發(fā)時提前觸發(fā)2025-01-01

