Я не большой поклонник ОРМ. Не поймите меня неправильно, я не собираюсь начинать кампанию против них. Я знаю, что многие люди считают их отличным уровнем абстракции в своей базе данных. Но я обычно чувствую себя более продуктивно с написанием запросов, когда я могу просто написать SQL.
Хорошо, не всегда . Написание простых старых запросов CRUD (создание, чтение, обновление, удаление) является бессмысленной рутинной работой, эквивалентной очистке туалета.
Поэтому я написал небольшую библиотеку с именем structable, которая заботится о шаблонном CRUD для меня, но позволяет легко и просто создавать свои собственные запросы, когда пришло время выйти за рамки простых операций.
Примечание. Structable 3.0 является новым и нарушает обратную совместимость со Structable 1.0. Эта статья охватывает 3.0.
Структурная таблица
В основе Structable лежит простое средство отображения, которое читает аннотированную структуру и отображает ее в таблицу в реляционной базе данных. Я называю это «отображение структуры таблицы» (так называется StrucTable).
С помощью нескольких аннотаций я могу сделать простую карту:
type User struct { Id int `stbl:"id,PRIMARY_KEY,SERIAL"` Name string `stbl:"name"` Email string `stbl:"email"` NotMapped string // This will not be mapped to a DB record at all. }
stbl
Аннотацию говорит Structable о поле на структуры. Например, Name
строка отображается в столбец с name
помощью stbl
аннотации. Structable предполагает, что вы спроектировали свою базу данных таким образом, что вы сможете хранить строку в этом столбце.
Id
Поле имеет некоторые дополнительные вещи в stbl
теге:
Id int `stbl:"id,PRIMARY_KEY,SERIAL"`
PRIMARY_KEY
Ключевое слово говорит structable , что поле Id является первичным ключом для таблицы. SERIAL
(Или , AUTOINCREMENT
если вы предпочитаете) говорит Structable , что значение этого поля можно считать управлять базой данных.
Запись против рекордера
Структура выше является записью . Он содержит данные, но не знает, как что-то сделать с этими данными. Structable обеспечивает рекордер, чтобы что-то делать с данными.
В простейшей форме использование рекордера — это вопрос присоединения структуры записи к рекордеру :
db = //... we'll get to this in a minute. dbFlavor = "postgres" user := new(User) userRecorder = structable.New(db, dbFlavor).Bind("users_table", user)
Рекордеру нужно несколько вещей. Во-первых, ему нужен дескриптор базы данных. Я вернусь к этому, но в двух словах, Structable использует мой любимый инструмент для работы с базами данных: белка .
Имея соединение с базой данных, мы можем связать () (прикрепить) конкретную таблицу и структуру к устройству записи. Теперь мы по существу сказали, что структура User хранится в users_table
и управляется нашей новой userRecorder
.
Отсюда мы можем использовать рекордер для вставки, обновления, загрузки, тестирования или удаления записи:
user.Name = "Matt" userRecorder.Insert()
Теоретически это похоже на шаблон проектирования, известный как DAO. Но самое элегантное выражение Structable найдено с использованием шаблона Active Record.
Использование Structable для активных записей
Активная запись — это объект записи, который является собственным рекордером. Мы можем легко это сделать с помощью Structable, добавив в нашу User
структуру всего два небольших дополнения :
type User struct { structable.Recorder builder squirrel.StatementBuilderType Id int `stbl:"id,PRIMARY_KEY,SERIAL"` Name string `stbl:"name"` Email string `stbl:"email"` } // NewUser creates a new Structable wrapper for a user. // // Of particular importance, watch how we intialize the Recorder. func NewUser(db squirrel.DBProxyBeginner, dbFlavor string) *User { u := new(User) u.Recorder = structable.New(db, dbFlavor).Bind("user_table", u) return u }
Структура User
теперь имеет анонимный structable.Recorder
. В Go это позволяет нам «наследовать» методы Recorder. В целях расширения я также всегда сохраняю a squirrel.StatementBuilderType
в своих записях, чтобы впоследствии я мог добавить новые методы базы данных.
Теперь давайте посмотрим на NewUser
метод. Вместо создания внешнего рекордера он инициализирует свой внутренний аноним Recorder
. Теперь я могу использовать экземпляры моей User
структуры следующим образом:
user := NewUser(db, dbFlavor) user.Name = "Matt" user.Insert() anotherUser := NewUser(db, dbFlavor) anotherUser.Id = 123 anotherUser.Load() // This loads because Id is a PRIMARY_KEY field.
Краткое слово о подключении к базе данных
Вы можете взглянуть на Squirrel, чтобы лучше понять, как управлять соединениями. Это интуитивно понятно и очень мощно. Но вот как я настроил это для примеров выше:
func main() { // Boilerplate DB setup. // First, we need to know the database driver. driver := "postgres" // Second, we need a database connection. con, _ := sql.Open(driver, "dbname=structable_test sslmode=disable") // Third, we wrap in a prepared statement cache for better performance. cache := squirrel.NewStmtCacheProxy(con) // Create an empty new user and give it some properties. user := NewUser(cache, driver) user.Name = "Matt" user.Email = "[email protected]" }
По сути, я создаю новое соединение с базой данных Postgres (хотя оно отлично работает с MySQL и, возможно, с другими разновидностями БД). Затем я оборачиваю его в кэш операторов Squirrel, чтобы я мог прозрачно повторно использовать подготовленные операторы. Оттуда я отправляюсь на гонки.