Tutorial Ruby / DBI

Este capítulo ensina como acessar um banco de dados usando Ruby. O módulo Ruby DBI fornece uma interface independente de banco de dados para scripts Ruby semelhante àquela do módulo Perl DBI.

DBI significa Database Independent Interface for Ruby, o que significa que DBI fornece uma camada de abstração entre o código Ruby e o banco de dados subjacente, permitindo que você alterne as implementações do banco de dados com facilidade. Ele define um conjunto de métodos, variáveis ​​e convenções que fornecem uma interface de banco de dados consistente, independente do banco de dados real que está sendo usado.

DBI pode interagir com o seguinte -

  • ADO (ActiveX Data Objects)
  • DB2
  • Frontbase
  • mSQL
  • MySQL
  • ODBC
  • Oracle
  • OCI8 (Oracle)
  • PostgreSQL
  • Proxy/Server
  • SQLite
  • SQLRelay

Arquitetura de um aplicativo DBI

DBI é independente de qualquer banco de dados disponível no backend. Você pode usar DBI se estiver trabalhando com Oracle, MySQL ou Informix, etc. Isso fica claro no diagrama de arquitetura a seguir.

A arquitetura geral para Ruby DBI usa duas camadas -

  • A camada de interface de banco de dados (DBI). Essa camada é independente do banco de dados e fornece um conjunto de métodos de acesso comuns usados ​​da mesma forma, independentemente do tipo de servidor de banco de dados com o qual você está se comunicando.

  • A camada do driver de banco de dados (DBD). Esta camada depende do banco de dados; diferentes drivers fornecem acesso a diferentes mecanismos de banco de dados. Existe um driver para MySQL, outro para PostgreSQL, outro para InterBase, outro para Oracle e assim por diante. Cada driver interpreta as solicitações da camada DBI e as mapeia nas solicitações apropriadas para um determinado tipo de servidor de banco de dados.

Pré-requisitos

Se você deseja escrever scripts Ruby para acessar bancos de dados MySQL, você precisará ter o módulo Ruby MySQL instalado.

Este módulo atua como um DBD conforme explicado acima e pode ser baixado de https://www.tmtm.org/en/mysql/ruby/

Obtendo e Instalando Ruby / DBI

Você pode baixar e instalar o módulo Ruby DBI a partir do seguinte local -

https://imgur.com/NFEuWe4/embed

Antes de iniciar esta instalação, certifique-se de ter privilégios de root. Agora, siga as etapas abaixo -

Passo 1

$ tar zxf dbi-0.2.0.tar.gz

Passo 2

Acesse o diretório de distribuição dbi-0.2.0 e configure-o usando o script setup.rb nesse diretório. O comando de configuração mais geral se parece com este, sem argumentos após o argumento de configuração. Este comando configura a distribuição para instalar todos os drivers por padrão.

$ ruby setup.rb config

Para ser mais específico, forneça uma opção --with que lista as partes específicas da distribuição que você deseja usar. Por exemplo, para configurar apenas o módulo DBI principal e o driver MySQL DBD, emita o seguinte comando -

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

etapa 3

A etapa final é construir o driver e instalá-lo usando os seguintes comandos -

$ ruby setup.rb setup
$ ruby setup.rb install

Conexão de banco de dados

Supondo que iremos trabalhar com o banco de dados MySQL, antes de conectar a um banco de dados, certifique-se do seguinte -

  • Você criou um banco de dados TESTDB.

  • Você criou EMPLOYEE em TESTDB.

  • Esta tabela contém os campos FIRST_NAME, LAST_NAME, AGE, SEX e INCOME.

  • O ID do usuário "testuser" e a senha "test123" são definidos para acessar o TESTDB.

  • O Módulo Ruby DBI está instalado corretamente em sua máquina.

  • Você passou pelo tutorial do MySQL para entender os fundamentos do MySQL.

A seguir está o exemplo de conexão com o banco de dados MySQL "TESTDB"

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   dbh = DBI.connect("DBI:Mysql:TESTDB:localhost", "testuser", "test123")
   # get server version string and display it
   row = dbh.select_one("SELECT VERSION()")
   puts "Server version: " + row[0]
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Ao executar este script, ele produz o seguinte resultado em nossa máquina Linux.

