web-dev-qa-db-ja.com

JDBC接続プールを使用していますか?

JDBC接続プールを実際に使用しているかどうかを確認しようとしています。いくつかの調査を行った後、実装はほとんど簡単すぎるようです。実際には通常の接続よりも簡単なので、確認したいと思います。

これが私の接続クラスです:

public class DatabaseConnection {

Connection conn = null;

public Connection getConnection() {

    BasicDataSource bds = new BasicDataSource();
    bds.setDriverClassName("com.mysql.jdbc.Driver");
    bds.setUrl("jdbc:mysql://localhost:3306/data");
    bds.setUsername("USERNAME");
    bds.setPassword("PASSWORD");

    try{
        System.out.println("Attempting Database Connection");
        conn = bds.getConnection();
        System.out.println("Connected Successfully");
    }catch(SQLException e){
        System.out.println("Caught SQL Exception: " + e);
    }
    return conn;
}

public void closeConnection() throws SQLException {
    conn.close();
}

}

これは本当の接続プーリングですか?別のクラスで接続を使用しています:

        //Check data against database.
    DatabaseConnection dbConn = new DatabaseConnection();
    Connection conn;
    ResultSet rs;
    PreparedStatement prepStmt;

    //Query database and check username/pass against table.
    try{
        conn = dbConn.getConnection();
        String sql = "SELECT * FROM users WHERE username=? AND password=?";
        prepStmt = conn.prepareStatement(sql);
        prepStmt.setString(1, user.getUsername());
        prepStmt.setString(2, user.getPassword());
        rs = prepStmt.executeQuery();

        if(rs.next()){ //Found Match.
            do{
                out.println("UserName = " + rs.getObject("username") + " Password = " + rs.getObject("password"));
                out.println("<br>");
            } while(rs.next());
        } else {
            out.println("Sorry, you are not in my database."); //No Match.
        }

        dbConn.closeConnection(); //Close db connection.

    }catch(SQLException e){
        System.out.println("Caught SQL Exception: " + e);
    }
31
ryandlf

BasicDataSource[〜#〜] dbcp [〜#〜] からのものであると想定すると、はい、接続プールを使用しています。ただし、接続を取得するたびに別の接続プールを再作成しています。同じプールからの接続を実際にプールしているわけではありません。アプリケーションの起動時に一度だけ接続プールを作成し、そこからすべての接続を取得する必要があります。また、接続をインスタンス変数として保持しないでください。また、例外が発生した場合にも、リソースが適切に閉じられるように、接続、ステートメント、結果セットを閉じる必要があります。 Java 7's try-with-resourcesステートメント はこれに役立ち、tryブロックが終了するとリソースを自動的に閉じます。

ここに小さな書き直しがあります:

public final class Database {

    private static final BasicDataSource dataSource = new BasicDataSource();

    static {
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/data");
        dataSource.setUsername("USERNAME");
        dataSource.setPassword("PASSWORD");
    }

    private Database() {
        //
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

}

(これは、必要に応じて、プラグイン性を向上させるために抽象ファクトリーとしてリファクタリングできます)

そして

private static final String SQL_EXIST = "SELECT * FROM users WHERE username=? AND password=?";

public boolean exist(User user) throws SQLException {
    boolean exist = false;

    try (
        Connection connection = Database.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL_EXIST);
    ) {
        statement.setString(1, user.getUsername());
        statement.setString(2, user.getPassword());

        try (ResultSet resultSet = preparedStatement.executeQuery()) {
            exist = resultSet.next();
        }
    }       

    return exist;
}

これは次のように使用されます。

try {
    if (!userDAO.exist(username, password)) {
        request.setAttribute("message", "Unknown login. Try again.");
        request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
    } else {
        request.getSession().setAttribute("user", username);
        response.sendRedirect("userhome");
    }
} catch (SQLException e) {
    throw new ServletException("DB error", e);
}

ただし、実際のJava EE環境では、DataSourceの作成をコンテナ/アプリケーションサーバーに委任し、JNDIから取得する必要があります。Tomcatの場合は、たとえば、このドキュメント: http://Tomcat.Apache.org/Tomcat-6.0-doc/jndi-resources-howto.html

55
BalusC

プールされているようには見えません。 getConnection()を呼び出すたびに新しいデータソースを作成するのではなく、データベース接続にデータソースを保存する必要があります。 getConnection()はdatasource.getConnection()を返す必要があります。

3
andy

DBCPの使用法のように見えます。もしそうなら、そうです。すでにプールされています。 DBCPのデフォルトのプールプロパティ値を次に示します。

/**
* The default cap on the number of "sleeping" instances in the pool.
* @see #getMaxIdle
* @see #setMaxIdle
*/
public static final int DEFAULT_MAX_IDLE  = 8;
/**
* The default minimum number of "sleeping" instances in the pool
* before before the evictor thread (if active) spawns new objects.
* @see #getMinIdle
* @see #setMinIdle
*/
public static final int DEFAULT_MIN_IDLE = 0;
/**
* The default cap on the total number of active instances from the pool.
* @see #getMaxActive
*/
public static final int DEFAULT_MAX_ACTIVE  = 8;
2
phyerbarte

BalusCのソリューションのフォローアップとして、以下は、複数の接続を必要とするアプリケーション内、または事前に接続プロパティを知らない一般的なライブラリ内で使用できる実装です...

import org.Apache.commons.dbcp.BasicDataSource;

import Java.sql.Connection;
import Java.sql.SQLException;
import Java.util.concurrent.ConcurrentHashMap;

public final class Database {

    private static final ConcurrentHashMap<String, BasicDataSource> dataSources = new ConcurrentHashMap();

    private Database() {
        //
    }

    public static Connection getConnection(String connectionString, String username, String password) throws SQLException {

        BasicDataSource dataSource;

        if (dataSources.containsKey(connectionString)) {
            dataSource = dataSources.get(connectionString);
        } else {
            dataSource = new BasicDataSource();
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl(connectionString);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            dataSources.put(connectionString, dataSource);
        }

        return dataSource.getConnection();

    }

}
1
Todd W. Powers