Java - mysql连接池的Singleton模式 - 连接太多

时间:2012-09-22 02:00:34

标签: java mysql singleton tomcat7 connection-pooling

我编写了下面的代码 - 我希望DataSource是一个单例,并使用枚举习惯用于单例。经过一段时间后,我得到了很多Data source rejected establishment of connection, message from server: "Too many connections" - 我对Singleton模式的实现是错误的还是原因在其他地方?

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;
        }
    }
}

为了完整起见,这里是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:我没有实现连接池!我用的是tomcat工厂。我所做的是将池包装在一个类中。该池是我认为仅实例化一次的ds对象。我关闭了finally块中的连接+集合+语句。使用示例:

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;
    }

其中:

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();
        }
    }
}

2 个答案:

答案 0 :(得分:5)

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

这也不是一个“聪明的裤子”的答案。它只是一个“不要重新发明轮子”,特别是一个线程安全的池实现,这很难做到正确,通常创建一个与线程相关,微妙和难以修复的错误的雷区。

答案 1 :(得分:2)

你的游泳池非常浅 - 里面只有一个连接!这个规模怎么样?

您已经硬连接了JNDI名称。为什么不把它传入?

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

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