Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/346.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JDBC事务错误处理_Java_Jdbc_Transactions - Fatal编程技术网

Java JDBC事务错误处理

Java JDBC事务错误处理,java,jdbc,transactions,Java,Jdbc,Transactions,建议使用以下代码 public void updateCoffeeSales(HashMap<String, Integer> salesForWeek) throws SQLException { PreparedStatement updateSales = null; PreparedStatement updateTotal = null; String updateString = "update " + dbName +

建议使用以下代码

public void updateCoffeeSales(HashMap<String, Integer> salesForWeek)
    throws SQLException {

    PreparedStatement updateSales = null;
    PreparedStatement updateTotal = null;

    String updateString =
        "update " + dbName + ".COFFEES " +
        "set SALES = ? where COF_NAME = ?";

    String updateStatement =
        "update " + dbName + ".COFFEES " +
        "set TOTAL = TOTAL + ? " +
        "where COF_NAME = ?";

    try {
        con.setAutoCommit(false);
        updateSales = con.prepareStatement(updateString);
        updateTotal = con.prepareStatement(updateStatement);

        for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
            updateSales.setInt(1, e.getValue().intValue());
            updateSales.setString(2, e.getKey());
            updateSales.executeUpdate();
            updateTotal.setInt(1, e.getValue().intValue());
            updateTotal.setString(2, e.getKey());
            updateTotal.executeUpdate();
            con.commit();
        }
    } catch (SQLException e ) {
        JDBCTutorialUtilities.printSQLException(e);
        if (con != null) {
            try {
                System.err.print("Transaction is being rolled back");
                con.rollback();
            } catch(SQLException excep) {
                JDBCTutorialUtilities.printSQLException(excep);
            }
        }
    } finally {
        if (updateSales != null) {
            updateSales.close();
        }
        if (updateTotal != null) {
            updateTotal.close();
        }
        con.setAutoCommit(true);
    }
}
public void updateCoffeeSales(HashMap salesForWeek)
抛出SQLException{
PreparedStatement updateSales=null;
PreparedStatement updateTotal=null;
字符串更新字符串=
更新“+dbName+”.coffes”+
“设置销售额=?其中COF_NAME=?”;
字符串更新语句=
更新“+dbName+”.coffes”+
“设置总计=总计+?”+
“其中COF_NAME=?”;
试一试{
con.setAutoCommit(假);
updateSales=con.prepareStatement(updateString);
updateTotal=con.prepareStatement(updateStatement);
对于(Map.Entry e:salesForWeek.entrySet()){
updateSales.setInt(1,e.getValue().intValue());
setString(2,例如getKey());
updateSales.executeUpdate();
updateTotal.setInt(1,e.getValue().intValue());
setString(2,例如getKey());
updateTotal.executeUpdate();
con.commit();
}
}捕获(SQLE异常){
jdbcttutorialutilities.printSQLException(e);
如果(con!=null){
试一试{
系统错误打印(“事务正在回滚”);
con.rollback();
}捕获(SQLException例外){
JDBCTutorialUtilities.printSQLException(excep);
}
}
}最后{
if(updateSales!=null){
updateSales.close();
}
if(updateTotal!=null){
updateTotal.close();
}
con.setAutoCommit(真);
}
}
然而,错误处理对我来说似乎是错误的

如果try块中存在NullPointerException,则不会捕获它。相反,执行将直接进入finally块,在那里它将调用
con.setAutoCommit(true)
,这将提交任何正在进行的事务。似乎这显然不是预期的行为,因为它提交了一个不完整的事务

我认为这可能只是示例中的一个bug,但是除了SqlException()之外,还忘了捕获异常


我是否误解了正在发生的事情?

在调用
con.setAutoCommit(true)
之前,该示例将关闭可能未完成的准备语句。因此,当切换回自动提交模式时,这些语句将不会执行。没有未完成的事务将在那里提交

我检查了您链接的第一个教程,它犯了一个错误,没有使用finally块,没有取消未完成的语句,也没有恢复自动提交模式。那看起来真的很粗心


总的来说,我建议坚持使用官方教程,或者使用您真正信任的资源。

