📄 ch03.html
字号:
<H4 CLASS="A"><A NAME="pgfId-1087505"></A><A NAME="63595"></A>Managing Transactions</H4><P CLASS="Body"><A NAME="pgfId-1087506"></A>Wouldn't it be great if every operation your application attempts succeeds? Unfortunately, in the multithreaded world of distributed applications and shared resources, this is not always possible. Why? First of all, shared resources must maintain a consistent view of the data to all users. This means reads and writes have to be managed so users do not overwrite each other's changes, or transaction errors do not corrupt data integrity. Also, if you factor in intermittent network delays or dropped connections, the potential for operations to fail in a Web-based application increases as the number of users increases. </P><P CLASS="Body"><A NAME="pgfId-1087507"></A>If operation failures are unavoidable, the next best thing is to recover safely, and that is where transaction management fits in. Modern databases and transaction managers let you undo and restore the state of a failed sequence of operations to ensure the data is consistent for access by multiple threads. </P><P CLASS="Body"><A NAME="pgfId-1087508"></A>This section adds code to the container-managed <EM CLASS="CODE">SellerBean</EM> so it can manage its auction item insertion transaction beyond the default transaction management provided by its container. </P><DIV><H5 CLASS="B"><A NAME="pgfId-1087509"></A>Why Manage Transactions?</H5><P CLASS="Body"><A NAME="pgfId-1087514"></A>When you access databases using the <A NAME="marker-1087510"></A><A NAME="marker-1087511"></A>JDBC API, all operations are run with an explicit <A NAME="marker-1087512"></A><A NAME="marker-1087513"></A>auto commit by default. This means any other application viewing this data will see the updated data after each JDBC call. For simple applications this may be acceptable, but consider the auction application and the sequences that occur when <EM CLASS="CODE">SellerBean</EM> inserts an auction item. The user's account is first charged for listing the item, and the item is then added to the list of items up for auction. These operations require <EM CLASS="CODE">RegistrationBean</EM> to debit the account and <EM CLASS="CODE">AuctionItemBean</EM> to add the item to the auction list. </P><P CLASS="Body"><A NAME="pgfId-1087518"></A>In auto-commit mode, if the auction item insertion fails, only the listing is <A NAME="marker-1087515"></A><A NAME="marker-1087516"></A><A NAME="marker-1087517"></A>backed out, and you have to manually adjust the user's account to refund the listing charge. In the meantime, another thread might try to deduct from the same user's account, find no credit left, and abort when perhaps a few milliseconds later it would have completed. There are two ways to ensure the debit is backed out if the auction item insertion fails: </P><UL><LI CLASS="BL"><A NAME="pgfId-1087519"></A>Add session synchronization code to a container-managed session bean to gain control over transaction commits and roll backs. </LI><LI CLASS="BL"><A NAME="pgfId-1087520"></A>Configure JDBC services to transaction commit mode and add code to start, stop, commit, and roll back the transaction. This is a bean-managed transaction and can be used with an entity or session bean.</LI></UL></DIV><DIV><H5 CLASS="B"><A NAME="pgfId-1087521"></A>Session Synchronization</H5><P CLASS="Body"><A NAME="pgfId-1087524"></A>A <A NAME="marker-1087522"></A><A NAME="marker-1087523"></A>container-managed session bean can optionally include session synchronization code to manage the default auto commit provided by the container. Session synchronization code lets the container notify the bean when important points in the transaction are reached. Upon receiving the notification, the bean can take any needed actions before the transaction proceeds to the next point. </P><UL><P CLASS="NOTE"><A NAME="pgfId-1087525"></A>NOTE A session bean using bean-managed transactions does not need session synchronization because the bean is in full control of the commit. </P></UL></DIV><DIV><H5 CLASS="B"><A NAME="pgfId-1087526"></A>Container-Managed Example</H5><P CLASS="Body"><A NAME="pgfId-1087528"></A><EM CLASS="CODE">SellerBean</EM> is a session bean that uses <EM CLASS="CODE">RegistrationBean</EM> and <EM CLASS="CODE">AuctionItemBean</EM> in the following ways:</P><UL><LI CLASS="BL"><A NAME="pgfId-1087529"></A><EM CLASS="CODE">RegistrationBean</EM> checks the user ID and password when someone posts an auction item, and debits the seller's account for a listing.</LI><LI CLASS="BL"><A NAME="pgfId-1087530"></A><EM CLASS="CODE">AuctionItemBean</EM> adds new auction items to the database. </LI></UL><P CLASS="Body"><A NAME="pgfId-1087532"></A>The transaction begins in the <EM CLASS="CODE">SellerBean.insertItem</EM><A NAME="marker-1087531"></A> method with the account debit and ends when the entire transaction either commits or rolls back. The entire transaction, including the 50-cents debit, rolls back if the auction item is null (the insertion failed), or if an exception is caught. <A NAME="marker-1087533"></A>If the auction item is not null and the insertion succeeds, the entire transaction including the 50-cents debit commits. </P></DIV><DIV><H5 CLASS="B"><A NAME="pgfId-1087534"></A>Session Synchronization Code</H5><P CLASS="Body"><A NAME="pgfId-1087536"></A>To use session synchronization, a session bean implements the <EM CLASS="CODE">SessionSynchronzation</EM><A NAME="marker-1087535"></A> interface and its three methods, <EM CLASS="CODE">afterBegin</EM>, <EM CLASS="CODE">beforeCompletion</EM>, and <EM CLASS="CODE">afterCompletion</EM>. This example adapts the <EM CLASS="CODE">SellerBean</EM> (Session Synchronization) code to use session synchronization. </P><PRE CLASS="CODE"><A NAME="pgfId-1087537"></A>public class SellerBean implements SessionBean, SessionSynchronization { private transient SessionContext ctx; private transient Properties p = new Properties(); private transient boolean success = true; public void afterBegin() {} public void beforeCompletion() { if (!success ) { ctx.setRollbackOnly(); } } public void afterCompletion(boolean state) {}</PRE><P CLASS="Body"><A NAME="pgfId-1087540"></A><EM CLASS="C-Code">afterBegin</EM>. <A NAME="marker-1087538"></A><EM CLASS="CODE"></EM><A NAME="marker-1087539"></A>The container calls the <EM CLASS="CODE">afterBegin</EM> method before the debit to notify the session bean that a new <A NAME="marker-1087541"></A><A NAME="marker-1087542"></A>transaction is about to begin. You can implement this method to do any preliminary database work that might be needed for the transaction. In this example, no preliminary database work is needed, so this method has no implementation. </P><P CLASS="Body"><A NAME="pgfId-1087544"></A><EM CLASS="C-Code">beforeCompletion</EM>. <EM CLASS="CODE"></EM><A NAME="marker-1087543"></A>The container calls the <EM CLASS="CODE">beforeCompletion</EM> method when it is ready to write the auction item <A NAME="marker-1087545"></A><A NAME="marker-1087546"></A>and debit to the database, but before it actually does so (commits). You can implement this method to write out any cached database updates or roll back the transaction. In this example, the method calls the <EM CLASS="CODE">setRollbackOnly</EM> method on its session context in the event the success variable is set to <EM CLASS="CODE">false</EM> during the transaction. </P><P CLASS="Body"><A NAME="pgfId-1087548"></A><EM CLASS="C-Code">afterCompletion</EM>. <EM CLASS="CODE"></EM><A NAME="marker-1087547"></A>The container calls the <EM CLASS="CODE">afterCompletion</EM> method when the <A NAME="marker-1087549"></A><A NAME="marker-1087550"></A>transaction commits. A Boolean value of <EM CLASS="CODE">true</EM> means the data committed and <EM CLASS="CODE">false</EM> means the transaction rolled back. The method uses the Boolean value to determine if it needs to reset the bean's state in the case of a rollback. In this example, there is no need to reset the state in the event of a failure. The <EM CLASS="CODE">insertItem</EM> method shown here has comments to indicate where <EM CLASS="CODE">SessionSynchronization</EM> methods are called. </P><PRE CLASS="CODE"><A NAME="pgfId-1087551"></A>public int insertItem(String seller, String password, String description, int auctiondays, double startprice, String summary) throws RemoteException { try { Context jndiCtx = new InitialContext(p); RegistrationHome rhome = (RegistrationHome) sCtx.lookup("registration"); RegistrationPK rpk=new RegistrationPK(); rpk.theuser=seller; Registration newseller=rhome.findByPrimaryKey(rpk); if((newseller == null) || (!newseller.verifyPassword(password))) { return(Auction.INVALID_USER); }//Call to afterBegin newseller.adjustAccount(-0.50); AuctionItemHome home = (AuctionItemHome) jndiCtx.lookup("auctionitems"); AuctionItem ai= home.create(seller, description, auctiondays, startprice, summary); if(ai == null) { success=false; return Auction.INVALID_ITEM; } else { return(ai.getId()); } } catch(Exception e){ System.out.println("insert problem="+e); success=false; return Auction.INVALID_ITEM; }//Call to beforeCompletion//Call to afterCompletion }</PRE></DIV><DIV><H5 CLASS="B"><A NAME="pgfId-1087552"></A>Transaction Commit Mode</H5><P CLASS="Body"><A NAME="pgfId-1087557"></A><A NAME="marker-1087553"></A><A NAME="marker-1087554"></A>If you configure the JDBC services to <A NAME="marker-1087555"></A><A NAME="marker-1087556"></A>transaction commit mode, you can have the bean manage the transaction. To set the JDBC services to commit, call <EM CLASS="CODE">con.setAutoCommit(false)</EM> on your JDBC connection. Not all <A NAME="marker-1087558"></A><A NAME="marker-1087559"></A>JDBC drivers support commit mode, but to have the bean control and manage transactions, you need a JDBC driver that does. </P><P CLASS="Body"><A NAME="pgfId-1087560"></A>Transaction commit mode lets you add code that creates a safety net around a sequence of dependent operations. The Java Transaction API (JTA) provides the hooks you need to create that safety net. But, if you are using the Enterprise JavaBeans architecture, you can do it with a lot less code. You only have to configure the Enterprise JavaBeans server, and specify where the transaction starts, stops, rolls back, and commits in your code. </P><P CLASS="Body"><A NAME="pgfId-1087562"></A><EM CLASS="Bold">Server Configuration. </EM><A NAME="marker-1087561"></A>Configuring the Enterprise JavaBeans server involves specifying the following settings in a configuration file for each bean: </P><UL><LI CLASS="BL"><A NAME="pgfId-1087565"></A>An <A NAME="marker-1087563"></A><A NAME="marker-1087564"></A>isolation level to specify how exclusive a transaction's access to shared data is</LI><LI CLASS="BL"><A NAME="pgfId-1087567"></A>A <A NAME="marker-1087566"></A>transaction attribute to specify how to handle bean-managed or container-managed transactions that continue in another bean</LI><LI CLASS="BL"><A NAME="pgfId-1087569"></A>A <A NAME="marker-1087568"></A>transaction type to specify whether the transaction is managed by the container or the bean.</LI></UL><P CLASS="Body"><A NAME="pgfId-1087572"></A><A NAME="marker-1087570"></A><A NAME="marker-1087571"></A>For example, you would specify these settings for the <EM CLASS="A">BEA Weblogic</EM> server in a <EM CLASS="CODE">DeploymentDescriptor.txt</EM> file for each bean. Here is the part of the <EM CLASS="CODE">DeploymentDescriptor.txt</EM> file for <EM CLASS="CODE">SellerBean</EM> that specifies the isolation level and transaction attribute. A description of the settings follows. </P><PRE CLASS="CODE"><A NAME="pgfId-1087573"></A>(controlDescriptors (DEFAULT isolationLevel TRANSACTION_SERIALIZABLE transactionAttribute REQUIRED runAsMode CLIENT_IDENTITY runAsIdentity guest ); end DEFAULT</PRE><PRE CLASS="CODE"><A NAME="pgfId-1087574"></A>); end controlDescriptors</PRE><P CLASS="Body"><A NAME="pgfId-1087577"></A><A NAME="marker-1087575"></A><A NAME="marker-1087576"></A>Here is the equivalent Enterprise JavaBeans 1.1 extensible markup language (XML) description that specifies the transaction type. In this example, <EM CLASS="CODE">SellerBean</EM> is container-managed. </P><PRE CLASS="CODE"><A NAME="pgfId-1087578"></A><container-transaction> <method> <ejb-name>SellerBean</ejb-name> <method-name>*</method-name> </method> <transaction-type>Container</transaction-type> <trans-attribute>Required</trans-attribute> </container-transaction></PRE><P CLASS="Body"><A NAME="pgfId-1087579"></A>In this example, <EM CLASS="CODE">SellerBean</EM> is bean-managed. </P><PRE CLASS="CODE"><A NAME="pgfId-1087580"></A><container-transaction> <method> <ejb-name>SellerBean</ejb-name> <method-name>*</method-name> </method> <transaction-type>Bean</transaction-type> <trans-attribute>Required</trans-attribute> </container-transaction></PRE><P CLASS="Body"><A NAME="pgfId-1087582"></A><EM CLASS="Bold">Transaction-Attribute Descriptions. </EM><A NAME="marker-1087581"></A>An enterprise bean uses a <EM CLASS="EM">transaction attribute</EM> to specify whether a bean's transactions are managed by the bean itself or by the container, and how to handle transactions that started in another bean. </P><P CLASS="Body"><A NAME="pgfId-1087583"></A>The Enterprise JavaBeans server can control only one transaction at a time. This model follows the example set by the <A NAME="marker-1087584"></A>OMG Object Transaction Service (OTS), which means that the current Enterprise JavaBeans specification does not provide a way to nest transactions. A nested transaction is a new transaction that starts from within an existing transaction. While transaction nesting is not allowed, continuing an existing transaction in another bean is allowed. </P><P CLASS="Body"><A NAME="pgfId-1087587"></A>When a bean is entered, the server creates a <A NAME="marker-1087585"></A><A NAME="marker-1087586"></A>transaction context to manage the transaction. When the transaction is managed by the bean, you access the context to begin, commit, and roll back the transaction as needed. </P><P CLASS="Body"><A NAME="pgfId-1087588"></A>The following table lists the transaction attributes with a brief description for each one. Note that the attribute names changed between the 1.0 and 1.1 versions of the Enterprise JavaBeans specification. <EM CLASS="A"></EM><A NAME="level"></A></P><TABLE><TR><TH ROWSPAN="1" COLSPAN="1"><P CLASS="TCH"><A NAME="pgfId-1087591"></A>1.1 Specification</P></TH><TH ROWSPAN="1" COLSPAN="1"><P CLASS="TCH"><A NAME="pgfId-1087593"></A>1.0 Specification</P></TH></TR><TR><TD ROWSPAN="1" COLSPAN="1"><P CLASS="TB1"><A NAME="pgfId-1087596"></A><EM CLASS="CODE">REQUIRED</EM><A NAME="marker-1087595"></A></P></TD><TD ROWSPAN="1" COLSPAN="1"><P CLASS="TB1"><A NAME="pgfId-1087599"></A><EM CLASS="CODE">TX_REQUIRED</EM><A NAME="marker-1087598"></A> </P></TD></TR><TR><TD ROWSPAN="1" COLSPAN="2"><P CLASS="TB"><A NAME="pgfId-1087601"></A>Container-managed transaction. The server either starts and manages a new transaction on behalf of the user or continues using the transaction that was started by the code that called this bean.</P></TD></TR><TR><TD ROWSPAN="1" COLSPAN="1"><P CLASS="TB"><A NAME="pgfId-1087606"></A><EM CLASS="CODE">REQUIRESNEW</EM><A NAME="marker-1087605"></A></P></TD>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -