Ruby Database Access - DBI Tutorial


This chapter will explain to you how to use Ruby to access the database. Ruby DBI The module provides a database-independent interface to Ruby scripts similar to the Perl DBI module.

DBI stands for Database independent interface, which represents Ruby’s database-independent interface. DBI provides an abstraction layer between your Ruby code and the underlying database, allowing you to easily implement database switching. It defines a series of methods, variables and specifications, providing a consistent database interface that is independent of the database.

DBI interacts with:

  • ADO (ActiveX Data Objects)

  • DB2

  • Frontbase

  • mSQL

  • MySQL

  • ODBC

  • Oracle

  • ##OCI8 (Oracle)

  • PostgreSQL

  • Proxy/Server

  • SQLite

  • SQLRelay

##DBI Application Architecture

DBI is independent of any database available in the backend. Whether you're using Oracle, MySQL, Informix, you can use DBI. The architecture diagram below illustrates this clearly.

Ruby DBI general architecture uses two layers: ruby_dbi.jpg

    Database Interface (DBI) layer. This layer is independent of the database and provides a series of public access methods that can be used regardless of the database server type.
  • Database driver (DBD) layer. This layer is dependent on the database, and different drivers provide access to different database engines. MySQL, PostgreSQL, InterBase, Oracle, etc. use different drivers respectively. Each driver is responsible for interpreting requests from the DBI layer and mapping these requests to those appropriate for a given type of database server.
  • Installation

If you want to write Ruby scripts to access a MySQL database, you need to install the Ruby MySQL module first.

Install Mysql development package

# Ubuntu
sudo apt-get install mysql-client
sudo apt-get install libmysqlclient15-dev

# Centos
yum install mysql-devel

Mac OS system needs to modify the ~/.bash_profile or ~/.profile file, add the following code:

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

Or use a soft connection:

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

Install DBI using RubyGems (recommended)

RubyGems was created around November 2003 and has become part of the Ruby standard library since Ruby version 1.9. For more details, please see: Ruby RubyGems

Use gem to install dbi and dbd-mysql:

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

Use source code to install (use this method if Ruby version is less than 1.9)

The The module is a DBD and can be downloaded from http://tmtm.org/downloads/mysql/ruby/.

After downloading the latest package, unzip it and enter the directory, execute the following command to install:

% ruby extconf.rb

或者

% ruby extconf.rb --with-mysql-dir=/usr/local/mysql

或者

% ruby extconf.rb --with-mysql-config

Then compile:

% make

Get and install Ruby/DBI

You can download and install the Ruby DBI module from the link below:

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

Before starting the installation, make sure you have root permissions. Now, please follow the steps below to install:

Step 1

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

Or directly download the zip package and unzip it.

Step 2

Enter the directory ruby-dbi-master, and use the setup.rb script in the directory to configure. The most commonly used configuration command is the config parameter followed by no parameters. This command is configured to install all drivers by default.

$ ruby setup.rb config

More specifically, you can use the --with option to list the specific sections you want to use. For example, if you only want to configure the main DBI module and MySQL DBD layer driver, please enter the following command:

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

Step 3

The last step is to create the driver and install it using the following command:

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

Database connection

Assuming we are using a MySQL database, before connecting to the database, please make sure:

  • You have created a database TESTDB.

  • You have created the table EMPLOYEE in TESTDB.

  • This table has fields FIRST_NAME, LAST_NAME, AGE, SEX and INCOME.

  • Set user ID "testuser" and password "test123" to access TESTDB

  • Have the Ruby module installed correctly on your machine DBI.

  • You have watched the MySQL tutorial and understood the basic operations of MySQL.


The following is an example of connecting to the MySQL database "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 "Server version: " + row[0]
rescue DBI::DatabaseError => e
     puts "An error occurred"
     puts "Error code:    #{e.err}"
     puts "Error message: #{e.errstr}"
ensure
     # 断开与服务器的连接
     dbh.disconnect if dbh
end

When running this script, it will be in Linux The following results are produced on the machine.

Server version: 5.0.45

If the connection is established with a data source, the database handle (Database Handle) will be returned and saved to dbh for subsequent use, otherwise dbh will be Set to a nil value, e.err and e::errstr return the error code and error string respectively.

Finally, before exiting this program, please make sure to close the database connection and release resources.

INSERT operation

When you want to create records in a database table, you need to use the INSERT operation.

Once the database connection is established, we are ready to create a table or create an insert into the data table using the do method or the prepare and execute methods record of.

Use do statement

Statements that do not return rows can be done by calling the do database processing method. This method takes a statement string parameter and returns the number of rows affected by the statement.

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

Similarly, you can execute the SQL INSERT statement to create records for insertion into the EMPLOYEE table.

