1

hsqldbデータベースにファイルを保存しようとする単純なJavaコードを作成しました。特定のディレクトリからファイルを読み取り、それらをDBに配置するだけです。シングルスレッドですが、後でマルチスレッドアクセスに対応できるようにするために、apachecommons.dbcpからのプールされた接続を使用しています。

問題は、いくつかのファイルを読み取った後にコードがブロックされることです。

以下にソースコード全体を見つけてください。

Program.java

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.KeyedObjectPoolFactory;
import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory;
import org.apache.commons.pool.impl.GenericObjectPool;

public class Program {
  public static DataSource getPoolingDataSource(String driverClass, String url, String user, String password) throws ClassNotFoundException {
    Class.forName(driverClass);
    ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(url, user, password);
    GenericObjectPool connectionPool = new GenericObjectPool();
    KeyedObjectPoolFactory stmtPool = new GenericKeyedObjectPoolFactory(null);
    new PoolableConnectionFactory(connectionFactory, connectionPool, stmtPool, null, false, true);
    return new PoolingDataSource(connectionPool);
  }

  public static void main(String[] args) throws ClassNotFoundException, SQLException, IOException, InterruptedException {
    String root = args.length == 0 ? "c:/Work/java/ntxdb" : args[0];

    Runtime run = Runtime.getRuntime();
    Process pr = run.exec("cmd /c del /s/q c:\\tmp\\file.db*");
    pr.waitFor();
    DataSource ds = getPoolingDataSource("org.hsqldb.jdbcDriver", "jdbc:hsqldb:file:c:/tmp/file.db", "sa", "");
    HsqldbFileStorage fs = new HsqldbFileStorage(ds);
    putFiles(fs, new File(root));
  }

  private static void putFiles(HsqldbFileStorage fs, File parent) throws IOException, SQLException {
    for (File child : parent.listFiles()) {
      if (child.isDirectory()) {
        putFiles(fs, child);
      } else {
        System.out.println(child.getCanonicalPath());
        fs.put(child);
      }
    }
  }
}

HsqldbFileStorage.java

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;

public class HsqldbFileStorage {
  private static final String SET_SQL = "MERGE INTO test" +
    "  USING (VALUES ?, CAST(? AS BLOB)) I (name, data)" +
    "  ON (test.name=I.name)" +
    "  WHEN MATCHED THEN UPDATE SET test.data = I.data" +
    "  WHEN NOT MATCHED THEN INSERT (name, data) VALUES (I.name, I.data)";
  private DataSource m_dataSource;

  public HsqldbFileStorage(DataSource dataSource) throws SQLException {
    super();
    m_dataSource = dataSource;
    Connection c = dataSource.getConnection();
    c.createStatement().execute("Create Cached Table IF NOT EXISTS test (name VARCHAR(256), data BLOB(10M));");
  }

  public void put(File file) throws IOException, SQLException {
    put(file.getCanonicalPath(), file);
  }

  public void put(String name, File file) throws IOException, SQLException {
    InputStream is = new BufferedInputStream(new FileInputStream(file));
    try {
      put(name, is);
    } finally {
      is.close();
    }
  }

  public void put(String name, InputStream data) throws SQLException, IOException {
    PreparedStatement set = m_dataSource.getConnection().prepareStatement(SET_SQL);
    try {
      set.setString(1, name);
      set.setBinaryStream(2, data);
      set.executeUpdate();
    } finally {
      set.close();
    }
  }
}

コードはcommons-dbcp1.4、commons-pool 1.6、hsqldb2.2.9に依存します

プロジェクトディレクトリ自体で実行すると、DBに62個のファイルが配置され(前述の2つのソースファイルよりもはるかに多くのファイルがあります)、ファイルごとに1行が出力されます。

残念ながら、次のスタックトレースで8番目のファイルをブロックします。

  at java.lang.Object.wait(Object.java:-1)
  at java.lang.Object.wait(Object.java:485)
  at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1118)
  at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
  at HsqldbFileStorage.put(HsqldbFileStorage.java:41)
  at HsqldbFileStorage.put(HsqldbFileStorage.java:34)
  at HsqldbFileStorage.put(HsqldbFileStorage.java:28)
  at Program.putFiles(Program.java:42)
  at Program.putFiles(Program.java:39)
  at Program.putFiles(Program.java:39)
  at Program.main(Program.java:33)

私は何が間違っているのですか?

4

1 に答える 1

3

あなたが呼んでいる:

m_dataSource.getConnection()

put(String、InputStream)メソッドで。これにより、新しい接続が作成されますが、これを閉じることはありません。

したがって、ある段階で62個のファイルを挿入すると、プール内の接続の最大数に達し、その後、プールは接続がプールに返されるのを待ちます。

メソッドをそのように変更する場合:

  public void put(String name, InputStream data) throws SQLException, IOException {
    Connection con = null;
    PreparedStatement set = null;
    try {
      con = m_dataSource.getConnection();
      set.prepareStatement(SET_SQL);
      set.setString(1, name);
      set.setBinaryStream(2, data);
      set.executeUpdate();
    } finally {
      if (set != null) {
        set.close();
      }
      if (con != null) {
        con.close();
      }
    }
  }

プールを介して接続にアクセスする場合でも、closeを呼び出す必要があることに注意してください。これは実際には接続を閉じませんが、プールに戻します。

于 2013-03-02T14:32:58.790 に答える