Ruby Доступ к базе данных - Учебник по DBI

В этой главе будет объяснено, как использовать Ruby для доступа к базам данных.Ruby DBIМодуль предоставляет Ruby-скриптам интерфейс, не зависящий от базы данных, аналогичный модулю Perl DBI.

DBI расшифровывается как Database Independent Interface, представляющий независимый от базы данных интерфейс Ruby. DBI обеспечивает уровень абстракции между кодом Ruby и нижележащей базой данных, позволяя легко переключаться между базами данных. Он определяет ряд методов, переменных и спецификаций, предоставляя согласованный интерфейс базы данных, не зависящий от базы данных.

DBI может взаимодействовать со следующим:

  • ADO (ActiveX Data Objects)
  • DB2
  • Frontbase
  • mSQL
  • MySQL
  • ODBC
  • Oracle
  • OCI8 (Oracle)
  • PostgreSQL
  • Прокси/Сервер
  • SQLite
  • SQLRelay

Архитектура приложений DBI

DBI не зависит от любой базы данных, доступной в фоновом режиме. Используете ли вы Oracle, MySQL или Informix, вы можете использовать DBI. Следующая схема архитектуры наглядно это иллюстрирует.

Ruby DBI 架构

Ruby DBI обычно использует двухуровневую архитектуру:

  • Уровень интерфейса базы данных (DBI). Этот уровень не зависит от базы данных и предоставляет ряд публичных методов доступа, которые можно использовать независимо от типа сервера базы данных.
  • Уровень драйвера базы данных (DBD). Этот уровень зависит от базы данных; разные драйверы обеспечивают доступ к разным движкам баз данных. MySQL, PostgreSQL, InterBase, Oracle и т. д. используют разные драйверы. Каждый драйвер отвечает за интерпретацию запросов из уровня DBI и сопоставление этих запросов с запросами, подходящими для данного типа сервера базы данных.

Установка

Если вы хотите писать скрипты на Ruby для доступа к базе данных MySQL, сначала необходимо установить модуль MySQL для Ruby.

Установка пакета разработки MySQL

# Ubuntu sudo apt-get install mysql-client sudo apt-get install libmysqlclient15-dev # Centos yum install mysql-devel

В Mac OS необходимо изменить файл ~/.bash_profile или ~/.profile и добавить следующий код:

MYSQL=/usr/local/mysql/bin export PATH=$PATH:$MYSQL export DYLD_LIBRARY_PATH=/usr/local/mysql/lib:$DYLD_LIBRARY_PATH

Или используйте символическую ссылку:

sudo ln -s /usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib/libmysqlclient.18.dylib

Установка DBI с помощью RubyGems (рекомендуется)

RubyGems был создан примерно в ноябре 2003 года и входит в стандартную библиотеку Ruby начиная с Ruby 1.9. Для получения дополнительных сведений см.:Ruby RubyGems

Используйте gem для установки dbi и dbd-mysql:

sudo gem install dbi sudo gem install mysql sudo gem install dbd-mysql

Установка из исходного кода (используйте этот метод для версий Ruby до 1.9)

Этот модуль является DBD, и его можно загрузить сhttp://tmtm.org/downloads/mysql/ruby/.

После загрузки последнего пакета распакуйте его, войдите в каталог и выполните следующие команды для установки:

ruby extconf.rbИлиruby extconf.rb --with-mysql-dir=/usr/local/mysqlИлиruby extconf.rb --with-mysql-config

Затем скомпилируйте:

make

Получение и установка Ruby/DBI

Вы можете скачать и установить модуль Ruby DBI по ссылке ниже:

https://github.com/erikh/ruby-dbi

Перед началом установки убедитесь, что у вас есть права root. Теперь выполните следующие шаги для установки:

Шаг 1

git clone https://github.com/erikh/ruby-dbi.git

Или напрямую скачайте zip-архив и распакуйте его.

Шаг 2

Войдите в каталогruby-dbi-masterи в этом каталоге используйте скриптsetup.rbдля настройки. Самая распространённая команда настройки — 'config' без параметров. По умолчанию эта команда устанавливает все драйверы.

ruby setup.rb config

Точнее, вы можете использовать опцию --with, чтобы перечислить конкретные части, которые хотите использовать. Например, если вы хотите настроить только основной модуль DBI и драйвер слоя MySQL DBD, введите следующую команду:

