在JSP中使用Singleton,如何正确整理关闭?

时间:2013-10-16 18:54:40

标签: java jsp destructor

我刚刚开始使用jsp,我的问题就是这个 - 当我有一个单身课程时,我该怎么整理它?

特别是:

public class DBConnection {
  private static Connection connection = null;
  private static Statement statement = null;

  public static ResultSet executeQuery(String query){
    if (connection == null) { /*initConnection*/ }
    if (statement == null) { /*initStatement*/ }

    // do some stuff
  }
}

现在,我在几个页面中使用此类来从jdbc获取结果。但是,我最终需要拨打statement.close();connection.close(); - 我应该何时拨打这些电话?

我正在使用单例,因为每当我需要进行查询时,一遍又一遍地调用连接数据库是错误的。

3 个答案:

答案 0 :(得分:1)

Connection必须关闭始终,并且在执行了所需的操作的所有数据库语句之后。两个例子:

案例1:您必须向用户显示按数据库条件筛选的产品列表。解决方案:获取连接,使用筛选条件检索产品列表,关闭连接。

案例2:客户选择其中一些产品并更新最低库存以获得警报并重新进货。解决方案:获取连接,更新所有产品,关闭连接。

基于这些案例,我们可以学到很多东西:

  • 在打开/维持单个连接时,您可以执行多个语句。
  • 连接应仅存在于使用它的块中。它不应该在之前或之后生活。
  • 这两种情况都可以同时发生,因为它们处于多线程环境中。因此,单个数据库连接不能可供两个线程同时使用,以避免出现结果问题。例如,用户A搜索Foo类别中的产品,用户B搜索类别栏中的产品,您不希望将类别栏中的产品显示给用户A.
  • 从最后一句开始,每个数据库操作(或类似操作的组,如案例2)应该在原子操作中处理。为了确保这一点,连接不能存储在单个对象中,而不是只在所使用的方法中存在。

结果:

  • 不要ConnectionStatementResultSet以及其他JDBC资源声明为static。它只会失败。相反,只声明Connection类的DBConnection字段。让每个方法决定处理每个Statement(或PreparedStatement)和ResultSet以及特定的JDBC资源。
  • 由于必须在使用后关闭连接,然后再添加两个方法:void open()void close()。这些方法将处理数据库连接检索和关闭该连接。
  • 另外,由于DBConnection看起来像Connection类和数据库连接操作的包装类,我建议至少再使用三种方法:void setAutoCommit(boolean autoCommit),{{1} }和void commit()。这些方法将分别是Connection#setAutoCommit Connection#closeConnection#rollback的简单包装。

然后你可以这样使用这个类:

void rollback()

请注意,在此示例中,public List<Product> getProducts(String categoryName) { String sql = "SELECT id, name FROM Product WHERE categoryName = ?"; List<Product> productList = new ArrayList<Product>(); DBConnection dbConnection = new DBConnection(); try { dbConnection.open(); ResultSet resultSet = dbConnection.executeSelect(sql, categoryName); //execute select and apply parameters //fill productList... } catch (Exception e) { //always handle your exceptions ... } finally { //don't forget to also close other resources here like ResultSet... //always close the connection dbConnection.close(); } } 不在PreparedStatement方法中,它将是getProducts方法的局部变量。

附加说明:

  • 在应用服务器中工作时,不应天真地打开连接,例如使用executeSelect,而不是使用数据库连接池。您可以在这里解释一些数据库连接池库,如C3P0:How to establish a connection pool in JDBC?。或者在应用程序服务器中配置一个,我在这里解释:Is it a good idea to put jdbc connection code in servlet class?
  • 如果这是出于学习目的,那么请使用您自己的类来处理与数据库的通信。在实际应用中,不建议这样做(并不意味着你不应该这样做)。相反,使用像ORM这样的数据库连接框架,例如JPA(Java官方ORM框架)或Hibernate;没有处理数据库通信的ORM框架,如Spring JDBCMyBatis。选择权归你所有。

更多信息:

答案 1 :(得分:1)

在mywebapp / META-INF / context.xml文件中定义连接资源

<Resource name="jdbc/mydb" auth="Container" type="javax.sql.DataSource" 
    maxActive="10" maxIdle="2" maxWait="20000" 
    driverClassName="com.mysql.jdbc.Driver" 
    username="myuser" password="mypwd" 
    url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&amp;characterEncoding=utf8"
    validationQuery="SELECT 1" />

创建DB.java帮助程序类以最小化应用程序其他部分中的代码

import java.sql.*;
import javax.sql.DataSource;
import javax.naming.Context;
import javax.naming.InitialContext;

public class DB {

public static Connection createConnection() throws SQLException {
    try {
        Context ctx = new InitialContext();
        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mydb");
        return ds.getConnection();
    } catch (SQLException ex) {
        throw ex;
    } catch (Exception ex) {
        SQLException sqex = new SQLException(ex.getMessage());
        sqex.initCause(ex);
        throw sqex;
    }
}

public static void close(ResultSet rs, Statement stmt, Connection conn) {
       if (rs != null) try { rs.close(); } catch (Exception e) { }
       if (stmt != null) try { stmt.close(); } catch (Exception e) { }
       if (conn != null) try { conn.close(); } catch (Exception e) { }
}

public static void close(ResultSet rs, boolean closeStmtAndConn) {
       if (rs==null) return;
       try {
          Statement stmt = rs.getStatement();
          close(rs, stmt, stmt!=null ? stmt.getConnection() : null);
       } catch (Exception ex) { }
}

}

在您的应用程序DAO代码中的某处使用数据库帮助程序。

public List<MyBean> getBeans() throws SQLException {
    List<MyBean> list = new ArrayList<MyBean>();
    ResultSet rs=null;
    try {
        Connection con = DB.createConnection();
        String sql = "Select * from beantable where typeid=?";
        PreparedStatement stmt = con.prepareStatement(sql, Statement.NO_GENERATED_KEYS);
        stmt.setInt(1, 101);
        rs = stmt.executeQuery();
        while(rs.next()
            list.add( createBean(rs) );
    } finally {
        DB.close(rs, true); // or DB.close(rs, stmt, conn);
    }
    return list;
}

private MyBean createBean(ResultSet rs) throws SQLException {
    MyBean bean = new MyBean();
    bean.setId( rs.getLong("id") );
    bean.setName( rs.getString("name" );
    bean.setTypeId( rs.getInt("typeid") );
    return bean;
}

答案 2 :(得分:0)

我会在课程中添加两个方法:

public static void open() throws SomeException;
public static void close() throws SomeException;

然后你的调用代码看起来像这样{

try {
    DBConnection.open();
    ... code to use the connection one or more times ...
} finally {
    DBConnection.close();
}

将所有数据库调用包含在其中,它将负责关闭是否抛出异常。

当然,这与普通课程没什么不同,我可能会建议:

try {
    DBConnection conn = new DBConnection();
    conn.open();

    ... all the code to use the database (but you pass 'conn' around) ...

} finally {
    conn.close();
}

您可能需要查看java.lang.AutoCloseablejava.io.Closeable,看看是否对您有帮助。

<强> 2

如果要在页面加载中保持打开状态,则没有任何地方可以放置try ... finally内容,以便在servlet关闭或服务器关闭或类似的情况下打开它并关闭它。 / p>

如果您打算将其打开,则需要确保并添加代码以确认在您不看时它不会关闭。例如,短暂的网络故障可能会将其关闭。在这种情况下,您需要在关闭时重新打开它。否则,从该点开始的所有数据库访问都将失败。

您可能希望研究数据库池的概念。 Apache有一个 - DBCP。 Tomcat有自己的特色。其他容器,如JBOSS,WebSphere,WebLogic都有它们。有一对可以与Spring Framework一起使用。它的作用是管理一个或多个数据库连接。你的代码要求一个并且它返回一个打开的代码,除非没有可用的,然后它打开一个并返回它。当您的代码通过它时调用close但它并没有真正关闭连接,它只是将它返回到池中。

您通常可以配置池以检查关闭连接,并在需要时重新打开。