2010-12-20 59 views
11

我正在重構其他代碼。我注意到的一點是關於系統如何從連接池獲取連接的方式。從連接池獲取數據庫連接

樣品是這樣的。在每次調用服務方法時,系統都會在數據源的JNDI上進行上下文查找。

public class CheckinServlet extends HttpServlet { 

    public void doPost(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException { 

     try { 
      //Obtain Connection 
      InitialContext initialContext = new InitialContext(); 
      javax.sql.DataSource ds = (javax.sql.DataSource) initialContext 
        .lookup("jdbc/mysqldb"); 
      java.sql.Connection conn = ds.getConnection(); 
      //business logic 
      //redirect 
     } finally { 
      conn.close(); 
     } 
    } 
} 

我認爲每次都會有這樣的表現。我在想如何從連接池中檢索連接。

我在考慮使用servlet的init()方法,但我認爲這不是最優的。

回答

15

在一個ServletContextListener中執行一次,而不是在許多servlet的init()中每次執行一次。在webapp的啓動過程中,contextInitialized()方法僅執行一次。

public class Config implements ServletContextListener { 
    private static final String ATTRIBUTE_NAME = "config"; 
    private DataSource dataSource; 

    @Override 
    public void contextInitialized(ServletContextEvent event) { 
     ServletContext servletContext = event.getServletContext(); 
     String databaseName = servletContext.getInitParameter("database.name"); 
     try { 
      dataSource = (DataSource) new InitialContext().lookup(databaseName); 
     } catch (NamingException e) { 
      throw new RuntimeException("Config failed: datasource not found", e); 
     } 
     servletContext.setAttribute(ATTRIBUTE_NAME, this); 
    } 

    @Override 
    public void contextDestroyed(ServletContextEvent event) { 
     // NOOP. 
    } 

    public DataSource getDataSource() { 
     return dataSource; 
    } 

    public static Config getInstance(ServletContext servletContext) { 
     return (Config) servletContext.getAttribute(ATTRIBUTE_NAME); 
    } 
} 

進行如下配置在web.xml

<context-param> 
    <param-name>database.name</param-name> 
    <param-value>jdbc/mysqldb</param-value> 
</context-param> 
<listener> 
    <listener-class>com.example.Config</listener-class> 
</listener> 

獲得它在你的servlet如下(init()doXXX()方法,你選擇):

DataSource dataSource = Config.getInstance(getServletContext()).getDataSource(); 

我會然而,重構它更進一步,JDBC代碼應該放在它自己的類中,而不是放在servlet中。查找DAO模式。

+1

嗨,先生,謝謝你總是詳細的答案。但有一件事讓我想起這件事。爲什麼將數據源對象作爲上下文範圍參數放置?不管我們只是讓getDataSource()方法是靜態的?我非常喜歡這個答案,但我想進一步瞭解這樣做的原因。謝謝.. – 2010-12-21 02:42:42

+0

看到它也很好的設計。一個'DataSource'特定於一個'Config'實例,而不是多個'Config'實例。即使在這種情況下只有一個。 – BalusC 2010-12-21 02:51:13

+0

調用contextDestroyed(ServletContextEvent)時數據庫連接是否關閉? – 2011-04-07 15:22:25

3

我已經在過去使用的方法是創建一個保存數據源

例如一個單例類

public class DatabaseConnectionManager { 

    DataSource ds; 

    public void init() { 
     InitialContext initialContext = new InitialContext(); 
     ds = (javax.sql.DataSource)initialContext.lookup("jdbc/mysqldb"); 
    } 

    public Connection getConnection() { 
     if(ds == null) init(); 

     return ds.getConnection(); 
    } 
} 

這意味着你有一個共享引用到你的數據源,帶走了jndi的查找開銷。

2

除此之外,還有一個名爲Service Locator的設計模式,它基本上是一個包含名爲「service」的註冊表的單例,用於存放您的JNDI對象。

基本上,如果在註冊表中找不到該對象,則服務將從JNDI池中取出並在註冊表中註冊。下一次調用將簡單地從註冊表中提取對象。

希望這會有所幫助。

3

我剛剛做了一些測試,發現jndi的查找時間並不那麼沉重。在這裏約1秒內查找50.000次。

所以在許多情況下,我根本沒有看到緩存DataSource的原因。

緩存問題是,如果您更改了與數據源定義相關的任何內容,您最終可能會生成一個陳舊的DataSource,迫使您重新啓動應用程序。