Server version: 5.0.45

Se uma conexão for estabelecida com a fonte de dados, um identificador de banco de dados é retornado e salvo em dbh para uso posterior de outra forma dbhé definido como valor nulo e e.err e e :: errstr retornam o código de erro e uma string de erro, respectivamente.

Finalmente, antes de lançá-lo, certifique-se de que a conexão com o banco de dados esteja fechada e os recursos liberados.

Operação INSERT

A operação INSERT é necessária quando você deseja criar seus registros em uma tabela de banco de dados.

Uma vez que uma conexão de banco de dados é estabelecida, estamos prontos para criar tabelas ou registros nas tabelas de banco de dados usando do método ou prepare e execute método.

Usando fazer declaração

As instruções que não retornam linhas podem ser emitidas invocando o dométodo de manipulação de banco de dados. Este método usa um argumento de string de instrução e retorna uma contagem do número de linhas afetadas pela instrução.

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 )" );

Da mesma forma, você pode executar a instrução SQL INSERT para criar um registro na tabela EMPLOYEE.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   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 "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Usando preparar e executar

Você pode usar métodos de preparação e execução da classe DBI para executar a instrução SQL por meio do código Ruby.

A criação do registro segue as seguintes etapas -

  • Preparando a instrução SQL com a instrução INSERT. Isso será feito usando oprepare método.

  • Execução de consulta SQL para selecionar todos os resultados do banco de dados. Isso será feito usando oexecute método.

  • Identificador de declaração de liberação. Isso será feito usandofinish API

  • Se tudo correr bem, então commit esta operação, caso contrário, você pode rollback a transação completa.

A seguir está a sintaxe para usar esses dois métodos -

sth = dbh.prepare(statement)
sth.execute
   ... zero or more SQL operations ...
sth.finish

Esses dois métodos podem ser usados ​​para passar bindvalores para instruções SQL. Pode haver um caso em que os valores a serem inseridos não sejam fornecidos com antecedência. Nesse caso, os valores de ligação são usados. Uma interrogação (?) é usado no lugar dos valores reais e, em seguida, os valores reais são passados ​​por meio da API execute ().

A seguir está o exemplo para criar dois registros na tabela EMPLOYEE -

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   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 "Record has been created"
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Se houver vários INSERTs ao mesmo tempo, preparar uma instrução primeiro e, em seguida, executá-la várias vezes dentro de um loop é mais eficiente do que chamar do a cada vez através do loop.

Operação READ

A operação READ em qualquer banco de dados significa buscar algumas informações úteis do banco de dados.

Assim que nossa conexão com o banco de dados for estabelecida, estamos prontos para fazer uma consulta neste banco de dados. Podemos usar qualquer umdo método ou prepare e execute métodos para buscar valores de uma tabela de banco de dados.

A busca de registros segue as seguintes etapas -

  • Preparando a consulta SQL com base nas condições exigidas. Isso será feito usando oprepare método.

  • Execução de consulta SQL para selecionar todos os resultados do banco de dados. Isso será feito usando oexecute método.

  • Buscando todos os resultados um por um e imprimindo esses resultados. Isso será feito usando ofetch método.

  • Identificador de declaração de liberação. Isso será feito usando ofinish método.

A seguir está o procedimento para consultar todos os registros da tabela EMPLOYEE com salário acima de 1000.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   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 "First Name: %s, Last Name : %s\n", row[0], row[1]
   printf "Age: %d, Sex : %s\n", row[2], row[3]
   printf "Salary :%d \n\n", row[4]
end
   sth.finish
rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Isso produzirá o seguinte resultado -

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

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

Existem mais métodos de atalho para buscar registros do banco de dados. Se estiver interessado, vá até Buscando o resultado, caso contrário, vá para a próxima seção.

Operação de atualização

