1

Я написал простой Java-код, который пытается хранить файлы в базе данных hsqldb. Все, что он делает, это считывать файлы из определенного каталога и помещать их в БД. Он однопоточный, но я использую объединенные соединения из apache commons.dbcp, чтобы позже справляться с многопоточным доступом.Почему следующий простой кусочек Java-кода использует объединенные соединения из блока commons.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(); 
    } 
    } 
} 

Код зависит от фонда-ГСБД 1.4, обыкновенные бассейн 1,6 и HSQLDB 2.2.9

Запуск этого в каталоге проекта должен поместить 62 файла в БД (у меня много re файлы там, чем два вышеупомянутых исходных файла), распечатывая строку для каждого файла.

К сожалению, блоки на восьмой файл со следующей трассировки стека:

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) 

Что я делаю не так?

ответ

3

Вы звоните:

m_dataSource.getConnection() 

в ваших путах метода (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(); 
     } 
    } 
    } 

Примечание, когда вы получаете доступ подключения через пул, вы должны вызвать близко. Это фактически не закрывает соединение, а возвращает его в пул.

+0

Глупо меня, конечно. Спасибо. – mark