0

I wrote the code below - I want the DataSource to be a singleton and use the enum idiom for the singleton. I am getting a lot of Data source rejected establishment of connection, message from server: "Too many connections" after some time - is my implementation of the Singleton pattern wrong or the cause is somewhere else ?

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

class DBConnectionPool {

    private DataSource ds = null;

    private DBConnectionPool() {
        try {
            Context context = new InitialContext();
            Context envctx = (Context) context.lookup("java:comp/env");
            ds = (DataSource) envctx.lookup("jdbc/TestDB");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static enum PoolSingleton {
        POOL_INSTANCE;

        private static final DBConnectionPool singleton = new DBConnectionPool();

        private DBConnectionPool getSingleton() {
            return singleton;
        }
    }

    private static DBConnectionPool getDBConnectionPoolInstance() {
        return PoolSingleton.POOL_INSTANCE.getSingleton();
    }

    static Connection getConnection() {
        try {
            return getDBConnectionPoolInstance().ds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
}

For completeness sake here are the contents of context.xml :

<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/myapp">
    <Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
        maxActive="100" maxIdle="30" maxWait="10000" username="root"
        password="root" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/mydb"
        removeAbandoned="true" removeAbandonedTimeout="60" />
</Context>

NB : I am not implementing a connection Pool ! I use the tomcat factory. What I do is wrapping the pool in a Class. The pool is the ds object I think is instantiated only once. I do close the connections +sets + statements in a finally block. Example use :

public User findByUsername(String username) throws DBExFailure {
        Connection conn = DBConnectionPool.getConnection();
        PreparedStatement statement = null;
        ResultSet set = null;
        final String query = "SELECT * FROM users WHERE username=?";
        if (conn != null) {
            try {
                statement = conn.prepareStatement(query);
                statement.setString(1, username);
                set = statement.executeQuery();
                while (set.next()) {
                    User user = new User();
                    // user.setId(set.getInt("ID"));
                    user.setUsername(set.getString("username"));
                    user.setName(set.getString("name"));
                    user.setSurname(set.getString("surname"));
                    user.setPassword(set.getString("password"));
                    user.setEmail(set.getString("email"));
                    user.setRole(RolesENUM.values()[set.getInt("role")]);
                    return user;
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
                throw new DBExFailure();
            } finally {
                DBConnectionPool.closeResources(set, statement, conn);
            }
        }
        return null;
    }

Where :

static void closeResources(ResultSet set, Statement statement,
        Connection conn) {
    if (set != null) {
        try {
            set.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            statement.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
}
4

2 回答 2

5

我不得不说,写一个的正确方法是不写一个:使用现有的库,如C3P0

这也不意味着是一个“聪明的裤子”答案。这只是一个“不要重新发明轮子”的方法,尤其是线程安全池实现,它很难正确执行,并且通常会创建一个与线程相关的、微妙且难以修复的错误的雷区。

于 2012-09-22T02:34:15.187 回答
2

您的池很浅 - 它只有一个连接!这将如何扩展?

您已经硬连线了 JNDI 名称。为什么不传进去?

这对于多线程应用程序来说不是很好。连接不是线程安全的。

你得到的最好的建议是“不要这样做”。使用现有池。或者,更好的是,内置在您的 Java EE 应用服务器中的那个。

于 2012-09-22T02:46:09.087 回答