Содержание


Язык программирования go

Часть 3. Работа с базами данных

Comments

Серия контента:

Этот контент является частью # из серии # статей: Язык программирования go

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Язык программирования go

Следите за выходом новых статей этой серии.

Язык go поддерживает большинство известных реляционных СУБД с открытым исходным кодом, например, MySQL, PostgreSQL и MongoDB. Поддержка этих СУБД реализована с помощью библиотек, написанных на самом языке go. Однако, если воспользоваться компилятором GNU или утилитой cgo, то появится возможность вызова функций, написанных на языке Си, благодаря чему можно будет обеспечить интеграцию с еще большим количеством СУБД.

Код, написанный на языке Си, можно использовать не только для интеграции с СУБД. Правда, эти новые возможности приводят к новым проблемам, связанным c существенными различиями, имеющимися между языками Си и go. Помимо ограничений, связанных с используемыми типами данных, в этих языках используются различные подходы к работе с памятью, например, в реализации процесса «сборки мусора». Поэтому достаточно сложно c первого раза обеспечить адекватную работу кода, написанного на языке Си, в программе, написанной на языке go.

В языке go существует несколько библиотек для работы с MySQL, однако не все из них являются удобными и законченными решениями. В рамках данной статьи будет использоваться библиотека GoMySQL, написанная Филом Бейфилдом (Phil Bayfield). Развитие и поддержка большей части остальных библиотек, по всей видимости, были прекращены, и поэтому на данный момент пользоваться ими фактически невозможно, так как сам язык ушел вперед. Возможно, стоит обратить внимание на библиотеку MyMySQL, так как она периодически обновляется и поддерживается разработчиками.

Подготовка к работе

Для запуска примеров, рассматривающихся в этой статье, потребуется работоспособный сервер MySQL. Если в системе отсутствует установленная версия MySQL, то её можно установить, введя в консоль следующую команду.

sudo apt-get install mysql-server mysql-client

Для более удобной работы можно установить пакет mysql-admin, содержащий административное приложение с графическим интерфейсом пользователя. В процессе установки потребуется задать пароль для пользователя root (данный пользователь будет использоваться для управления СУБД и не имеет никакого отношения к суперпользователю root, использующемуся на платформе UNIX). Значение пароля необходимо запомнить, так как оно будет использоваться для доступа к базе данных. В настоящем проекте для подключения к базе данных лучше создавать отдельных пользователей, а пользователя root использовать только для выполнения административных задач.

После установки и настройки сервера MySQL потребуется установить библиотеку GoMySQL, которая распространяется по одной из свободных лицензий. Если в системе уже установлена исполняемая среда go, то для установки библиотеки достаточно будет выполнить следующую команду:

goinstall github.com/Philio/GoMySQL

Возможно, потребуется еще установить систему контроля версий git. После установки библиотеку можно будет импортировать в файлы исходного кода с помощью следующей инструкции:

import "github.com/Philio/GoMySQL"

Второй вариант установки данной библиотеки потребует больше действий со стороны программиста. В листинге 1 показано, как с помощью утилиты make загрузить и собрать библиотеку GoMySQL.

Листинг 1. Установка библиотеки GoMySQL
#загрузка исходного кода библиотеки
git clone git://github.com/Philio/GoMySQL.git
#сборка библиотеки из исходного кода
cd GoMySQL
make
make install

Если хочется избежать установки системы контроля версий git, то можно загрузить архив непосредственно с данного Web-сайта, распаковать в отдельный каталог и собрать командами, описанными выше.

В обоих случаях выполняются следующие действия: загружается исходный код, из которого в первом варианте собирается пакет с именем GoMySQL.а или mysql.a во втором. При использовании второго варианта для импорта библиотеки потребуется уже другая инструкция:

import "mysql"

Затем собранный пакет должен быть скопирован в каталог pkg, например, ~/go/pkg/linux_386. После этого он станет доступен по имени, зависящему от расположения пакета в дереве каталогов.

Подключение к базе данных

