1

我刚刚开始使用 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();我应该什么时候打电话?

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

4

3 回答 3

1

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

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

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

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

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

结果:

  • 不要Connection将norStatementResultSetnor 其他 JDBC 资源声明为static. 它只会失败。相反,只声明你的类的Connectionas 字段。DBConnection让每个方法决定处理每个Statement(或PreparedStatementResultSet和特定的 JDBC 资源。
  • 由于必须在使用后关闭连接,因此再添加两个方法:void open()void close(). 这些方法将处理数据库连接检索和关闭该连接。
  • 另外,由于DBConnection看起来像Connection类和数据库连接操作的包装类,我建议至少再有三个方法void setAutoCommit(boolean autoCommit)void commit()void rollback(). 这些方法将分别是和的普通包装器。Connection#setAutoCommit Connection#closeConnection#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

补充笔记:

  • 在应用程序服务器中工作时,您不应天真地打开连接,例如使用Class.forName("..."),而应使用数据库连接池。您可以使用一些数据库连接池库,如 C3P0,如下所述:如何在 JDBC 中建立连接池?. 或者在您的应用程序服务器中配置一个,正如我在这里解释的那样:将 jdbc 连接代码放在 servlet 类中是个好主意吗?
  • 如果这是出于学习目的,请使用您自己的课程来处理与数据库的通信。在现实世界的应用程序中,不建议这样做(并不意味着您不应该这样做)。相反,使用数据库连接框架,如 ORM,例如JPA(Java 官方 ORM 框架)或Hibernate;没有像Spring JDBCMyBatis这样处理数据库通信的 ORM 框架。这是你的选择。

更多信息:

于 2013-10-16T19:45:38.917 回答
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 代码中的某处使用 DB 助手。

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;
}
于 2013-10-16T20:27:14.920 回答
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.AutoCloseable看看 java.io.Closeable这是否对你有帮助。

2

如果您在页面加载时保持打开状态,则没有任何地方可以放置这些try ... finally内容,因此您可以在 servlet 关闭或服务器关闭或类似情况时打开并关闭它。

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

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

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

于 2013-10-16T19:03:25.190 回答