我倾向于认为您是对的;我的解决方案是不久前编写以下类,当出现意外异常时,该类将正确回滚。作为奖励,您可以将事务打包到一个try-with-resources块中,我发现这个块更可读,也更易于使用

你这样使用它:

try(Transaction trans = Transaction.create(conn)) {
  // execute multiple queries, use trans.getConn()
  trans.commit();
} catch (SQLException e) {
  // Handle exception, transaction is safely rolled-back and closed
}
// No need for a finally block, or to catch RuntimeException.
下面是完整的课程:

import static com.google.common.base.Preconditions.checkState;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * A Transaction can be used in a try-with-resources block to ensure a set of queries are
 * executed as a group.
 * 
 * try(Transaction trans = Transaction.create(conn)) {
 *   // execute multiple queries, use trans.getConn()
 *   trans.commit();
 * } catch (SQLException e) {
 *   // Handle exception, transaction is safely rolled-back and closed
 * }
 */
public final class Transaction implements AutoCloseable {
    private Connection conn;
    private boolean committed = false;
    private boolean rolledback = false;

    /**
     * Create a Transaction on the current connection, use to create
     * a try-with-resources block.
     * 
     * Note that if a transaction is started while another transaction is
     * ongoing (i.e. conn.getAutoCommit() == true) the earlier transaction
     * is committed. 
     */
    public static Transaction start(Connection conn) throws SQLException {
        return new Transaction(conn);
    }

    private Transaction(Connection conn) throws SQLException {
        this.conn = conn;
        // this is a no-op if we're not in a transaction, it commits the previous transaction if we are
        this.conn.setAutoCommit(true);
        this.conn.setAutoCommit(false);
    }

    /**
      * Call once all queries in the transaction have been executed,
      * to indicate transaction is complete and ready to be committed.
      * Should generally be the last line in the try block. 
     */
    public void commit() throws SQLException {
        if(committed) {
            throw new SQLException("Cannot commmit a transaction more than once");
        }
        if(rolledback) {
            throw new SQLException("Cannot commit a previously rolled-back transaction");
        }
        committed = true;
        getConn().commit();
    }

    /**
     * Call explicitly to cancel the transaction, called implicitly
     * if commit() is not called by the time the Transaction should
     * be closed.
     */
    public void rollback() throws SQLException {
        if(rolledback) {
            throw new SQLException("Cannot rollback a transaction more than once");
        }
        if(committed) {
            throw new SQLException("Cannot rollback a previously committed transaction");
        }
        rolledback = true;
        getConn().rollback();
    }

    /**
     * Should not be called directly, called in the try-with-resources
     * finally block to close the transaction.
     */
    @Override
    public void close() throws SQLException {
        try {
            if(!committed && !rolledback) {
                conn.rollback();
                throw new SQLException("Should explicitly rollback or commit transaction, rolling-back");
            }
        } finally {
            conn.setAutoCommit(true);
            conn = null;
        }
    }

    /**
     * Returns the Connection being used for this transaction.  You are encouraged
     * to use this method to access the transactional connection while inside the
     * transaction's try-with-resources block.
     */
    public Connection getConn() {
        checkState(conn != null, "Connection has already been closed");
        return conn;
    }
}

我还没有公开这个类所属的项目的源代码,但是如果有人需要的话,我很乐意在麻省理工学院的许可下明确发布它。

这似乎与这个问题中给出的答案不一致:如果错误发生在以下行:
updateSales.executeUpdate()
然后Jeremy is wright:finally块将提交由语句完成的更新(尽管给定了代码,但如果第一个语句没有,第二个语句将抛出NPE的可能性很小),在本例中,这可能不太可能,但是,对于某些人来说,在类似的代码中引入
运行时异常
是完全可能的。实际上,我认为您可能是对的(至少,我有一些代码,在恢复自动提交之前显式回滚事务)。你能总结出这次失败的原因吗?我也认为你是对的。捕获
Exception
比捕获
SQLException
不一致更好,盲目捕获
Exception
也会有同样的问题。显式捕获运行时异常、回滚和重试。