Функции являются первоклассными представителями языка Go. Стоит подумать и попрактиковаться в том, как эффективно использовать его расширенные возможности использования.
В ежедневном развитии бизнеса фильтрация и запросы к различным полям некоторых таблиц являются основной функцией. И большинство из них могут делать запросы на основе разных условий. так
type XXXRepo interface {
GetXXXByIdOrName(ctx context.Context, id int, name string) (o []admin.XXX, err error)
GetXXXInfoList(ctx context.Context, req *GetXXXRequest) (total int64, o []admin.XXX, err error)
GetXXXInfo(ctx context.Context, columnId, gradeId int) (o []admin.XXX, err error)
GetXXXByIdList(ctx context.Context, idList []int) (o []admin.XXX, err error)
}
Это всего лишь несколько условий. Что делать, если в таблице имеется более десяти полей, соответствующих запросу? Уровень dao также будет содержать много избыточного кода и может просто изменить входные параметры.
Предположим, что существует таблица заказов с упрощенной структурой следующим образом:
CREATE TABLE `order` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT «Автоматическое увеличение первичного ключа»,
`order_id` bigint NOT NULL COMMENT 'Идентификатор заказа',
`shop_id` varchar NOT NULL COMMENT 'идентификатор магазина',
`product_id` int NOT NULL DEFAULT '0' COMMENT «Идентификатор продукта»,
`status` int NOT NULL DEFAULT '0' COMMENT 'состояние',
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Форма заказа';
Например, используйте различные комбинации следующих полей для запроса
order_id, shop_id, produce_id,status
Код, подобный этому, будет написан на уровне dao.
Запрос на основе orderId
func GetOrderInfoByOrderId(ctx context.Context, orderId int64) ([]*resource.Order) {
db := GetDB(ctx)
db = db.Table(resource.Order{}.TableName())
var infos []*resource.Order
db = db.Where("order_id = ?", orderId)
db.Find(&infos)
return infos
}
Запрос на основе shopId
func GetOrderInfoByShopId(ctx context.Context, shopId int64) ([]*resource.Order) {
db := GetDB(ctx)
db = db.Table(resource.Order{}.TableName())
var infos []*resource.Order
db = db.Where("shop_id = ?", shopId)
db.Find(&infos)
return infos
}
можно увидеть,Код для двух методовОчень похоже,За исключением различных параметров и именования,Если вам нужно следовать produce_id или status Query, то вам нужно написать еще несколько подобных методов, в результате чего будет много похожих методов. Конечно, легко подумать, что если передается несколько параметров, не лучше ли передать несколько параметров? Возможно, так написано?
func GetOrderInfo(ctx context.Context,orderId, shopId int64) ([]*resource.Order) {
db := GetDB(ctx)
db = db.Table(resource.Order{}.TableName())
var infos []*resource.Order
db = db.Where("shop_id = ? and order_id = ?", shopId,orderId)
db.Find(&infos)
return infos
}
Если бизнес меняется в любой момент, необходимо изменить условия. Может быть, это станет таким
func GetOrderInfo(ctx context.Context,orderId, shopId int64) ([]*resource.Order) {
db := GetDB(ctx)
db = db.Table(resource.Order{}.TableName())
var infos []*resource.Order
if orderId != 0 {
db = db.Where("order_id = ?",orderId)
}
if shopId != 0 {
db = db.Where("shop_id = ?",shopId)
}
db.Find(&infos)
return infos
}
Код вызывающего абонента, вероятно, выглядит так
// По идентификатору магазина Запрос
infos := GetOrderInfo(ctx, 0, 1)
// Согласно идентификатору заказа Запрос
infos := GetOrderInfo(ctx, 1, 0)
Это эквивалентно замене других полей запроса, которые не связаны с нулевым значением по умолчанию соответствующего типа.
Конечно, вы также можете использовать структуру в качестве параметра.
func GetOrderInfo(ctx context.Context,order Order) ([]*resource.Order) {
db := GetDB(ctx)
db = db.Table(resource.Order{}.TableName())
db.Where(&order).find(&infos)
return infos
}
ноДумаю, некоторые люди сталкивались с такими ловушками.,То есть, если поле имеет тип int, int64 и т. д.,Есть 0 часов,Неясно, передается ли 0.,Значение по-прежнему не передано,Это неотличимо。потому чтоgoТип языка по умолчанию, нулевое значение。 Если это тип значения 0, вы также можете использовать его в качестве параметра.,игнорируется по умолчанию,Можетссылка
у горм-чиновника есть это предложение
NOTE When querying with struct, GORM will only query with non-zero fields, that means if your field’s value is 0, '', false or other zero values, it won’t be used to build query conditions, for example:
В этом случае вы можете преобразовать ее в карту, как показано ниже.
func GetOrderInfoInfo(ctx context.Context, o Order) ([]*resource.Order) {
db := GetDB(ctx)
db = db.Table(resource.Order{}.TableName())
var infos []*resource.Order
if o.orderId > 0 {
db = db.Where("order_id = ?", o.orderId)
}
if o.shopId != "" {
db = db.Where("shop_id = ?", o.shop_id)
}
// я опущу это позже
if xxxx
db.Find(&infos)
return infos
}
Здесь всего несколько полей. Если вы используете более десятка полей для объединения запроса, вам придется писать много суждений if.
Исходя из всех вышеперечисленных ситуаций, необходимо оптимизировать
Может利用функциональное программированиеПриходитьоптимизация
Определяется следующим образом
type Option func(*gorm.DB)
Параметр определения — это функция. Тип входного параметра этой функции — *gorm.DB, а возвращаемое значение — пустое.
Затем определите функцию для полей таблицы, которые необходимо фильтровать и запрашивать, и присвойте значения.
func OrderID(orderID int64) Option {
return func(db *gorm.DB) {
db.Where("`order_id` = ?", userID)
}
}
func ShopID(shopID int64) Option {
return func(db *gorm.DB) {
db.Where("`shop_id` = ?", shopID)
}
}
Поэтому нам нужно создать разные функции для возможных полей и вернуть функцию Option. Эта функция присваивает входные параметры объекту [db *gorm.DB].
Итак, исходя из вышеизложенного, очень удобно переписать слой дао.
func GetOrderInfo(ctx context.Context, options ...func(option *gorm.DB)) ([]*resource.OrderInfo) {
db := GetDB(ctx)
db = db.Table(resource.OrderInfo{}.TableName())
for _, option := range options {
option(db)
}
var infos []*resource.OrderInfo
db.Find(&infos)
return infos
}
Таким образом, в базовой логике не нужно писать много суждений if, вместо этого используется цикл for.
Если вызывающая сторона знает, на основе каких параметров ему нужно выполнить запрос, он может использовать функцию параметра, написанную выше, в качестве входного параметра.
// orderID Запрос
infos := GetOrderInfo(ctx, OrderID(orderID))
// orderID,shopID Комбинация Запрос
infos := GetOrderInfo(ctx, OrderID(orderID), ShopID(shopID))
Конечно, вы также можете выполнить запрос на основе других условий и затем написать функцию.
После оптимизации логика упрощается. Генерируется Option, эквивалентный классу конфигурации, и код становится гораздо более элегантным. Здесь упоминаются только запросы, обновления аналогичны, удаления и записи не нужны.