1

A full day of googling this problem has left me more confused than ever so I'm appealing to SO for help. I'm trying to use pooled connections to a mysql DB but I'm clearly misunderstanding something. Below are snippets of code from an application that scans a folder for new directories that represent "jobs"; when found, database objects are created for each folder found. I based the _insert() method on a pattern I found on SO. My understanding is that the connections are properly closed and returned to the connection pool. However, I noticed that, after adding 8 objects, the code would hang on getConnection(). I found somewhere that the default number of active connections was 8, so I added the debug line where I limit the number of active connections to 2. Sure enough, only two objects get added before the code hangs.

What's going on? What do I need to change to make these connections get freed and added back to the pool? I found one post that mentioned the PoolableConnection class but I'm confused by the documentation as well as by the fact that most other examples I've found don't seem to use it.

The Scanner class that creates Job objects in the database based on folders found in a particular directory on disk:

public class Scanner extends Thread {
    public void run() {     
        syncJobs();
    }

    void syncJobs(List<String> folderNames) {
        for (String folderName : folderNames) {
            Job job = addJobToDB(folderName);
        }
    }

    Job addJobToDB(String folderName ) {
        Job job = new Job();
        job.name = folderName;
        job.save();
        return job;
    }   
}

There's an abstract base class for all objects (each objects overrides _insert):

public abstract class DBObject {
    private final int insert() {
        return _insert();
    }

    public final void save() {
        if (id == 0)
            id = insert();
        else
            update();
    }   
}

And there's the actual Job object (with only the insert method shown):

public class Job extends DBObject {
    public int _insert()  {
        String query = "insert into jobs (name) values (?)";
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;    
        int id = 0;
        try {
            conn = Database.getConnection();
            ps = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
            ps.setInt(1, id);
            ps.executeUpdate();
            rs = ps.getGeneratedKeys();
            rs.next();
            id = rs.getInt(1);
        } catch (Exception e) {
            System.out.println(e.getMessage());             
        } finally {
            DbUtils.closeQuietly(rs);
            DbUtils.closeQuietly(ps);
            DbUtils.closeQuietly(conn);
        }
        return id;
    }       
}

And, lastly, the Database object that provides connections:

import org.apache.commons.dbcp.BasicDataSource;
public final class Database {
    private static final BasicDataSource dataSource = new BasicDataSource();

    static {
        dataSource.setUrl("jdbc:mysql://localhost:3306/dbName?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC");
        dataSource.setUsername("user");
        dataSource.setPassword("****");     

        // This line added for debugging: sure enough, only 2 objects are created. 
        dataSource.setMaxActive(2);
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }      
}
Gadzooks34
  • 1,718
  • 2
  • 20
  • 29
  • 1
    Try using `closeNoisily(conn)` instead of `closeQuietly(conn)` to check if connections are indeed being closed. – mustaccio Aug 21 '17 at 13:43
  • I changed all three statements from closeQuietly(xxx) to close(xxx) and it threw an exception on close(conn), complaining that the connection was already closed. I guess closing the resultSet is already taking care of the connection. Commented out that line and now back to original problem. – Gadzooks34 Aug 21 '17 at 13:48
  • 1
    Closing the `ResultSet` does not close a `Connection`. Closing a `Statement` closes its `ResultSet`, and closing a `Connection` closes its `Statements` and `ResultSets`. – Kayaman Aug 21 '17 at 13:54
  • Method `BasicDataSource.getConnection` is thread safe? If not add `synchronized` to method `Database.getConnection`. – anemomylos Aug 21 '17 at 14:00
  • Add synchronized to getConnection but had no effect. – Gadzooks34 Aug 21 '17 at 14:08
  • I see only one possible way - you do not close connections. `DbUtils.closeQuietly(conn);` wasn't called or do not close connection. Check imports `org.apache.commons.dbutils.DbUtils` or manually do `conn.close()` – user1516873 Aug 21 '17 at 14:46

0 Answers0