ruby setup.rb config --with=dbi,dbd_mysql

Шаг 3

Последний шаг — собрать драйверы и установить их с помощью следующей команды:

ruby setup.rb setup ruby setup.rb install

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

Предполагая, что мы используем базу данных MySQL, перед подключением к базе данных убедитесь:

  • Вы создали базу данных TESTDB.
  • Вы создали таблицу EMPLOYEE в TESTDB.
  • Таблица содержит поля FIRST_NAME, LAST_NAME, AGE, SEX и INCOME.
  • Установите идентификатор пользователя "testuser" и пароль "test123" для доступа к TESTDB.
  • Модуль DBI для Ruby был корректно установлен на вашем компьютере.
  • Вы прочитали руководство по MySQL и понимаете основные операции MySQL.

Ниже приведён пример подключения к базе данных MySQL "TESTDB":

Пример

#!/usr/bin/ruby -w require "dbi" begin #Подключение к серверу MySQL dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") #Получить строку версии сервера и отобразить её row = dbh.select_one("SELECT VERSION()") puts "Версия сервера: " + row[0] rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" ensure #Отключение от сервера dbh.disconnect if dbh end

При запуске этого скрипта на машине Linux будут получены следующие результаты.

Server version: 5.0.45

Если соединение установлено с источником данных, возвращается дескриптор базы данных и сохраняется вdbhдля дальнейшего использования, в противном случаеdbhбудет установлен в nil,e.errиe::errstrвозвращает код ошибки и строку ошибки соответственно.

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

Операция INSERT

Когда вы хотите создать записи в таблице базы данных, вам нужно использовать операцию INSERT.

После установки соединения с базой данных мы можем подготовиться к использованию методаdoилиprepareиexecuteметод для создания таблиц или создания записей для вставки в таблицы базы данных.

Использование оператора do

Инструкции, которые не возвращают строки, можно выполнить, вызвав методdoобработки базы данных. Этот метод принимает строковый параметр инструкции и возвращает количество строк, затронутых инструкцией.

dbh.do("DROP TABLE IF EXISTS EMPLOYEE") dbh.do("CREATE TABLE EMPLOYEE ( FIRST_NAME CHAR(20) NOT NULL, LAST_NAME CHAR(20), AGE INT, SEX CHAR(1), INCOME FLOAT )" );

Аналогично вы можете выполнять SQLINSERTинструкции, чтобы создавать записи и вставлять их в таблицу EMPLOYEE.

Пример

#!/usr/bin/ruby -w require "dbi" begin #Подключение к серверу MySQL dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") dbh.do( "INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME) VALUES ('Mac', 'Mohan', 20, 'M', 2000)" ) puts "Record has been created" dbh.commit rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" dbh.rollback ensure #Отключиться от сервера dbh.disconnect if dbh end

Использованиеprepareиexecute

Вы можете использовать методы DBI:prepareиexecuteдля выполнения SQL-запросов в коде Ruby.

Шаги по созданию записей следующие:

  • Подготовьте SQL-запрос с помощью инструкции INSERT. Это будет сделано с использованием методаprepare.
  • Выполните SQL-запрос, чтобы выбрать все результаты из базы данных. Это будет сделано с использованием методаexecute.
  • Освободите дескриптор запроса. Это будет сделано с использованиемfinishAPI.
  • Если всё идет гладко, тоcommitэту операцию, иначе вы можетеrollbackзавершить транзакцию.

Ниже приведен синтаксис использования этих двух методов:

Пример

sth = dbh.prepare(statement) sth.execute ... ноль или более SQL operations ... sth.finish

Эти два метода можно использовать для передачиbindзначений в SQL-запросы. Иногда значения для ввода могут быть не заданы заранее; в этом случае используются связанные значения. Используйте вопросительный знак (?) вместо фактического значения, а фактическое значение передается через API execute().

В следующем примере создаются две записи в таблице EMPLOYEE:

Пример