ATUALIZAR Operação em qualquer banco de dados significa atualizar um ou mais registros, que já estão disponíveis no banco de dados. A seguir está o procedimento para atualizar todos os registros que têm SEXO como 'M'. Aqui, aumentaremos a AGE de todos os homens em um ano. Isso levará três etapas -

  • Preparando a consulta SQL com base nas condições exigidas. Isso será feito usando oprepare método.

  • Execução de consulta SQL para selecionar todos os resultados do banco de dados. Isso será feito usando oexecute método.

  • Identificador de declaração de liberação. Isso será feito usando ofinish método.

  • Se tudo correr bem então commit esta operação, caso contrário, você pode rollback a transação completa.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   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 "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Operação DELETE

A operação DELETE é necessária quando você deseja excluir alguns registros de seu banco de dados. A seguir está o procedimento para excluir todos os registros de EMPLOYEE onde AGE é maior que 20. Esta operação executará as seguintes etapas.

  • Preparando a consulta SQL com base nas condições exigidas. Isso será feito usando oprepare método.

  • Execução de consulta SQL para excluir registros necessários do banco de dados. Isso será feito usando oexecute método.

  • Identificador de declaração de liberação. Isso será feito usando ofinish método.

  • Se tudo correr bem então commit esta operação, caso contrário, você pode rollback a transação completa.

#!/usr/bin/ruby -w

require "dbi"

begin
   # connect to the MySQL server
   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 "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Realizando transações

As transações são um mecanismo que garante a consistência dos dados. As transações devem ter as seguintes quatro propriedades -

  • Atomicity - Ou uma transação é concluída ou nada acontece.

  • Consistency - Uma transação deve começar em um estado consistente e deixar o sistema em um estado consistente.

  • Isolation - Os resultados intermediários de uma transação não são visíveis fora da transação atual.

  • Durability - Depois que uma transação foi confirmada, os efeitos são persistentes, mesmo após uma falha do sistema.

O DBI fornece dois métodos para confirmar ou reverter uma transação. Existe mais um método chamado transação que pode ser usado para implementar transações. Existem duas abordagens simples para implementar transações -

Abordagem I

A primeira abordagem usa os métodos de confirmação e reversão do DBI para confirmar ou cancelar explicitamente a transação -

dbh['AutoCommit'] = false # Set auto commit to 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 "transaction failed"
   dbh.rollback
end
dbh['AutoCommit'] = true

Abordagem II

A segunda abordagem usa o método de transação . Isso é mais simples, porque leva um bloco de código contendo as instruções que constituem a transação. O método de transação executa o bloco e, em seguida, invoca o commit ou rollback automaticamente, dependendo se o bloco foi bem-sucedido ou falhou -

dbh['AutoCommit'] = false # Set auto commit to 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

Operação COMMIT

Commit é a operação, que dá um sinal verde ao banco de dados para finalizar as alterações e, após esta operação, nenhuma alteração pode ser revertida.

Aqui está um exemplo simples para chamar o commit método.

dbh.commit

Operação ROLLBACK

Se você não estiver satisfeito com uma ou mais das mudanças e quiser reverter essas mudanças completamente, use o rollback método.

Aqui está um exemplo simples para chamar o rollback método.

dbh.rollback

Desconectando banco de dados

Para desconectar a conexão do banco de dados, use a API de desconexão.

dbh.disconnect

Se a conexão com um banco de dados for fechada pelo usuário com o método de desconexão, todas as transações pendentes serão revertidas pelo DBI. No entanto, em vez de depender de qualquer um dos detalhes de implementação do DBI, seu aplicativo seria melhor chamar o commit ou rollback explicitamente.

Tratamento de erros

Existem muitas fontes de erros. Alguns exemplos são um erro de sintaxe em uma instrução SQL executada, uma falha de conexão ou chamada do método fetch para um identificador de instrução já cancelado ou concluído.

Se um método DBI falhar, o DBI levantará uma exceção. Os métodos DBI podem gerar qualquer um dos vários tipos de exceção, mas as duas classes de exceção mais importantes são DBI :: InterfaceError e DBI :: DatabaseError .

