2008年10月21日火曜日

[SQLite JDBC] Javaで始めるSQLiteデータベース入門

SQLiteデータベースは、Cで書かれた軽量データベースです。「軽量」というのは2つの意味があって、全体のコード数が10万行程度という点(PostgreSQLは100万行に近づいています)と、データベースを保存するファイルが1つに納まっているのがSQLiteの特徴です。他のシステムだと、複数のデータベース用のファイルがあって管理が面倒なのですが、SQLiteのデータベースはファイル1つで、しかもOS互換フォーマットで保存されているので、簡単にOSをまたがったデータベースのコピーを作成することができます。

そもそもリレーショナルデータベース(日本語では関係データベースと訳すことが多いです)って何?という方は、初心者向けに用意した以下の講義資料を参考にしてください。
Javaでデータベースアプリケーションを作成するには、JDBC (Java Database Connection)というAPIを使います。ただし、データベースを使うには、まずシステム側にデータベースシステムをインストールする必要があり、ここが慣れた人でもデータベースシステム(英語では、Database Management System: DBMSと言います)の設定でつまづいたり、面倒なことが多いのが難点でした。

このような問題を解決するために、SQLiteの軽量さを生かしたJava用のライブラリ(jarファイル)を作成しました。
このSQLite JDBC Driverでは、Mac OS X, Windows, Linux (i386, amd64)などよく使われるOSでSQLiteをインストールなしで動作させるために、それぞれのOSでコンパイルしたSQLiteのバイナリをjarファイルの中に埋め込んであります。これは、SQLiteが軽量だからなせる技です。プログラムの実行時に、自動的にjarファイルの中から、OSに応じたSQLiteのバイナリを取り出し、Java側でロードして使えるようにしています。

もし、少々特殊なOSや、CPUを使っていても安心です。SQLiteのCのコードを完全にJavaで動作するように置き換えたpure java版のSQLiteもライブラリに含めているので、上記以外のOSでもきちんと動作します(CをJavaコードに書き換えるのに少々無理があるので、動作は遅くなりますが)

JavaでSQLiteデータベースを使うには次の手順で行います:
  1. sqlite-jdbc-(version).jar をここからダウンロードします。2008年10月現在の最新版は3.6.4です。(Maven central repoのミラーからもダウンロードできます。)
  2. 下にあるサンプルプログラム(Sample.java)は、JDBCを通してデータベースの作成、検索をする例です。これをコンパイルします。
  3. Javaを実行するときに、上記のJARファイルを以下のようにクラスパスに含めます。
    java -classpath ".:sqlite-jdbc-(VERSION).jar" Sample
  4. これだけでJavaでDBMSが使えるようになります。お手軽!
Sample.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class Sample
{
public static void main(String[] args) throws ClassNotFoundException
{
// load the sqlite-JDBC driver using the current class loader
Class.forName("org.sqlite.JDBC");

Connection connection = null;
try
{
// create a database connection
connection = DriverManager.getConnection("jdbc:sqlite:sample.db");
Statement statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.

statement.executeUpdate("drop table if exists person");
statement.executeUpdate("create table person (id integer, name string)");
statement.executeUpdate("insert into person values(1, 'leo')");
statement.executeUpdate("insert into person values(2, 'yui')");
ResultSet rs = statement.executeQuery("select * from person");
while(rs.next())
{
// read the result set
System.out.println("name = " + rs.getString("name"));
System.out.println("id = " + rs.getInt("id"));
}
}
catch(SQLException e)
{
// if the error message is "out of memory",
// it probably means no database file is found
System.err.println(e.getMessage());
}
finally
{
try
{
if(connection != null)
connection.close();
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}
}
}

やや高度な内容ですが、MavenをJavaの開発に使っている人は、mavenのcentral repositoryにsyncされているこのSQLite JDBCライブラリを使うことができます。pom.xmlファイルに以下のような記述をするだけで自動的にダウンロードされます。
<dependencies>
<dependency>
<groupid>org.xerial</groupid>
<artifactid>sqlite-jdbc</artifactid>
<version>3.6.4</version>
</dependency>
</dependencies>

SQLiteではファイルにデータベースを保存せずにメモリーの上でデータベースを構築することもできるので、データベースのテスト用にも最適です。

6 件のコメント:

doraneko さんのコメント...

JDBCドライバを使わせていただいております。
いきなりの不躾な質問ご容赦下さい。

サンプルコードなどで記載されている
Statement#setQueryTimeout()ですが
setQueryTimeout(1) など極端に短くして、
それなりに高コストのSQLを流しても、
数分経過してもSQLExceptionの例外に
ならないため、悩んでおります。

お忙しい中、恐縮ですが、もしお目に留まり
ましたら、ご教示頂けませんでしょうか。
コメント欄への書込みにて失礼致します。

doraneko さんのコメント...

JDBCドライバを使わせていただいております。
いきなりの不躾な質問ご容赦下さい。

サンプルコードなどで記載されている
Statement#setQueryTimeout()ですが
setQueryTimeout(1) など極端に短くして、
それなりに高コストのSQLを流しても、
数分経過してもSQLExceptionの例外に
ならないため、悩んでおります。

お忙しい中、恐縮ですが、もしお目に留まり
ましたら、ご教示頂けませんでしょうか。
コメント欄への書込みにて失礼致します。

Taro L. Saito (leo) さんのコメント...

> doraneko さん
そもそもSQLiteにはtimeoutを判定する機能がありません。複数のconnectionが同時にデータベースにアクセスしたときの待ち時間の上限はsetQueryTimeout()で指定できるのですが。。。

問題の報告に関しては、今後
http://code.google.com/p/xerial/issues/list
をお使いください。こちらの方が更新が通知されるので便利です。

また、問題を再現するテストコードなどを用意していただけると、workaroundなどを用意したり問題解決の早道になるので助かります。問題を再現するだけでも数時間費やしてしまうことに鳴るので。。。

doraneko さんのコメント...

突然の投稿へのご返答、ありがとうございます。

> 複数のconnectionが同時にデータベースに
> アクセスしたときの待ち時間の上限は
> setQueryTimeout()で指定できるのですが

setQueryTimeout()メソッドの趣旨について、
納得致しました。
ご教示ありがとうございます。

頂いたフォーラム?のほうにも、折を見て
要望として書き込みさせて頂きます。
(テストコード呼べるものかは疑問ですが)

rizel さんのコメント...

いきなりの質問失礼いたします。
研究の都合でSQLiteをjavaで使う必要が出たのですが、公開されているドライバはver2~のSQLiteに使えるでしょうか?

見当違いな質問になっているかもしれませんが、もし返答をいただければ幸いです。

Taro L. Saito (leo) さんのコメント...

>rizelさん

version2.x はもうサポートしていません。sqlite-jdbcを使うときは、version3.x 以降に対応できるよう、db側をアップグレードする必要があります

License

Creative Commons LicenseLeo's Chronicle by Taro L. Saito is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.1 Japan License.