#!/usr/bin/ruby -w require "dbi" begin #Подключение к серверу MySQL dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") sth = dbh.prepare( "INSERT INTO EMPLOYEE(FIRST_NAME, LAST_NAME, AGE, SEX, INCOME) VALUES (?, ?, ?, ?, ?)" ) sth.execute('John', 'Poul', 25, 'M', 2300) sth.execute('Zara', 'Ali', 17, 'F', 1000) sth.finish dbh.commit puts "Запись создана" rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" dbh.rollback ensure #Отключиться от сервера dbh.disconnect if dbh end

Если вы используете несколько INSERTs, подготовка одного оператора и его многократное выполнение в цикле гораздо эффективнее, чем вызов do при каждом проходе цикла.

Операция READ

Операция READ в любой базе данных означает извлечение полезной информации из базы данных.

После установления соединения с базой данных мы можем подготовиться к запросу к базе данных. Мы можем использовать методdoилиprepareиexecuteметод для извлечения значений из таблиц базы данных.

Шаги для извлечения записей следующие:

  • На основе требуемых условий подготовьте SQL-запрос. Это делается с помощью методаprepare.
  • Выполните SQL-запрос, чтобы выбрать все результаты из базы данных. Это делается с помощью методаexecute.
  • Извлекайте результаты по одному и выводите эти результаты. Это делается с помощью методаfetch.
  • Освободите дескриптор оператора. Это делается с помощью методаfinish.

В следующем примере из таблицы EMPLOYEE извлекаются все записи с зарплатой больше 1000.

Пример

#!/usr/bin/ruby -w require "dbi" begin #Подключение к серверу MySQL dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") sth = dbh.prepare("SELECT * FROM EMPLOYEE WHERE INCOME > ?") sth.execute(1000) sth.fetch do |row| printf "Имя: %s, Фамилия : %s\n", row[0], row[1] printf "Возраст: %d, Пол : %s\n", row[2], row[3] printf "Зарплата :%d \n\n", row[4] end sth.finish rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" ensure #Отключиться от сервера dbh.disconnect if dbh end

Это даст следующий результат:

First Name: Mac, Last Name : Mohan
Age: 20, Sex : M
Salary :2000

First Name: John, Last Name : Poul
Age: 25, Sex : M
Salary :2300

Существует много других методов для извлечения записей из базы данных. Если вам интересно, вы можете ознакомиться сОперацией чтения Ruby DBI。

Операция обновления

Операция UPDATE в любой базе данных означает обновление одной или нескольких существующих записей в базе данных. В следующем примере обновляются все записи с SEX = 'M'. Здесь мы увеличим AGE всех мужчин на один год. Это разделено на три шага:

  • На основе требуемых условий подготовьте SQL-запрос. Это делается с помощью методаprepare.
  • Выполните SQL-запрос, чтобы выбрать все результаты из базы данных. Это делается с помощью методаexecute.
  • Освободите дескриптор оператора. Это делается с помощью методаfinish.
  • Если всё пройдёт хорошо, тоcommitоперацию, иначе вы можетеrollbackзавершить транзакцию.

Пример

#!/usr/bin/ruby -w require "dbi" begin #Подключение к серверу MySQL dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") sth = dbh.prepare("UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = ?") sth.execute('M') sth.finish dbh.commit rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" dbh.rollback ensure #Отключение от сервера dbh.disconnect if dbh end

Операция DELETE

Когда вы хотите удалить записи из базы данных, необходимо использовать операцию DELETE. Следующий пример удаляет все записи из EMPLOYEE, у которых AGE превышает 20. Шаги этой операции следующие:

  • На основе требуемых условий подготовьте SQL-запрос. Это делается с помощью методаprepare.
  • Выполните SQL-запрос для удаления нужных записей из базы данных. Это делается с помощью методаexecute.
  • Освободите дескриптор оператора. Это делается с помощью методаfinish.
  • Если всё пройдёт хорошо, тоcommitоперацию, иначе вы можетеrollbackзавершить транзакцию.

Пример

#!/usr/bin/ruby -w require "dbi" begin #Подключение к серверу MySQL dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") sth = dbh.prepare("DELETE FROM EMPLOYEE WHERE AGE > ?") sth.execute(20) sth.finish dbh.commit rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" dbh.rollback ensure #Отключение от сервера dbh.disconnect if dbh end

Выполнение транзакций

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

  • Атомарность:Атомарность транзакции означает, что программы, входящие в транзакцию, представляют собой логическую единицу работы с базой данных; выполняемые ими операции модификации данных выполняются либо все, либо ни одна.
  • Согласованность:Согласованность транзакции означает, что база данных должна находиться в согласованном состоянии до и после выполнения транзакции. Если состояние базы данных удовлетворяет всем ограничениям целостности, говорят, что база данных согласована.
  • Изоляция:Изоляция транзакций означает, что параллельные транзакции изолированы друг от друга, то есть операции внутри транзакции и данные, с которыми они работают, должны быть заблокированы и невидимы для других транзакций, пытающихся их изменить.
  • Долговечность:Долговечность транзакции означает, что при сбое системы или носителя гарантируется, что обновления зафиксированных транзакций не будут потеряны. То есть, как только транзакция зафиксирована, её изменения данных в базе данных должны быть постоянными и выдерживать любой сбой системы базы данных. Долговечность обеспечивается за счёт резервного копирования и восстановления базы данных.

DBI предоставляет два метода для выполнения транзакций. Один —commitилиrollbackметод, используемый для фиксации или отката транзакций. Другой —transactionметод, который можно использовать для реализации транзакций. Далее давайте рассмотрим эти два простых метода реализации транзакций:

Метод I

Первый метод использует DBIcommitиrollbackметод для явной фиксации или отмены транзакции:

Пример

dbh['AutoCommit'] = false #Установите автофиксацию в false. begin dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'John'") dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'Zara'") dbh.commit rescue puts "транзакция не удалась" dbh.rollback end dbh['AutoCommit'] = истина

Метод II

Второй метод используетtransactionметод. Этот метод относительно проще, потому что он требует блок кода, содержащий инструкции, составляющие транзакцию.transactionМетод выполняет блок, а затем, в зависимости от того, успешно ли выполнился блок, автоматически вызываетcommitилиrollback:

Пример

dbh['AutoCommit'] = false #Установите auto-commit в false dbh.transaction do |dbh| dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'John'") dbh.do("UPDATE EMPLOYEE SET AGE = AGE+1 WHERE FIRST_NAME = 'Zara'") end dbh['AutoCommit'] = true

Операция COMMIT

Commit — это операция, которая помечает базу данных как завершившую изменения. После этой операции все изменения необратимы.

Ниже приведён простой пример вызова методаcommit.

dbh.commit

Операция ROLLBACK

Если вас не устраивают одно или несколько изменений и вы хотите полностью отменить эти изменения, используйте методrollback.

Ниже приведён простой пример вызова методаrollback.

dbh.rollback

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

Чтобы отключиться от базы данных, используйте API disconnect.

dbh.disconnect

Если пользователь закрывает соединение с базой данных через метод disconnect, DBI откатит все незавершённые транзакции. Однако, не полагаясь на какие-либо детали реализации DBI, ваше приложение может корректно явно вызвать commit или rollback.

Обработка ошибок

Существует много разных источников ошибок. Например, синтаксические ошибки при выполнении SQL-инструкций, сбои соединения или вызов метода fetch для отменённого или завершённого дескриптора оператора.

Если метод DBI завершается с ошибкой, DBI возбуждает исключение. Методы DBI могут возбуждать исключения любого типа, но два наиболее важных класса исключений — этоDBI::InterfaceErrorиDBI::DatabaseError。

Объекты исключений этих классов имеютerr、errstrиstateтри атрибута, соответственно представляющих номер ошибки, описательную строку ошибки и стандартный код ошибки. Атрибуты подробно описаны ниже:

  • err:Возвращает целочисленное представление возникшей ошибки; если DBD это не поддерживает, он возвращаетnil. Например, Oracle DBD возвращаетORA-XXXXчисловую часть сообщения об ошибке.
  • errstr:Возвращает строковое представление возникшей ошибки.
  • state:Возвращает код SQLSTATE возникшей ошибки. SQLSTATE — строка из пяти символов. Большинство DBD не поддерживают его, поэтому возвращают nil.

В приведённых выше примерах вы уже видели следующий код:

rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" dbh.rollback ensure #Отключиться от сервера dbh.disconnect if dbh end

Чтобы получать отладочную информацию о том, что делает скрипт во время выполнения, можно включить трассировку. Для этого нужно сначала загрузить модуль dbi/trace, а затем вызвать методtrace:

require "dbi/trace" .............. trace(mode, destination)

Значение mode может быть 0 (выкл.), 1, 2 или 3, а значение destination должно быть объектом IO. Значения по умолчанию — 2 и STDERR соответственно.

Блоки кода методов

Есть методы, которые создают дескрипторы. Эти методы вызываются с блоком кода. Преимущество использования блоков кода с методами состоит в том, что они передают дескриптор блоку в качестве аргумента, а дескриптор автоматически очищается при завершении блока. Ниже приведены некоторые примеры, помогающие понять эту концепцию.

  • DBI.connect :Этот метод создаёт дескриптор базы данных. Рекомендуется вызывать в конце блокаdisconnect, чтобы отключиться от базы данных.
  • dbh.prepare :Этот метод создаёт дескриптор оператора. Рекомендуется вызывать в конце блокаfinish. Внутри блока необходимо вызвать методexecute, чтобы выполнить оператор.
  • dbh.execute :Этот метод похож на dbh.prepare, но при использовании dbh.execute не нужно вызывать метод execute внутри блока. Дескриптор оператора выполняется автоматически.

Пример 1

DBI.connectОн может принимать блок кода, передавать ему дескриптор базы данных и автоматически отключать дескриптор в конце блока.

dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", 
                  "testuser", "test123") do |dbh|

Пример 2

dbh.prepareОн может принимать блок кода, передавать ему дескриптор оператора и автоматически вызывать finish в конце блока.

dbh.prepare("SHOW DATABASES") do |sth| sth.execute puts "Databases: " + sth.fetch_all.join(", ") end

Пример 3

dbh.executeОн может принимать блок кода, передавать ему дескриптор оператора и автоматически вызывать finish в конце блока.

dbh.execute("SHOW DATABASES") do |sth| puts "Databases: " + sth.fetch_all.join(", ") end

DBI transactionМетод также может принимать блок кода, как уже объяснялось в предыдущих главах.

Функции и атрибуты, специфичные для драйвера

DBI позволяет драйверам баз данных предоставлять дополнительные функции, специфичные для базы данных, которые пользователь может вызывать через методfuncлюбого объекта Handle.

Используйте метод[]= или []для установки или получения атрибутов, специфичных для драйвера.

DBD::Mysql реализует следующие функции, специфичные для драйвера:

Нет.Функция и описание
1dbh.func(:createdb, db_name)
Создаёт новую базу данных.
2dbh.func(:dropdb, db_name)
Удаляет базу данных.
3dbh.func(:reload)
Выполняет операцию перезагрузки.
4dbh.func(:shutdown)
Выключает сервер.
5dbh.func(:insert_id) => Fixnum
Возвращает последнее значение AUTO_INCREMENT для этого соединения.
6dbh.func(:client_info) => String
Возвращает информацию о клиенте MySQL на основе версии.
7dbh.func(:client_version) => Fixnum
Возвращает информацию о клиенте на основе версии. Это похоже на :client_info, но возвращает fixnum вместо строки.
8dbh.func(:host_info) => String
Возвращает информацию о хосте.
9dbh.func(:proto_info) => Fixnum
Возвращает протокол, используемый для связи.
10dbh.func(:server_info) => String
Возвращает информацию о серверной части MySQL в зависимости от версии.
11dbh.func(:stat) => Stringb>
Возвращает текущий статус базы данных.
12dbh.func(:thread_id) => Fixnum
Возвращает ID текущего потока.

#!/usr/bin/ruby require "dbi" begin #Подключение к серверу MySQL dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123") puts dbh.func(:client_info) puts dbh.func(:client_version) puts dbh.func(:host_info) puts dbh.func(:proto_info) puts dbh.func(:server_info) puts dbh.func(:thread_id) puts dbh.func(:stat) rescue DBI::DatabaseError => e puts "Произошла ошибка" puts "Код ошибки: #{e.err}" puts "Сообщение об ошибке: #{e.errstr}" ensure dbh.disconnect if dbh end

Это даст следующий результат:

5.0.45
50045
Localhost via UNIX socket
10
5.0.45
150621
Uptime: 384981  Threads: 1  Questions: 1101078  Slow queries: 4 \
Opens: 324  Flush tables: 1  Open tables: 64  \
Queries per second avg: 2.860