В представленном ниже примере выполняется подключение к базе данных, загрузка данных из таблицы и вывод полученных данных на консоль. Такая программа достаточно подробно иллюстрирует принципы работы с базами данных в языке go. И хотя эти принципы являются одинаковыми практически во всех языках программирования: установить соединение, передать SQL-запрос, получить и обработать результаты запроса, - но в языке go существуют определенные отличия, которые необходимо учитывать.

Листинг 2. Пример go-приложения для работы с базой данных MySQL
package main

import (
	"fmt";
	"mysql";
	"os"
)
 
func main(){
	//подключение к базе данных
	db, err := mysql.DialTCP("localhost:3306", "root", "kabbala", "mysql") 
	//проверка ошибок, которые могли произойти в ходе подключения
	if err != nil {
		fmt.Printf(err.String())
		os.Exit(1) 
	}
	
	//выполнение SQL-запроса
	err = db.Query("select * from help_topic")  
	//проверка ошибок, которые могли произойти в ходе выполнения SQL-запроса
	if err != nil {  
		fmt.Printf(err.String())
	 	os.Exit(1)  
	}
	
	//получение результатов SQL-запроса
	result, err := db.UseResult()  
	//проверка ошибок, которые могли произойти в ходе получения результатов
	if err != nil {  
		fmt.Printf(err.String())
		os.Exit(1)  
	}  
	

	//обработка полученных результатов
	for {  
		//извлечение текущей записи из результатов запроса
		map_result := result.FetchMap()

		//проверка, в запросе еще не закончились записи
		if map_result == nil {  
			break  
		} 

		//распечатка содержимого текущей записи
		for key, val := range map_result {
			fmt.Printf("%s:%s \n", key, val)
		}
		fmt.Printf("-------------------------------------------\n")
	}  
}

Представленный в листинге 2 исходный код необходимо сохранить в файле db_usage.go и подготовить для него makefile, показанный в листинге 3.

Листинг 3. makefile для сборки файла db_usage.go
include $(GOROOT)/src/Make.inc

TARG=db_usage
GOFILES=\
	db_usage.go\

include $(GOROOT)/src/Make.cmd

Для работы приведенного сценария необходимо, чтобы была установлена переменная окружения GOROOT, а в переменной PATH была ссылка на каталог $GOROOT/bin. Это можно сделать с помощью следующих команд:

export GOROOT=~/go
export PATH=$PATH:$GOROOT/bin

В листинге 2 импортируются следующие пакеты:

  • fmt для вывода информации в консоль;
  • mysql для работы с базой данных;
  • os для управления работой программы.

Все действия выполняются в функции main. Сначала создается соединение с базой путем подключения к серверу MySQL, расположенному по адресу localhost на порту 3306. Этот порт используется MySQL по умолчанию, и его значение можно не указывать. Затем последовательно устанавливаются имя пользователя, его пароль и название базы данных. В данном примере используется пользователь root (который был создан при установке сервера), но это совсем не обязательно – пользователь может быть любой, главное чтобы у него было достаточно полномочий. Соединение с базой данных можно также создать и на основе сокета, в этом случае для подключения должна использоваться следующая инструкция:

db, err := mysql.DialUnix(mysql.DEFAULT_SOCKET, "user", "password", "database")

Для отображения информации о возможной ошибке выполняется проверка значения переменной err (что оно не равно nil), и вывод этого значения на экран, как показано в листинге 3.

После установки соединения можно передавать в СУБД команды на языке SQL для выполнения запросов, как показано ниже:

db.Query("select * from help_topic")

В качестве примера выполняется SQL-запрос к существующей таблице, содержащей справочную информацию по MySQL. Результат выполнения SQL-запроса извлекается из базы данных с помощью функции UseResult. Эта функция возвращает все строки в таблице, удовлетворяющие условию. Наличие ошибок проверяется описанным выше способом, а подробную информацию об ошибке можно получить, если вызвать метод String() у объекта err.

Далее записи (строки из таблицы базы данных, хранящиеся в переменной result) последовательно обрабатываются в цикле. Цикл заканчивается после обработки всех строк по условию map_result == nil. Вызов функции FetchMap для объекта result вернет хэш-таблицу, которую можно будет использовать для получения значения поля строки по его названию (имени столбца в таблице базы данных).

