2010-12-20 136 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,迫使您重新启动应用程序。