Обзор GORP — ORM для языка Go

от автора

В предыдущей статье (http://habrahabr.ru/post/178963/) я рассказывал как работать с базой данных на Go. В комментариях к посту мне посоветовали посмотреть две библиотеки ORM.

Вообще, если говорить о работе с базой данных в Go, то самым простым способом будет представление структуры данных в виде массива или словаря. Сериализация в этом случае будет очень простой, но работа с такими типами данных в коде не выглядит привлекательной. Конечно, можно добавить обертки и т. д, но если посмотреть на архитектуру любого подобного решения, то так или иначе вырисовывается ORM.

В обеих предложенных библиотеках я не нашел всего необходимого функционала.

Beedb (https://github.com/astaxie/beedb)

Beedb — отличная библиотека с обширным API. Используя beedb, мы сможем строить запросы, оперируя функциями Having, Limit, Where и т. д. (если мы не используем ORM, необходимо прописывать весь запрос к базе строкой). Прикладной программный интерфейс можно посмотреть по адресу https://github.com/astaxie/beedb/wiki/API-Interface.

GORP (https://github.com/coopernurse/gorp)

Gorp — небольшая библиотека, в API которой входит минимум функций:

  • Get — извлекаем запись по ключу.
  • Insert — добавляем новую запись.
  • Delete — удаляем по ключу.
  • Update — обновляем по ключу.
  • Select — выборка произвольного числа объектов.
  • Exec — выполнить произвольный запрос.

Библиотека позволяет работать с разными SQL диалектами, API значительно меньше, чем у Beedb.

Как мне показалось, Beedb намного сложнее в использовании. Задача, под которую выбиралась библиотека — создать единый вход для форумов “Сети Знаний” (чтобы пользователь мог авторизоваться на одном форуме по данным с другого). Напомню, движок http://hashcode.ru/ написан на Python.

Как мне кажется, главной проблемой при работе с базой данных в Go является не сложность занесения объектов, а сложность выборки. Весь код обрастает строками содержащими sql запросы, в которых тяжко разбираться (чего нет в Django). В добавок с отсутствием перегрузки функций в языке, код становится очень объемный.

Beedb при всем своем многообразии API не решает проблему. Для реализации я выбрал GORP. Далее я расскажу о нескольких “подводных камнях” этой библиотеки.

Главные файлы:

  • dialect.go — содержит код зависимый от SQL диалектов.
  • gorp.go — код библиотеки.
  • gorp_test.go — крайне важный файл с тестами, в нем мы можем посмотреть примеры использования библиотеки.

Главной проблемой в работе с библиотекой послужило то, что она “проглатывает” ошибки. Напомню, в Go нет исключений. В таком случае (как и в языке C) вызываемая функция сообщает об ошибке в возвращаемом значение. GORP не возвращает ошибок во многих случаях с функцией Get (не возвращает объектов, но и ошибка равна nil). В случае если вы заподозрили что-то неладное, попробуйте использовать метод Select вместо Get.

Второй интересной особенностью стала работа с объектом time.Time. GORP использует отображение (рефлексию) для создания таблиц базы данных. За это отвечает файл с диалектами. Функция отвечающая за преобразование структуры в табличку:

func (d PostgresDialect) ToSqlType(val reflect.Type, maxsize int, isAutoIncr bool) string { 	switch val.Kind() { 	case reflect.Bool: 		return "boolean" 	case reflect.Int, reflect.Int16, reflect.Int32, reflect.Uint16, reflect.Uint32: 		if isAutoIncr { 			return "serial" 		} 		return "integer" 	case reflect.Int64, reflect.Uint64: 		if isAutoIncr { 			return "bigserial" 		} 		return "bigint" 	case reflect.Float64, reflect.Float32: 		return "real" 	case reflect.Slice: 		if val.Elem().Kind() == reflect.Uint8 { 			return "bytea" 		} 	}  	switch val.Name() { 	case "NullableInt64": 		return "bigint" 	case "NullableFloat64": 		return "double" 	case "NullableBool": 		return "smallint" 	case "NullableBytes": 		return "bytea" 	}  	if maxsize < 1 { 		maxsize = 255 	} 	return fmt.Sprintf("varchar(%d)", maxsize) } 

Можно увидеть, что время будет записано как число, а не как timestamp. При работе с драйвером для PostgreSQL от lxn (https://github.com/lxn/go-pgsql) это вызвало проблемы с извлечением данных. Я добавил следующие строки во второй switch:

	case "Time": 		return "timestamp with time zone" 

Следующим моментом стала ошибка при извлечении нулевого времени. При записи в базу дописываются дополнительные элементы в конец строки, которые не соответствуют стандарту RFC3339, с помощью которого идет преобразования строки в объект времени. Это происходит только в том случае, если объект нулевой (IsZero возвращает true).

Еще один момент касается префикса базы данных. Как ни странно в диалекте Postgres он объявлен не экспортируемым и его невозможно задать.

В следующих статьях я расскажу вам об архитектуре единого входа на форумы.

Далее секция Q&A. Пожалуйста, задавайте ваши вопросы в комментариях к посту, на форуме ХэшКод c метками “Golang” и “Go” или в личных сообщениях. Лучшие вопросы будут в конце каждой статьи.

Пользователь sergeyfast спросил, как обрабатывать нулевые значения при извлечении данных из базы данных.

Это делается за счет специальных типов, объявленных в пакете sql. Типы:

  • NullBool
  • NullFloat64
  • NullInt64
  • NullString

Пример обработки нулевого значения:

func makeIntFromResultSet(rows * sql.Rows) interface{} {	 	var value sql.NullInt64 	err := rows.Scan(&value) 	HandleDataBaseError(err) 	 	if value.Valid { 		return value.Int64 	} 	 	return -1 } 

Пользователь zuborg спрашивал как мы управляем временем жизни кэша.

Алгоритм прост: время и ревалидация.

Мы заносим данные в memcached на 15 минут. Данные в memcached — это не просто пара ключ — значение, мы храним их как двойной указатель. Для доступа к записи необходимо сначала извлечь ключ, и только затем данные. Все данные описываются мета-ключами. Мета-ключом выступает первичный (или уникальный) ключ таблицы в базе, а также сам SQL запрос. Когда происходит обращение к базе, мы добавляем данные в кэш и приписываем им мета-ключи Таким образом, мы можем ревалидровать одни и те же данные по разным ключам, например, удалить из кэша все данные, где встречается пользователь с ID.

ссылка на оригинал статьи http://habrahabr.ru/post/182602/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *