【问题标题】:"Lazy initialization" of jdbc connections from jndi datasource/connection pool: feasibility来自 jndi 数据源/连接池的 jdbc 连接的“延迟初始化”:可行性
【发布时间】:2012-05-01 16:40:33
【问题描述】:

我有一个主控制器 servlet,我在其中实例化了一个 数据源。 servlet 打开和关闭连接。主要是,servlet 使用“工厂模式”实例化来自应用程序的命令。这里有一些代码来解释:

public void init() throws ServletException {
    super.init();
    try {
            datasource =(DataSource) getServletContext().getAttribute("DBCPool");
    }
    catch (Exception e) {

    }
}
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
  //some code...
  Connection connection = null;
  if(cmd.mightNeedLazyLoadingAConnection)
  {

       connection = null;
  } 
  else 
       connection = getConnection();//where getConnection is a method: datasource.getconnection();

      //now a command (a java class) is instantied, to which the "null" CONNECTION obj is passed as parameter
     cmdFactory.getInstance().getCommand(Cmd).execute(tsk,connection);


  //some code

//Then wherever there is catch exception i close() the connection
// and it is always closed in finally
finally { 
 if(connection!=null)
  connection.close()
 }

}

现在,对于第一种情况,即connection=null,这有一个问题,因为它永远不会关闭“finally”中的连接 b> 部分(在下面的 Update 中解释了原因)。

"connection=null" 适用于命令可能不需要打开数据库连接的情况,因为它正在寻找的数据缓存在 identity map 中。

我尝试在 .execute(tsk,connection); 中将 "Connection" obj 作为“null”参数传递,然后在相应的 java 类中打开一个连接如果需要

--> 它确实在命令中打开了连接,但是当进程返回到 servlet 时:“Connection”为空,因此未关闭。
我该怎么做才能使 "Connection" obj 的值得到更新,以便在返回 servlet 时它不再是“Null”并且我可以关闭它?

我通常更喜欢使用 打开/关闭 db 连接的控制器 servlet,那么处理这种必须执行某种“延迟加载”的场景的最佳方法是什么来自池的 db 连接,同时保持分配给 servlet 的 db 连接的 opens/close

更新(进一步解释):

  • 说我有一个命令:X.java
  • 此命令可能/可能不需要数据库连接(取决于搜索的数据是否在身份映射中)

我想要的系统是:

(1)“客户请求”

(2)---> "Servlet": command.execute(connection) //where connection = null for now

(3) ---> “Command X”:我需要去数据库还是记录在身份映射中?
(3.a) 需要去数据库的情况:
(3.a.1)connection = datasource.getconnection
(3.a.2) 去获取数据

(4)--->返回到servlet:关闭“Servlet”中的“连接”

现在它一直工作到 (3.a.2),但一旦回到 (4),连接似乎仍然是“null”,因此代码:

finally { 
 if(connection!=null)
  connection.close()
 }

不起作用(不关闭连接),因此数据库池会像那样被耗尽。 连接(以“null”开头并在命令“X”内发生变化)如何将“全局”更新为其新值,而不仅仅是在命令“X”范围内“更新”?

解决方案

如果您遇到相同的情况,您可以选择以下 2 种解决方案:

  • 您可以使用 LazyConnectionDataSourceProxy,正如 @Ryan Stewart 所提到的“干净的抽象”和更专业的解决方案

  • 或者,如果您想使用下面描述的我的解决方案(基本上我实现了一个类似于“LazyConnectionDataSourceProxy”的类,但它不是那么干净,它比“LazyConnectionDataSourceProxy”具有更少的细节抽象)

我的个人解决方案,详情:

  • 我创建了一个“Helper”类,它的构造函数将“datasource”作为参数
  • 此帮助程序类具有以下方法:“延迟获取”连接池、“关闭”连接
  • 这个类在 servlet 中实例化,它在整个应用程序需要时从池中获取连接。

这是我在 servlet 中添加/修改的代码:

