2012-09-22 145 views
1

我編寫了下面的代碼 - 我想讓DataSource成爲一個單例,並使用單例的枚舉成語。我在一段時間後得到了很多Data source rejected establishment of connection, message from server: "Too many connections" - 我的Singleton模式的實現是錯誤的還是原因在其他地方?Java - 針對mysql連接池的單例模式 - 連接太多

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> 

注:我沒有實現連接池!我使用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(); 
     } 
    } 
} 

回答

5

我不得不說的是正確的方式來寫一個就是不寫一個:使用現有的庫像

這不是也意味着一個「聰明的褲子」的答案。它只是一個「不要重新發明輪子」的程序,特別是線程安全的池實現,這很難做到正確,並且通常會創建一個線程相關,微妙且難以修復的錯誤的雷區。

+1

+1 - 用於合理的建議。最好的答案通常是「不要那樣做」。 (不管是提問者希望聽到的是另一回事......) –

2

你的游泳池很淺 - 它只有一個連接!這將如何擴展?

你已經硬連線JNDI名稱。爲什麼不通過它?

這對多線程應用程序來說不是很好。連接不是線程安全的。

你得到的最好建議是「不要這樣做」。使用現有的池。或者,更好的是,構建到Java EE應用服務器中的那個。

+0

只需遵循課堂建議 - 但是我擔心多線程 - 爲什麼Pool很淺?每當我要求連接時,數據源都不會提供一個新的數據源(編輯的問題包含'context.xml'的內容?我擔心Tomcat沒有任何內存池 - 將會查看它.JDDI對於我還沒有(我發佈的代碼是枚舉修改類講義排序) –

+1

Tomcat有一個池 - 你可能沒有配置它。http://tomcat.apache.org/tomcat-7.0-doc/jndi-datasource- examples-howto.html – duffymo

+0

注意詳細說明如何通過一個xml文件傳遞JDNI名稱?一個'.properties'文件?你確定'getConnection'不是線程安全的嗎?實際上這個池是淺的 - 請參閱添加的context.xml –