Типы данных

Для этой же цели можно использовать функцию FetchRow. Если посмотреть в исходный код библиотеки, то можно увидеть, что функция FetchMap(), по сути, является «адаптером» для функции FetchRow. В листинге 4 приведено объявление типа данных Row, с которым работают обе эти функции.

Листинг 4. Структуры данных для получения результатов SQL-запроса
type Row []interface{}
type Map map[string]interface{}

// также используются объекты типов Result и Field
type Result struct {
	// указатель на клиента
	c *Client

	// столбцы
	fieldCount uint64
	fieldPos   uint64
	fields     []*Field

	// строки
	rowPos uint64
	rows   []Row

	// данные
	mode    byte
	allRead bool
}
type Field struct {
	Database string
	Table    string
	Name     string
	Length   uint32
	Type     FieldType
	Flags    FieldFlag
	Decimals uint8
}

Объект типа Result возвращается из дескриптора соединения db при вызове метода UseResult после выполнения запроса. Далее для получения объектов типа Field из объекта Result используется метод FetchField. Для работы со столбцами, хранящими значения в формате даты/времени, в библиотеке существует специальный тип данных, описанный в листинге 5.

Листинг 5. Тип данных для работы со временем/датой
type DateTime struct {
	Year   uint16
	Month  uint8
	Day    uint8
	Hour   uint8
	Minute uint8
	Second uint8
}

Необходимо учитывать, что для вывода временных значений используется формат, применяемый по умолчанию в MySQL, как показано ниже:

func (d *DateTime) String() string {
    return fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d",
                       d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second)
}

Предварительная компиляция SQL-запросов

Из особенностей рассматриваемой библиотеки можно отметить поддержку заранее откомпилированных SQL-запросов (prepared statement).

Листинг 6. Структура для работы с откомпилированными SQL-запросами
type Statement struct {
	// указатель на клиента, использующего этот запрос
	c *Client

	// флаги состояния запроса
	prepared      bool
	paramsBound   bool
	paramsRebound bool

	// идентификатор запроса
	statementId uint32

	// параметры запроса
	paramCount uint16
	paramType  [][]byte
	paramData  [][]byte

	// столбцы (поля)
	columnCount uint64

	// результат
	AffectedRows uint64
	LastInsertId uint64
	Warnings     uint16
	result       *Result
	resultParams []interface{}
}

Для работы с предварительно откомпилированными запросами используются следующие методы:

func (s *Statement) Prepare(sql string) (err os.Error)
func (s *Statement) BindParams(params ...interface{}) (err os.Error)
func (s *Statement) Execute() (err os.Error)

Скомпилированные запросы – это средство для повышения производительности приложения, в котором часто выполняются одни и те же запросы, однако объяснение этого подхода выходит за рамки данной статьи. Методы, перечисленные выше, используются в том порядке, в котором они описаны. Жизненный цикл такого запроса включает следующие состояния: подготовка запроса, установка значений для пропущенных параметров и выполнение запроса. Для получения результатов в объекте Statement существует поле result, реализованное как указатель на структуру типа Result, по которому можно получить результаты выполнения данного запроса с текущими значениями входных параметров.

Заключение

Простая, но надежная реализация библиотеки (драйвера) для работы с реляционной базой данных вполне может быть написана одним человеком, как было успешно доказано Филом Бейфилдом и его библиотекой GoMySQL. Несмотря на свою простоту, эта библиотека предоставляет доступ ко всем возможностям языка SQL. В данной статье демонстрировалось использование функции Query для непосредственного выполнения SQL-запроса, однако можно использовать и предварительно откомпилированные SQL-запросы. Таким образом, программист, пишущий на языке go, ничем не ограничен в работе с реляционными базами данных по сравнению со своими коллегами, пишущими на более зрелых языках программирования, таких как Java или Си.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=696661
ArticleTitle=Язык программирования go: Часть 3. Работа с базами данных
publish-date=06302011