В этом уроке мы спроектируем модель для работы с базой данных нашего проекта.
Если вам не нравится термин модель, вы можете использовать название уровень обслуживания или уровень доступа к данным. Как данный паттерн не назови, идея состоит в том, что мы инкапсулируем код для работы с MySQL в отдельный пакет которого можно использовать в остальной части нашего веб-приложения.
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
Содержание статьи
Сейчас мы создадим скелет модели базы данных, которая возвращает тестовые данные. Прежде чем мы перейдем к мельчайшим деталям SQL-запросов, давайте разберем данный паттерн поподробнее.
Звучит неплохо? Давайте создадим пару новых папок и файлов в каталоге pkg
:
1 2 3 4 |
$ cd $HOME/code/snippetbox $ mkdir -p pkg/models/mysql $ touch pkg/models/models.go $ touch pkg/models/mysql/snippets.go |
Запомните: Папка
pkg
используется для хранения вспомогательного кода, не зависящего от приложения, который может использоваться повторно. Модель базы данных, которая может использоваться другими приложениями в будущем (например, приложение с интерфейсом командной строки), соответствует всем требованиям.
Мы начнем с использования файла pkg/models/models.go
, чтобы определить типы данных верхнего уровня, которые модель базы данных будет использовать и возвращать. Откройте данный файл и добавьте следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package models import ( "errors" "time" ) var ErrNoRecord = errors.New("models: подходящей записи не найдено") type Snippet struct { ID int Title string Content string Created time.Time Expires time.Time } |
Обратите внимание, как названия полей структуры Snippet
соответствуют полям в MySQL таблице snippets
?
Перейдем к файлу pkg/models/mysql/snippets.go
, который будет содержать код, специально предназначенный для работы с заметками в нашей MySQL базе данных. В этом файле мы определим новый тип SnippetModel
и реализуем в нем некоторые методы для доступа и управления базой данных. К примеру:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package mysql import ( "database/sql" // Импортируем только что созданный пакет models. Нам нужно добавить к нему префикс // независимо от того, какой путь к модулю вы установили // чтобы оператор импорта выглядел как // "{ваш-путь-модуля}/pkg/models".. "golangify.com/snippetbox/pkg/models" ) // SnippetModel - Определяем тип который обертывает пул подключения sql.DB type SnippetModel struct { DB *sql.DB } // Insert - Метод для создания новой заметки в базе дынных. func (m *SnippetModel) Insert(title, content, expires string) (int, error) { return 0, nil } // Get - Метод для возвращения данных заметки по её идентификатору ID. func (m *SnippetModel) Get(id int) (*models.Snippet, error) { return nil, nil } // Latest - Метод возвращает 10 наиболее часто используемые заметки. func (m *SnippetModel) Latest() ([]*models.Snippet, error) { return nil, nil } |
Важно отметить оператор импорта для golangify.com/snippetbox/pkg/models
. Обратите внимание, что путь импорта для внутреннего пакета указан с префиксом в виде выбранного пути модуля.
Создание модели базы данных в Go
Чтобы использовать эту модель в наших обработчиках, требуется вызвать новую структуру SnippetModel
в функции main()
, а затем внедрить ее как зависимость через структуру application — точно так же, как мы это делали с другими зависимостями.
Это делается следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
package main import ( "database/sql" "flag" "log" "net/http" "os" "golangify.com/snippetbox/pkg/models/mysql" // Новый импорт _ "github.com/go-sql-driver/mysql" ) // Добавляем поле snippets в структуру application. Это позволит // сделать объект SnippetModel доступным для наших обработчиков. type application struct { errorLog *log.Logger infoLog *log.Logger snippets *mysql.SnippetModel } func main() { addr := flag.String("addr", ":4000", "Сетевой адрес веб-сервера") dsn := flag.String("dsn", "web:pass@/snippetbox?parseTime=true", "Название MySQL источника данных") flag.Parse() infoLog := log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime) errorLog := log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile) db, err := openDB(*dsn) if err != nil { errorLog.Fatal(err) } defer db.Close() // Инициализируем экземпляр mysql.SnippetModel и добавляем его в зависимостях. app := &application{ errorLog: errorLog, infoLog: infoLog, snippets: &mysql.SnippetModel{DB: db}, } srv := &http.Server{ Addr: *addr, ErrorLog: errorLog, Handler: app.routes(), } infoLog.Printf("Запуск сервера на %s", *addr) err = srv.ListenAndServe() errorLog.Fatal(err) } func openDB(dsn string) (*sql.DB, error) { db, err := sql.Open("mysql", dsn) if err != nil { return nil, err } if err = db.Ping(); err != nil { return nil, err } return db, nil } |
Преимущества модели базы данных
Такая настройка моделей может показаться немного сложной и запутанной, особенно если вы новичок в Go. Однако по мере того, как наше веб-приложение продолжает расти, должно стать яснее, почему мы выбрали именно такую структуру.
Если вы вспомните все проделанное, то сможете осознать несколько преимуществ:
- Присутствует четкое разделение проблем. Логика базы данных не привязана к нашим обработчикам, а это означает, что обязанности обработчика ограничены HTTP элементами (то есть проверкой запросов и возвращение ответов). Это упростит написание тестов в будущем;
- Создав настраиваемый тип
SnippetModel
и реализовав для него методы, мы смогли сделать модель одним аккуратно инкапсулированным объектом, который можно легко инициализировать и затем передать обработчикам в качестве зависимости. Это упрощает поддержку и тестирование кода; - Так как действия модели определены как методы объекта — в данном случае, у
SnippetModel
— есть возможность создать интерфейс и смоделировать его для тестирования; - Мы полностью контролируем, какая база данных используется во время работы, используя флаг из командной строки;
- В конечном итоге структура каталогов хорошо масштабируется, если у проекта есть несколько бэкэндов. К примеру, если некоторые данные хранятся на Redis, можно поместить все их модели в пакет
pkg/models/redis
.
Скачать исходный код
Под каждой статьей из этого курса у нас есть актуальный архив с текущей версией нашего веб-приложения.
Скачать: snippetbox-18.zip
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
Что то с импортом напутано. Работали в локальной папке и в 127.0.0.1, а тут херак и внешний домен какой-то. и не слова об этом главное. что-то главные детали вы упускаете. И установка голанга на виду у вас не правильная
я вот одного понять не могу, что за маниакальное стремление каждую строчку в отдельный файл вынести????