[ Team LiB ] |
![]() ![]() |
Recipe 21.10 Executing Several SQL Statements Within a Single TransactionProblemYou want to execute more than one SQL statement within a single transaction. SolutionUse the java.sql.Connection API and the setAutoCommit( ), commit( ), and rollback( ) methods to create a transaction. DiscussionSome SQL statements, such as those that update customer information in two different database tables, are meant to be executed only as a group. If one of them does not succeed, the database is returned to its previous state. This is the purpose of using a transaction in your Java code. A transaction is a logical unit of database operations that can be "rolled back" or canceled as a group if something goes wrong with one of the operations. Once you have a database connection (an instance of java.sql.Connection), you can call various Connection methods to create a transaction. Here are the steps for executing a transaction:
Example 21-14 is a servlet that illustrates this process. Example 21-14. A servlet that uses a SQL transactionpackage com.jspservletcookbook; import java.sql.*; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.*; import javax.servlet.*; import javax.servlet.http.*; public class DbServletTrans extends HttpServlet { DataSource pool; /*Initialize the DataSource in the servlet's init( ) method which the servlet container calls once when it creates an instance of the servlet */ public void init( ) throws ServletException { Context env = null; try{ env = (Context) new InitialContext( ).lookup("java:comp/env"); pool = (DataSource) env.lookup("jdbc/oracle-8i-athletes"); if (pool == null) throw new ServletException( "'oracle-8i-athletes' is an unknown DataSource"); } catch (NamingException ne) { throw new ServletException(ne); } }//init public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException { Connection conn = null; Statement stmt = null; response.setContentType("text/html"); java.io.PrintWriter out = response.getWriter( ); out.println( "<html><head><title>Using transactions</title></head><body>"); out.println( "<h2>These SQL statements are part of a transaction</h2>"); out.println("CallableStatement.executeUpdate( )"); out.println("<br><br>"); out.println("Statement.executeUpdate( )"); out.println("<br><br>"); try{ //Get a connection from the pool conn = pool.getConnection( ); //Display the default values for setAutoCommit( ) //and the isolation level out.println("AutoCommit before setAutoCommit( ): " + conn.getAutoCommit( ) + "<br><br>"); out.println("Transaction isolation level: "); //just out of curiosity, display the existing transaction // isolation level witch(conn.getTransactionIsolation( )){ case 0 : out.println("TRANSACTION_NONE<br><br>"); break; case 1 : out.println( "TRANSACTION_READ_UNCOMMITTED<br><br>"); break; case 2 : out.println( "TRANSACTION_READ_COMMITTED<br><br>"); break; case 4 : out.println( "TRANSACTION_REPEATABLE_READ<br><br>"); break; case 8 : out.println( "TRANSACTION_SERIALIZABLE<br><br>"); break; default: out.println("UNKNOWN<br><br>"); }//switch //set Autocommit to false so that individual SQL statements will //not be committed until Connection.commit( ) is called conn.setAutoCommit(false); //Transaction-related SQL begins... CallableStatement cs = null; //Create an instance of the CallableStatement cs = conn.prepareCall( "{call addEvent (?,?,?)}" ); cs.setString(1,"Salisbury Beach 5-Miler"); cs.setString(2,"Salisbury MA"); cs.setString(3,"14-Aug-2003"); //Call the inherited PreparedStatement.executeUpdate( ) method cs.executeUpdate( ); String sql = "update raceevent set racedate='13-Aug-2003' "+ "where name='Salisbury Beach 5-Miler'"; int res = 0; stmt = conn.createStatement( ); res = stmt.executeUpdate(sql); //commit the two SQL statements conn.commit( ); } catch (Exception e){ try{ //rollback the transaction in case of a problem conn.rollback( ); } catch (SQLException sqle){ } throw new ServletException(e.getMessage( )); } finally { try{ if (stmt != null) stmt.close( ); if (conn != null) conn.close( ); } catch (SQLException sqle){ } } out.println("</table></body></html>"); } //doGet } The doGet( ) method in Example 21-14 displays the default values for "auto committing" SQL statements and the transaction isolation level (the level of database-locking that occurs as the transactions within your Java code are initiated). For example, if your SQL statements include the updating of database fields, can other users of the database view the new column values before your transaction is committed? If allowed, this type of behavior is called a dirty read. Table 21-2 shows the different types of transaction isolation levels, from the least to most restrictive level. Two other terms need addressing before you inspect this table:
Example 21-14 runs two SQL statements within a transaction: it executes a stored procedure and initiates an UPDATE statement. Then the code calls commit( ) on the Connection object to commit any database changes to the underlying data store. If this SQL code throws an exception, the transaction is rolled back with a call to Connection's rollback( ) method. This method call prevents the prior SQL statements from having any effect on the underlying database. Figure 21-7 shows the output of the servlet in Example 21-14, as it would appear in a web browser. Figure 21-7. A servlet with a database transaction provides browser output![]() See AlsoThe JDBC specification: http://java.sun.com/products/jdbc/download.html; Recipe 21.1 on accessing a database from a servlet without a connection pool; Recipe 21.2 and Recipe 21.3 on using a DataSource on Tomcat; Recipe 21.4-Recipe 21.6 on using DataSources with servlets and JSPs on WebLogic; Recipe 21.7 and Recipe 21.8 on calling stored procedures from servlets and JSPs; Recipe 21.11 on using transactions in JSPs; Recipe 21.12 on finding out information about a ResultSet. ![]() |
[ Team LiB ] |
![]() ![]() |