使用 Apache 常用 DBCP 和 DBUtils 的连接池和并发

Posted

技术标签:

【中文标题】使用 Apache 常用 DBCP 和 DBUtils 的连接池和并发【英文标题】:Connection Pooling using Apache common DBCP And DBUtils and concurrency 【发布时间】:2013-06-09 14:25:42 【问题描述】:

我正在研究在不同线程中执行多个查询时的并发性。 我使用 Apache DBCP 和 DBUtils 不是因为我想让我的生活复杂化,而是因为它们应该保证查询得到正确处理以及并发性。

但是,即使使用上述很酷的工具,我也会得到:

Error : org.h2.jdbc.JdbcSQLException: Das Objekt wurde bereits geschlossen
The object is already closed [90007-148]
Error : java.lang.NullPointerException

这与手动使用数据库和连接对象时遇到的错误相同。 程序每运行 5-6 次就会发生一次,但这只是一个玩具程序,在现实世界的应用程序中,这种错误会不断弹出。

在我的示例代码下方

DatatTransaction.java

import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;

public class DataTransaction

    private final static String username = "";
    private final static String password = "";
    private final static String url = "db" + File.separator + "persondb;create=true";
    public static Connection connection = null;
    public static BasicDataSource dataSource;

    public DataTransaction(boolean setCon)
    
        try
        
            setConnectionTest();
        
        catch (Exception e)
        
            System.out.println("Error in Connection:" + e.toString());
        
    

    public final void setConnectionTest() throws SQLException
    
        try
        
            if (dataSource == null)
            
                dataSource = new BasicDataSource();
                String driver = "org.h2.Driver";
                try
                
                    dataSource.setDriverClassName(driver);
                    dataSource.setUrl("jdbc:h2:"+url);
                    dataSource.setUsername(username);
                    dataSource.setPassword(password);
                    dataSource.setMaxActive(100);
                    dataSource.setMaxWait(10000);
                    dataSource.setMaxIdle(10);
                    if (connection == null || connection.isClosed())
                    
                        connection = dataSource.getConnection();
                    
                
                catch (SQLException e)
                
                    System.out.println("Could not connect to the database msg :" + e.getMessage());
                
            
            else
            
                connection = dataSource.getConnection();
            
        
        catch (Exception e)
        
            System.out.println("open connection exception" + e);
        
    

DBTest2.java

package dbtest;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class DBTest2


    public static void main(String[] args)
    
        try
        
            new Thread(db1).start();
            new Thread(db2).start();
        
        catch (Exception e)
        
            System.out.println("MM : Error : " + e);
        
    
    private static Runnable db1 = new Runnable()
    
        public void run()
        
            try
            
                for (int i = 0; i < 50; i++)
                
                    DBTest2 dBTest = new DBTest2();
                    List<Object[]> list1 = dBTest.DB1();
                    for (Object[] object : list1)
                    
                        System.out.println("DB1 : FirstName : " + object[0] + " Lastname: " + object[1]);
                    
                
            
            catch (Exception e)
            
                System.out.println("Error : " + e);
            
        
    ;

    private static Runnable db2 = new Runnable()
    
        public void run()
        
            try
            
                for (int i = 0; i < 50; i++)
                
                    DBTest2 dBTest = new DBTest2();
                    List<Object[]> list = dBTest.DB2();
                    for (Object[] object : list)
                    
                        System.out.println("DB2 : FirstName : " + object[0] + " Lastname: " + object[1]);
                    
                
            
            catch (Exception e)
            
                System.out.println("Error : " + e);
            
        
    ;

    public List<Object[]> DB1()
    
        try
        
            DataTransaction dt = new DataTransaction(true);
            Connection conn = dt.connection;
            Statement statement = conn.createStatement();
            ResultSet rs = statement.executeQuery("select NAME,SURNAME from PERSON");
            ResultSetMetaData rsmd = rs.getMetaData();
            int dataCnt = rsmd.getColumnCount();
            List<Object[]> list = new ArrayList<Object[]>();
            while (rs.next())
            
                Object[] data = new Object[dataCnt];
                for (int i = 0; i < dataCnt; i++)
                
                    data[i] = rs.getString(i + 1);
                
                list.add(data);
            
            conn.close();
            return list;
        
        catch (Exception e)
        
            System.out.println("Error : " + e);
            return null;
        
    

    public List<Object[]> DB2()
    
        try
        
            DataTransaction dt = new DataTransaction(true);
            Connection conn = dt.connection;
            Statement statement = conn.createStatement();
            ResultSet rs = statement.executeQuery("select NAME,SURNAME from PERSON");
            ResultSetMetaData rsmd = rs.getMetaData();
            int dataCnt = rsmd.getColumnCount();
            List<Object[]> list = new ArrayList<Object[]>();
            while (rs.next())
            
                Object[] data = new Object[dataCnt];
                for (int i = 0; i < dataCnt; i++)
                
                    data[i] = rs.getString(i + 1);
                
                list.add(data);
            
            conn.close();
            return list;
        
        catch (Exception e)
        
            System.out.println("Error : " + e);
            return null;
        
    

【问题讨论】:

请注意,如果我评论 connection.close();我得到一个不同的错误:打开连接异常org.apache.commons.dbcp.SQLNestedException:无法获得连接,池错误超时等待空闲对象 【参考方案1】:

您应该阅读derby pitfalls,其中有一些常见的陷阱,您感兴趣的是:

“执行语句会自动关闭任何现有打开的由先前执行该语句生成的结果集。如果线程共享语句,一个线程可以关闭另一个线程的结果集。在许多情况下,将每个线程分配给不同的连接更容易。 "

好的,所以你的代码的问题是DatatTransaction.java,通过删除静态变量连接来更改代码并添加这个方法:

    public final Connection getConnection()

    Connection conn = null;
    try
    
        conn = dataSource.getConnection();
    
    catch (SQLException e)
    
        System.out.println("Could not connect to the database msg :" + e.getMessage());
    
    return conn;

现在它不会再出现任何问题(顺便说一下,如果您在示例中注释 System.out.println(),您将更快、更频繁地看到并发错误,否则这些错误会因打印到控制台)。

至于“exceptionorg.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object”那是因为你没有通过注释close()方法正确关闭资源,所以你快速池中的连接用完。

【讨论】:

以上是关于使用 Apache 常用 DBCP 和 DBUtils 的连接池和并发的主要内容,如果未能解决你的问题,请参考以下文章

Java学习:数据库连接池DBCP的使用

连接池

DBCP(Apache Commons 数据库连接池)是不是仍然相关?

使用 Spring-Boot 启动 Tomcat 上下文时出错:java.lang.ClassNotFoundException: org.apache.tomcat.dbcp.dbcp.BasicD

如何解决这个 java.lang.ClassCastException: org.apache.tomcat.dbcp.dbcp.BasicDataSource 不能转换为 org.apache.to

JDBC 学习复习7 学习 Apache 开源DBCP 数据源