#!/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 "An error occurred"
     puts "Error code:    #{e.err}"
     puts "Error message: #{e.errstr}"
     dbh.rollback
ensure
     # 断开与服务器的连接
     dbh.disconnect if dbh
end

Using prepare and execute

You can use DBI's prepare and execute methods to execute SQL statements in Ruby code.

The steps to create a record are as follows:

  • Prepare the SQL statement with the INSERT statement. This will be done using the prepare method.

  • Execute a SQL query and select all results from the database. This will be done using the execute method.

  • Release the statement handle. This will be done using the finish API.

  • If everything goes well, commit the action, otherwise you can rollback to complete the transaction.

The following is the syntax for using these two methods:

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

These two methods can be used to pass bind values ​​to SQL statements. Sometimes the value being entered may not be given in advance, in which case a bound value is used. Use question marks (?) in place of the actual value, which is passed through the execute() API.

The following example creates two records in the EMPLOYEE table:

#!/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 "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
     # 断开与服务器的连接
     dbh.disconnect if dbh
end

If you use multiple INSERTs at the same time, prepare a statement first, and then execute it multiple times in a loop than It's much more efficient to call do each time in a loop.

READ operation

The READ operation on any database refers to obtaining useful information from the database.

Once the database connection is established, we are ready to query the database. We can get the values ​​from the database table using do method or prepare and execute methods.

The steps to obtain records are as follows:

  • Prepare a SQL query based on the required conditions. This will be done using the prepare method.

  • Execute a SQL query and select all results from the database. This will be done using the execute method.

  • Get the results one by one and output them. This will be done using the fetch method.

  • Release the statement handle. This will be done using the finish method.

The following example queries all records with salary (salary) exceeding 1,000 from the EMPLOYEE table.

#!/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 "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
     # 断开与服务器的连接
     dbh.disconnect if dbh
end

This will produce the following result:

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

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

There are also many ways to get records from the database, if you are interested, you can check out the Ruby DBI Read operation.

Update operation

The UPDATE operation on any database refers to updating one or more existing records in the database. The following example updates all records whose SEX is 'M'. Here we will add one year to the AGE for all males. This will be done in three steps:

  • Prepare the SQL query based on the required conditions. This will be done using the prepare method.

  • Execute a SQL query and select all results from the database. This will be done using the execute method.

  • Release the statement handle. This will be done using the finish method.

  • If everything goes well, commit the action, otherwise you can rollback to complete the transaction.

#!/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 "An error occurred"
     puts "Error code:    #{e.err}"
     puts "Error message: #{e.errstr}"
     dbh.rollback
ensure
     # 断开与服务器的连接
     dbh.disconnect if dbh
end

DELETE operation

When you want to delete records from the database, you need to use the DELETE operation. The following example deletes all records from EMPLOYEE with an AGE greater than 20. The steps for this operation are as follows:

  • Prepare the SQL query based on the required conditions. This will be done using the prepare method.

  • Execute the SQL query to delete the required records from the database. This will be done using the execute method.

  • Release the statement handle. This will be done using the finish method.

  • If everything goes well, commit the action, otherwise you can rollback to complete the transaction.

#!/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 "An error occurred"
     puts "Error code:    #{e.err}"
     puts "Error message: #{e.errstr}"
     dbh.rollback
ensure
     # 断开与服务器的连接
     dbh.disconnect if dbh
end

Execute transactions

Transactions are a mechanism to ensure transaction consistency. A transaction should have the following four attributes:

  • Atomicity: The atomicity of a transaction means that the program included in the transaction works as the logic of the database Unit, all data modification operations it performs are either performed or not performed at all.

  • Consistency: The consistency of a transaction means that the database must be in a consistent state before and after a transaction is executed. A database is said to be consistent if its state satisfies all integrity constraints.

  • Isolation: Isolation of transactions means that concurrent transactions are isolated from each other, that is, the operations within a transaction and the data being operated must be blocked up so that it cannot be seen by other transactions attempting to modify it.

  • Durability (Durability): The durability of a transaction means that when the system or media fails, it ensures that updates to committed transactions cannot be lost. That is, once a transaction is committed, its changes to the data in the database should be permanent and withstand any database system failure. Durability is ensured through database backup and recovery.

DBI provides two methods of executing transactions. One is the commit or rollback method, which is used to commit or rollback the transaction. There is also the transaction method, which can be used to implement transactions. Next, we will introduce these two simple methods of implementing transactions:

Method I

The first method uses DBI’s commit and rollback Method to explicitly commit or cancel a transaction:

   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 "transaction failed"
     dbh.rollback
   end
   dbh['AutoCommit'] = true

Method II