Os objetos de exceção dessas classes têm três atributos denominados err , errstr e estado , que representam o número do erro, uma string de erro descritiva e um código de erro padrão. Os atributos são explicados abaixo -

  • err- Retorna uma representação inteira do erro ocorrido ou nulo se isso não for suportado pelo DBD. O DBD Oracle, por exemplo, retorna a parte numérica de uma mensagem de erro ORA-XXXX .

  • errstr - Retorna uma representação de string do erro ocorrido.

  • state- Retorna o código SQLSTATE do erro ocorrido. O SQLSTATE é uma string de cinco caracteres. A maioria dos DBDs não oferece suporte para isso e retorna nil.

Você viu o seguinte código acima na maioria dos exemplos -

rescue DBI::DatabaseError => e
   puts "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
   dbh.rollback
ensure
   # disconnect from server
   dbh.disconnect if dbh
end

Para obter informações de depuração sobre o que seu script está fazendo enquanto é executado, você pode ativar o rastreio. Para fazer isso, você deve primeiro carregar o módulo dbi / trace e, em seguida, chamar o método de rastreamento que controla o modo de rastreamento e o destino de saída -

require "dbi/trace"
..............

trace(mode, destination)

O valor do modo pode ser 0 (desligado), 1, 2 ou 3, e o destino deve ser um objeto IO. Os valores padrão são 2 e STDERR, respectivamente.

Blocos de código com métodos

Existem alguns métodos que criam alças. Esses métodos podem ser chamados com um bloco de código. A vantagem de usar bloco de código junto com métodos é que eles fornecem o identificador para o bloco de código como seu parâmetro e limpam automaticamente o identificador quando o bloco termina. Existem alguns exemplos para entender o conceito.

  • DBI.connect- Este método gera um identificador de banco de dados e é recomendado chamar a desconexão no final do bloco para desconectar o banco de dados.

  • dbh.prepare- Este método gera um identificador de instrução e é recomendado terminar no final do bloco. Dentro do bloco, você deve invocar executar método para executar a instrução.

  • dbh.execute- Este método é semelhante, exceto que não precisamos invocar execute dentro do bloco. O identificador de instrução é executado automaticamente.

Exemplo 1

DBI.connect pode pegar um bloco de código, passar o identificador do banco de dados para ele e desconectar automaticamente o identificador no final do bloco da seguinte maneira.

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

Exemplo 2

dbh.prepare pode pegar um bloco de código, passar o identificador de instrução para ele e chamar automaticamente terminar no final do bloco como segue.

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

Exemplo 3

dbh.execute pode pegar um bloco de código, passar o identificador de instrução para ele e chamar automaticamente terminar no final do bloco da seguinte maneira -

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

O método de transação DBI também leva um bloco de código que foi descrito acima.

Funções e atributos específicos do driver

O DBI permite que os drivers de banco de dados forneçam funções adicionais específicas do banco de dados, que podem ser chamadas pelo usuário por meio do método func de qualquer objeto Handle.

Atributos específicos do driver são suportados e podem ser definidos ou obtidos usando o []= ou [] métodos.

Sr. Não. Funções e descrição
1

dbh.func(:createdb, db_name)

Cria um novo banco de dados.

2

dbh.func(:dropdb, db_name)

Descarta um banco de dados.

3

dbh.func(:reload)

Executa uma operação de recarregamento.

4

dbh.func(:shutdown)

Encerra o servidor.

5

dbh.func(:insert_id) => Fixnum

Retorna o valor AUTO_INCREMENT mais recente para uma conexão.

6

dbh.func(:client_info) => String

Retorna informações do cliente MySQL em termos de versão.

7

dbh.func(:client_version) => Fixnum

Retorna informações do cliente em termos de versão. É semelhante a: client_info, mas retorna um fixnum em vez de sting.

8

dbh.func(:host_info) => String

Retorna informações do host.

9

dbh.func(:proto_info) => Fixnum

Retorna o protocolo que está sendo usado para a comunicação.

10

dbh.func(:server_info) => String

Retorna informações do servidor MySQL em termos de versão.

11

dbh.func(:stat) => String

Retorna o estado atual do banco de dados.

12

dbh.func(:thread_id) => Fixnum

Retorna o ID do segmento atual.

Exemplo

#!/usr/bin/ruby

require "dbi"
begin
   # connect to the MySQL server
   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 "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
ensure
   dbh.disconnect if dbh
end

Isso produzirá o seguinte resultado -

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