Connection connection = null;
if(cmd.mightNeedLazyLoadingAConnection)
{

     helper hp =  new helper(datasource);
     cmdFactory.getInstance().getCommand(Cmd).execute(tsk,hp);
} 
else 
{
     connection = getConnection(); 
     cmdFactory.getInstance().getCommand(Cmd).execute(tsk,connection);
}

然后在命令“X”中说,我需要一个数据库连接:

Connection connection = hp.LazyGet();//Now got a connection from the pool

这样,当进程流回到servlet级别时,我可以:

  • 关闭
  • 回滚
  • 提交
  • 等等..

所有在帮助类的这个 hp 对象上。

我能从中得到什么好处:

  • 我将所有数据库打开/关闭/提交/回滚限制在一个的地方,即负责执行命令的Servlet。
  • 3 个案例:从不需要 db / 总是需要 db / 可能需要 db 因此现在我减少了对数据库的调用 1/3 ,知道数据库调用随着新功能和新用户注册而呈指数增长,这非常重要。

这可能不是 最干净的 解决方法,但在这种方式和额外的“不必要的”1/3 数据库调用之间,它肯定更好。或者,如果您想要一个经过测试、抽象且干净的方法,请使用LazyConnectionDataSourceProxy

【问题讨论】:

    标签: java servlets jdbc jndi connection-pooling


    【解决方案1】:

    使用LazyConnectionDataSourceProxy。然后每次都获得一个“连接”,但只有在您实际执行需要的操作时才会打开真正的连接。因此,您遵循 Hiro2k 指出的“创建/销毁”智慧,因为连接的生命周期完全由您的 servlet 管理。

    【讨论】:

    • 感谢您的提示。我刚刚在几分钟前发布了我的解决方案。我没有使用框架,但我做了某种帮助/代理类(检查我的答案)。
    • 我没有说任何关于使用框架的事情。 LazyConnectionDataSourceProxy 的作用与您的解决方案描述的几乎相同,只是它将肮脏的细节隐藏在干净的抽象层后面。没有“可能需要连接”或“懒惰获取”的概念。您只需将其视为提供普通Connections 的普通DataSource,即可免费获得懒惰。由于这些原因,这是一个更好的选择。
    【解决方案2】:

    在您的特定情况下,唯一的方法是返回连接。 Java 没有任何可以帮助您的引用传递语义,这与 C 不同,您可以将引用传递给连接,然后在方法中设置它。

    我不建议你的方法返回连接,而是记住这个简单的规则,一切都会如你所愿:

    创建它的对象负责销毁它。

    如果您不想为不需要的命令实例化连接,则向您的命令接口添加一个方法,该方法在需要时简单地返回。

    Command command = cmdFactory.getInstance().getCommand(Cmd);
    if(command.requiresConnections){
         connection = getConnection();
    }
    command.execute(tsk,connection);
    

    【讨论】:

    • 感谢提示。我无法返回连接,因为每个命令都会返回要发送到客户端的数据。至于:“创建它的对象,负责销毁它。” ,非常正确,但这意味着我必须在每个命令中打开/关闭连接。我正在尝试查看是否可能有一种方法可以将打开/关闭仅留在一个地方,即 servlet。如果看起来绝对没有办法,那我就照你说的做。
    • 再次感谢,但我认为您误解了我的问题,您所写的是我的代码中已有的内容。现在的代码“始终”打开一个连接,我想知道的是命令何时可能/可能不需要连接,如何将“连接”空对象传递给命令,以及“那里”你打开一个连接,当流程返回到 servlet 时,您在 servlet 中“关闭”该“连接”。
    • 根据什么标准决定“命令何时可能/可能不需要连接”
    • @HardikMishra 如果命令需要/可能需要(延迟加载)/不需要连接,我有一个过滤器可以从字典中进行检查
    • 你没看懂我的代码 shadesco。我建议您创建一个名为 requiresConnections 的新方法,它使用相同的逻辑告诉 servlet 它需要创建连接。我已经说过,不可能按照你想要的方式去做,但是这个替代方案应该仍然可以解决你的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-21
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 1970-01-01
    相关资源
    最近更新 更多