The second method uses the transaction method. This method is relatively simple because it requires a block of code that contains the statements that make up the transaction. The transaction method executes the block, and then automatically calls commit or rollback depending on whether the block is executed successfully:

   dbh['AutoCommit'] = false # 设置自动提交为 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 operation

Commit is an operation that identifies that changes to the database have been completed. After this operation, all changes are irreversible.

The following is a simple example of calling the commit method.

     dbh.commit

ROLLBACK Operation

If you are not satisfied with one or several changes and you want to completely revert these changes, use the rollback method.

The following is a simple example of calling the rollback method.

     dbh.rollback

Disconnect the database

If you need to disconnect from the database, please use the disconnect API.

    dbh.disconnect

If the user closes the database connection through the disconnect method, DBI will roll back all outstanding transactions. However, without relying on any DBI implementation details, your application is just fine with explicitly calling commit or rollback.

Handling Errors

There are many different sources of errors. For example, there is a syntax error when executing a SQL statement, or the connection fails, or the fetch method is called on a canceled or completed statement handle.

If a DBI method fails, DBI will throw an exception. DBI methods can throw any type of exception, but the two most important exception classes are DBI::InterfaceError and DBI::DatabaseError.

Exception objects of these classes have three attributes: err, errstr and state. The sub-table represents the error number, a descriptive The error string and a standard error code. The attributes are specifically described as follows:

  • err: Returns the integer representation of the error that occurred, or nil if the DBD does not support it. For example, Oracle DBD returns the numeric portion of the ORA-XXXX error message.

  • #errstr: Returns a string representation of the error that occurred.

  • state: Returns the SQLSTATE code of the error that occurred. SQLSTATE is a five-character string. Most DBDs don't support it, so nil is returned.

In the above example you have seen the following code:

rescue DBI::DatabaseError => e
     puts "An error occurred"
     puts "Error code:    #{e.err}"
     puts "Error message: #{e.errstr}"
     dbh.rollback
ensure
     # 断开与服务器的连接
     dbh.disconnect if dbh
end

In order to get debugging information about what the script is executing when it is executed, you can enable tracing. To do this, you must first download the dbi/trace module and then call the trace method that controls the trace mode and output destination:

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

trace(mode, destination)

mode values ​​can be 0 (off), 1, 2 or 3, the value of destination should be an IO object. The default values ​​are 2 and STDERR respectively.

The code block of the method

There are some methods for creating handles. These methods are called through code blocks. The advantage of using code blocks with methods is that they provide a handle to the code block as a parameter, which is automatically cleared when the block terminates. Below are some examples to help understand this concept.

  • DBI.connect: This method generates a database handle. It is recommended to call disconnect at the end of the block to disconnect the database.

  • dbh.prepare: This method generates a statement handle, it is recommended to call finish at the end of the block. Inside the block, you must call the execute method to execute the statement.

  • dbh.execute: This method is similar to dbh.prepare, but dbh.execute does not require calling the execute method within the block. Statement handles are automatically executed.

Example 1

DBI.connect can have a code block, pass the database handle to it, and will automatically disconnect at the end of the block Open handle.

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

Example 2

dbh.prepare You can have a code block, pass it a statement handle, and finish will be automatically called at the end of the block.

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

Example 3

dbh.execute can have a code block, pass it a statement handle, and finish will be automatically called at the end of the block.

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

DBI transaction The method can also have a code block, which has been explained in the above chapter.

Specific driver functions and properties

DBI allows the database driver to provide additional database-specific functions, which can be used by the user through any Handle object's func method is called.

Use the []= or [] methods to set or get driver-specific properties.

DBD::Mysql implements the following driver-specific functions:

Serial NumberFunction & Description
1dbh.func(:createdb, db_name)
Create a new database.
2dbh.func(:dropdb, db_name)
Delete a database.
3dbh.func(:reload)
Perform the reload operation.
4dbh.func(:shutdown)
Shut down the server.
5dbh.func(:insert_id) => Fixnum
Returns the most recent AUTO_INCREMENT value for this connection.
6dbh.func(:client_info) => String
Returns MySQL client information according to version.
7dbh.func(:client_version) => Fixnum
Return client information according to version. This is similar to :client_info, but it returns a fixnum instead of a string.
8dbh.func(:host_info) => String
Returns host information.
9dbh.func(:proto_info) => Fixnum
Returns the protocol used for communication.
10dbh.func(:server_info) => String
Returns the MySQL server-side information according to the version.
11dbh.func(:stat) => Stringb>
Returns the current status of the database.
12dbh.func(:thread_id) => Fixnum
Returns the ID of the current thread.

Example

#!/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 "An error occurred"
   puts "Error code:    #{e.err}"
   puts "Error message: #{e.errstr}"
ensure
   dbh.disconnect if dbh
end

This will produce the following results:

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