Java > SQLiteをJavaから使う方法
更新日
2013-12-21
広告
SQLiteは組み込み型のRDBMSです。
組み込み型のせいか、マルチスレッド/プロセスからアクセスするとエラーが発生することがあります。
以下、https://bitbucket.org/xerial/sqlite-jdbc で公開されているSQLiteのJDBCドライバを使って説明します。
エラーが発生する例
import java.sql.*; import java.util.*; public class Sample1 { public static void main(String[] args) throws Exception { Class.forName("org.sqlite.JDBC"); Connection connection = null; // init table try { connection = DriverManager.getConnection("jdbc:sqlite:sample.db"); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); statement.executeUpdate("drop table if exists person"); statement.executeUpdate("create table person (id integer, name string)"); } catch (SQLException e) { System.out.println(e); } finally { try { if(connection != null) { connection.close(); } } catch (SQLException e) { System.out.println(e); } } // access from multi threads. List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 100; i++) { Thread thread = new Thread(new InsertThread()); thread.start(); threads.add(thread); } try { for (Thread thread : threads) { thread.join(); } } catch (Exception e) { e.printStackTrace(); } } } class InsertThread implements Runnable { public void run() { Connection connection = null; try { connection = DriverManager.getConnection("jdbc:sqlite:sample.db"); Statement statement = connection.createStatement(); statement.executeUpdate("insert into person values(100, 'hoge')"); } catch (SQLException e) { System.out.println(e); } finally { try { if (connection != null) { connection.close(); } } catch (SQLException e) { System.out.println(e); } } } }このサンプルでは、100個のスレッドから、一斉にインサートを実行します。 各スレッドが、コネクションを作成します。 すると、以下のエラーが発生します。
java.sql.SQLException: database is lockedPostgreSQLやMySQLのようなスタンドアロン型のRDBMSであれば、このようなエラーは発生しません。
エラーを発生させない例
上で示したように、SQLiteにマルチスレッドでアクセスする場合は、注意が必要です。 以下に、上記エラーを回避する方法を紹介します。import java.sql.*; import java.util.*; public class Sample2 { public static void main(String[] args) throws Exception { Class.forName("org.sqlite.JDBC"); Connection connection = null; // init table try { connection = DriverManager.getConnection("jdbc:sqlite:sample.db"); Statement statement = connection.createStatement(); statement.setQueryTimeout(30); statement.executeUpdate("drop table if exists person"); statement.executeUpdate("create table person (id integer, name string)"); } catch (SQLException e) { System.out.println(e); } finally { try { if (connection != null) { connection.close(); } } catch (SQLException e) { System.out.println(e); } } // access from multi threads List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 100; i++) { Thread thread = new Thread(new SyncInsertThread()); thread.start(); threads.add(thread); } try { for (Thread thread : threads) { thread.join(); } } catch (Exception e) { e.printStackTrace(); } try { connection.close(); } catch (SQLException e) { System.out.println(e); } } } class SyncInsertThread implements Runnable { private static Connection connection; static { try { connection = DriverManager.getConnection("jdbc:sqlite:sample.db"); connection.setAutoCommit(false); } catch (Exception e) { System.out.println(e); } } public void run() { insert(); } public static synchronized void insert() { try { Statement statement = connection.createStatement(); statement.setQueryTimeout(30); statement.executeUpdate("insert into person values(100, 'hoge')"); connection.commit(); statement.close(); } catch (SQLException e) { System.out.println(e); } } }
この例では、1つしかConnectionオブジェクトを作成しません。 また、DBアクセスを行うメソッドをstatic synchronizedにしています。 つまり、同時に1つのスレッドのみが、DBアクセスを行います。
こうすると、上記のエラーは発生しません。 各スレッドが順番にDBアクセスするので、処理に時間がかかりますが、エラーを回避するほうが重要だと思います。
広告
お問い合わせは sweng.tips@gmail.com まで。