Как разработать поддержку null/undefined для типов (DB/JSON)?
Добрый день, товарищи!
Уже долгое время бьюсь с вопросом организации null/undefined в golang для связи JSON и DB.
Например, у меня есть тип bool. С клиента оно может придти как true/false/null/undefined.
Если пришло true/false, то надо обновить значение в базе. Если пришел null, то надо в базе его выставить как NULL. Если пришёл undefined, то в базе его вообще трогать не надо.
Решения я пока вижу только такие: 1. Использовать *ptr вместо типа. В связке с тем, что Go почти всегда их сам умеет конвертировать, это просто, удобно и легко. Но тут я теряю одно из состояний. То есть либо я undefined привожу к nil, либо null к nil. Соответственно, этот способ не подходит (но это на мой взгляд самый красивый способ, если его можно как-то доработать, будет максимально круто). 2. Использовать sql.NullInt и т.п. вместо обычных типов. Но тут такая же проблема, как и в п.1. Решилось изобретением своих типов, которые оборачивают все стандартные типы. Но это жутко неудобно. Во-первых, чтобы записать их в структуру приходится городить что-то типа nullable.Int{12}, во-вторых, чтобы получить значение обратно надо делать item.count.Value, например.
Хочется какого-то простого решения. Возможно, использовать указатели и как-то красиво посылать запросы в базу. Нужна помощь сообщества. Кто как делает в своих проектах?
P.S.: для превращения структуры есть своя функция, которая с помощью reflect преобразовывает структуру к 2 массивам: names (имена полей) и values (значения полей в том же порядке). Потом скармливается функции для создания запроса или обновления строк. Её можно менять/выкидывать.
Tyranron, то, что из БД оно приедет как nil (Scan не вызывается на NULL), а из JS nil станет undefined (также не вызывается UnmarshalJSON на undefined).
1. Из БД можно (и нужно в Вашем случае) делать Scan на sql.NullInt значения. Для того их и ввели, чтобы работать с NULL'ями в БД. А будет оно nil или нет - решаете Вы, скармливая его в Scan, или нет. Это получится именно то, что Вы хотели - если у Вас nil, то Вы не работаете с полем в запросе, если у Вас sql.NullInt.Valid == false, то драйвер БД будет работать со значением как NULL в БД, во всех остальных случая идёт работа с самим значением.
2. Что касается JS/JSON, то тут всё намного хуже, потому что у Вас проблема с сериализацией данных, ибо undefined не является валидным JSON типом. То есть Вам нужно здесь либо менять формат сериализации и отказываясь от undefined (например, просто не включая подобные поля в JSON), либо писать кастомный анмаршалинг руками.
так а что мешает спарить 1 и 2 варианты и использовать *sql.NullInt?
Плохой метод.
Вы подтягивайте за уши предметную область к техническим особенностям языка программирования.
А как бы наоборот - ради решения проблем предметной области и существует язык программирования.
stratosmi, не совсем вижу где именно я "подтягиваю за уши" предметную область? Я разве предлагал менять JSON/структуру БД?
Использовать *Type в Go вполне себе идиоматично для выражения опциональности (честные enum'ы, а с ними и Optional, не завезли), что хорошо ложится на undefined случай.
Использовать sql.NullInt вполне себе идиоматично и правильно для работы с NULL в БД, что хорошо ложится на null случай.
Где здесь "подтягивание за уши"? Всего лишь попытка выразить предметную область в уже существующих широко используемых типах.
Наваять свой набор типов всегда можно. Но, во-первых, бойлерплейт, а во-вторых, это будет явно не "простым решением", о котором спрашивал автор.