1<?xml version="1.0" encoding="UTF-8" standalone="no"?> 2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 3<html xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 6 <title>DPL Transaction Example</title> 7 <link rel="stylesheet" href="gettingStarted.css" type="text/css" /> 8 <meta name="generator" content="DocBook XSL Stylesheets V1.62.4" /> 9 <link rel="home" href="index.html" title="Getting Started with Berkeley DB Transaction Processing" /> 10 <link rel="up" href="wrapup.html" title="Chapter��6.��Summary and Examples" /> 11 <link rel="previous" href="txnexample_java.html" title="Base API Transaction Example" /> 12 <link rel="next" href="inmem_txnexample_java.html" title="Base API In-Memory Transaction Example" /> 13 </head> 14 <body> 15 <div class="navheader"> 16 <table width="100%" summary="Navigation header"> 17 <tr> 18 <th colspan="3" align="center">DPL Transaction Example</th> 19 </tr> 20 <tr> 21 <td width="20%" align="left"><a accesskey="p" href="txnexample_java.html">Prev</a>��</td> 22 <th width="60%" align="center">Chapter��6.��Summary and Examples</th> 23 <td width="20%" align="right">��<a accesskey="n" href="inmem_txnexample_java.html">Next</a></td> 24 </tr> 25 </table> 26 <hr /> 27 </div> 28 <div class="sect1" lang="en" xml:lang="en"> 29 <div class="titlepage"> 30 <div> 31 <div> 32 <h2 class="title" style="clear: both"><a id="txnexample_dpl"></a>DPL Transaction Example</h2> 33 </div> 34 </div> 35 <div></div> 36 </div> 37 <p> 38 The following Java code provides a fully functional example of a 39 multi-threaded transactional DB application using the DPL. 40 This example is nearly identical to the example provided in the 41 previous section, except that it uses an entity class and entity 42 store to manage its data. 43 </p> 44 <p> 45 As is the case with the previous examples, this example opens 46 an environment and then an entity store. It then creates 47 5 threads, each of which writes 500 records to the database. 48 The primary key for these writes are based on pre-determined 49 integers, while the data is randomly generated data. 50 This means that the actual data is arbitrary and therefore uninteresting; 51 we picked it only because it requires minimum code to implement and therefore will 52 stay out of the way of the main points of this example. 53 </p> 54 <p> 55 Each thread writes 10 records under a single transaction 56 before committing and writing another 10 (this is repeated 50 57 times). At the end of each transaction, but before committing, each 58 thread calls a function that uses a cursor to read every record in 59 the database. We do this in order to make some points about 60 database reads in a transactional environment. 61 </p> 62 <p> 63 Of course, each writer thread performs deadlock detection as 64 described in this manual. In addition, normal recovery is performed 65 when the environment is opened. 66 </p> 67 <p> 68 To implement this example, we need three classes: 69 </p> 70 <div class="itemizedlist"> 71 <ul type="disc"> 72 <li> 73 <p> 74 <tt class="literal">TxnGuide.java</tt> 75 </p> 76 <p> 77 This is the main class for the application. It performs 78 environment and store management, spawns threads, and 79 creates the data that is placed in the database. See <a href="txnexample_dpl.html#txnguideexample_dpl">TxnGuide.java</a> for implementation details. 80 </p> 81 </li> 82 <li> 83 <p> 84 <tt class="literal">StoreWriter.java</tt> 85 </p> 86 <p> 87 This class extends <tt class="literal">java.lang.Thread</tt>, and 88 as such it is our thread implementation. It is responsible 89 for actually reading and writing store. It also 90 performs all of our transaction management. See <a href="txnexample_dpl.html#storewriter">StoreWriter.java</a> for 91 implementation details. 92 </p> 93 </li> 94 <li> 95 <p> 96 <tt class="literal">PayloadDataEntity.java</tt> 97 </p> 98 <p> 99 This is an entity class used to encapsulate several data 100 fields. See <a href="txnexample_dpl.html#payloaddataentity">PayloadDataEntity.java</a> for 101 implementation details. 102 </p> 103 </li> 104 </ul> 105 </div> 106 <div class="sect2" lang="en" xml:lang="en"> 107 <div class="titlepage"> 108 <div> 109 <div> 110 <h3 class="title"><a id="txnguideexample_dpl"></a>TxnGuide.java</h3> 111 </div> 112 </div> 113 <div></div> 114 </div> 115 <p> 116 The main class in our example application is used to open and 117 close our environment and store. It also spawns all the 118 threads that we need. We start with the normal series 119 of Java package and import statements, followed by our class 120 declaration: 121 </p> 122 <pre class="programlisting">// File TxnGuideDPL.java 123 124package persist.txn; 125 126import com.sleepycat.db.DatabaseConfig; 127import com.sleepycat.db.DatabaseException; 128import com.sleepycat.db.DatabaseType; 129import com.sleepycat.db.LockDetectMode; 130 131import com.sleepycat.db.Environment; 132import com.sleepycat.db.EnvironmentConfig; 133 134import com.sleepycat.persist.EntityStore; 135import com.sleepycat.persist.StoreConfig; 136 137import java.io.File; 138import java.io.FileNotFoundException; 139 140public class TxnGuideDPL { </pre> 141 <p> 142 Next we declare our class' private data members. Mostly these are used 143 for constants such as the name of the database that we are opening and 144 the number of threads that we are spawning. However, we also declare 145 our environment and database handles here. 146</p> 147 <pre class="programlisting"> private static String myEnvPath = "./"; 148 private static String storeName = "exampleStore"; 149 150 // Handles 151 private static EntityStore myStore = null; 152 private static Environment myEnv = null; 153 private static final int NUMTHREADS = 5; </pre> 154 <p> 155 Next, we implement our <tt class="function">usage()</tt> method. This 156 application optionally accepts a single command line argument which is 157 used to identify the environment home directory. 158</p> 159 <pre class="programlisting"> private static void usage() { 160 System.out.println("TxnGuideDPL [-h <env directory>]"); 161 System.exit(-1); 162 } </pre> 163 <p> 164 Now we implement our <tt class="function">main()</tt> method. This method 165 simply calls the methods to parse the command line arguments and open 166 the environment and store. It also creates and then joins the store writer 167 threads. 168</p> 169 <pre class="programlisting"> public static void main(String args[]) { 170 try { 171 // Parse the arguments list 172 parseArgs(args); 173 // Open the environment and store 174 openEnv(); 175 176 // Start the threads 177 StoreWriter[] threadArray; 178 threadArray = new StoreWriter[NUMTHREADS]; 179 for (int i = 0; i < NUMTHREADS; i++) { 180 threadArray[i] = new StoreWriter(myEnv, myStore); 181 threadArray[i].start(); 182 } 183 184 for (int i = 0; i < NUMTHREADS; i++) { 185 threadArray[i].join(); 186 } 187 } catch (Exception e) { 188 System.err.println("TxnGuideDPL: " + e.toString()); 189 e.printStackTrace(); 190 } finally { 191 closeEnv(); 192 } 193 System.out.println("All done."); 194 } </pre> 195 <p> 196 Next we implement <tt class="function">openEnv()</tt>. This method is used 197 to open the environment and then an entity store in that environment. Along 198 the way, we make sure that every handle is free-threaded, and that the 199 transactional subsystem is correctly initialized. Because this is a 200 concurrent application, we also declare how we want deadlock detection 201 to be performed. In this case, we use DB's internal block detector 202 to determine whether a deadlock has occurred when a thread attempts to 203 acquire a lock. We also indicate that we want the deadlocked thread 204 with the <span class="emphasis"><em>youngest</em></span> lock to receive deadlock 205 notification. 206</p> 207 <p> 208 Notice that we also cause normal recovery to be run when we open the 209 environment. This is the standard and recommended thing to do whenever 210 you start up a transactional application. 211</p> 212 <p> 213 Finally, notice that we open the database such that it supports 214 uncommitted reads. We do this so that some cursor activity later in 215 this example can read uncommitted data. If we did not do this, then 216 our <tt class="methodname">countObjects()</tt> method described later in 217 this example would cause our thread to self-deadlock. This is because 218 the cursor could not be opened to support uncommitted reads (that flag 219 on the cursor open would, in fact, be silently ignored). 220</p> 221 <pre class="programlisting"> private static void openEnv() throws DatabaseException { 222 System.out.println("opening env and store"); 223 224 // Set up the environment. 225 EnvironmentConfig myEnvConfig = new EnvironmentConfig(); 226 myEnvConfig.setAllowCreate(true); 227 myEnvConfig.setInitializeCache(true); 228 myEnvConfig.setInitializeLocking(true); 229 myEnvConfig.setInitializeLogging(true); 230 myEnvConfig.setRunRecovery(true); 231 myEnvConfig.setTransactional(true); 232 // EnvironmentConfig.setThreaded(true) is the default behavior 233 // in Java, so we do not have to do anything to cause the 234 // environment handle to be free-threaded. 235 236 // Indicate that we want db to internally perform deadlock 237 // detection. Also indicate that the transaction that has 238 // performed the least amount of write activity to 239 // receive the deadlock notification, if any. 240 myEnvConfig.setLockDetectMode(LockDetectMode.MINWRITE); 241 242 // Set up the entity store 243 StoreConfig myStoreConfig = new StoreConfig(); 244 myStoreConfig.setAllowCreate(true); 245 myStoreConfig.setTransactional(true); 246 247 // Need a DatabaseConfig object so as to set uncommitted read 248 // support. 249 DatabaseConfig myDbConfig = new DatabaseConfig(); 250 myDbConfig.setType(DatabaseType.BTREE); 251 myDbConfig.setAllowCreate(true); 252 myDbConfig.setTransactional(true); 253 myDbConfig.setReadUncommitted(true); 254 255 try { 256 // Open the environment 257 myEnv = new Environment(new File(myEnvPath), // Env home 258 myEnvConfig); 259 260 // Open the store 261 myStore = new EntityStore(myEnv, storeName, myStoreConfig); 262 263 // Set the DatabaseConfig object, so that the underlying 264 // database is configured for uncommitted reads. 265 myStore.setPrimaryConfig(PayloadDataEntity.class, myDbConfig); 266 } catch (FileNotFoundException fnfe) { 267 System.err.println("openEnv: " + fnfe.toString()); 268 System.exit(-1); 269 } 270 } </pre> 271 <p> 272 Finally, we implement the methods used to close our environment and 273 databases, parse the command line arguments, and provide our class 274 constructor. This is fairly standard code and it is mostly 275 uninteresting from the perspective of this manual. We include it here 276 purely for the purpose of completeness. 277</p> 278 <pre class="programlisting"> private static void closeEnv() { 279 System.out.println("Closing env and store"); 280 if (myStore != null ) { 281 try { 282 myStore.close(); 283 } catch (DatabaseException e) { 284 System.err.println("closeEnv: myStore: " + 285 e.toString()); 286 e.printStackTrace(); 287 } 288 } 289 290 if (myEnv != null ) { 291 try { 292 myEnv.close(); 293 } catch (DatabaseException e) { 294 System.err.println("closeEnv: " + e.toString()); 295 e.printStackTrace(); 296 } 297 } 298 } 299 300 private TxnGuideDPL() {} 301 302 private static void parseArgs(String args[]) { 303 int nArgs = args.length; 304 for(int i = 0; i < args.length; ++i) { 305 if (args[i].startsWith("-")) { 306 switch(args[i].charAt(1)) { 307 case 'h': 308 if (i < nArgs - 1) { 309 myEnvPath = new String(args[++i]); 310 } 311 break; 312 default: 313 usage(); 314 } 315 } 316 } 317 } 318} </pre> 319 </div> 320 <div class="sect2" lang="en" xml:lang="en"> 321 <div class="titlepage"> 322 <div> 323 <div> 324 <h3 class="title"><a id="payloaddataentity"></a>PayloadDataEntity.java</h3> 325 </div> 326 </div> 327 <div></div> 328 </div> 329 <p> 330 Before we show the implementation of the store writer thread, we 331 need to show the class that we will be placing into the store. This 332 class is fairly minimal. It simply allows you to store and retrieve an 333 <tt class="literal">int</tt>, a <tt class="literal">String</tt>, and a 334 <tt class="literal">double</tt>. The <tt class="literal">int</tt> is our primary key. 335</p> 336 <pre class="programlisting">package persist.txn; 337import com.sleepycat.persist.model.Entity; 338import com.sleepycat.persist.model.PrimaryKey; 339import static com.sleepycat.persist.model.Relationship.*; 340 341@Entity 342public class PayloadDataEntity { 343 @PrimaryKey 344 private int oID; 345 346 private String threadName; 347 348 private double doubleData; 349 350 PayloadDataEntity() {} 351 352 public double getDoubleData() { return doubleData; } 353 public int getID() { return oID; } 354 public String getThreadName() { return threadName; } 355 356 public void setDoubleData(double dd) { doubleData = dd; } 357 public void setID(int id) { oID = id; } 358 public void setThreadName(String tn) { threadName = tn; } 359} </pre> 360 </div> 361 <div class="sect2" lang="en" xml:lang="en"> 362 <div class="titlepage"> 363 <div> 364 <div> 365 <h3 class="title"><a id="storewriter"></a>StoreWriter.java</h3> 366 </div> 367 </div> 368 <div></div> 369 </div> 370 <p> 371 <tt class="literal">StoreWriter.java</tt> provides the implementation 372 for our entity store writer thread. It is responsible for: 373 </p> 374 <div class="itemizedlist"> 375 <ul type="disc"> 376 <li> 377 <p> 378 All transaction management. 379 </p> 380 </li> 381 <li> 382 <p> 383 Responding to deadlock exceptions. 384 </p> 385 </li> 386 <li> 387 <p> 388 Providing data to be stored in the entity store. 389 </p> 390 </li> 391 <li> 392 <p> 393 Writing the data to the store. 394 </p> 395 </li> 396 </ul> 397 </div> 398 <p> 399 In order to show off some of the ACID properties provided 400 by DB's transactional support, 401 <tt class="literal">StoreWriter.java</tt> does some things in a less 402 efficient way than you would probably decide to use in a 403 true production application. First, it groups 10 database 404 writes together in a single transaction when you could just 405 as easily perform one write for each transaction. If you 406 did this, you could use auto commit for the individual 407 database writes, which means your code would be slightly 408 simpler and you would run a <span class="emphasis"><em>much</em></span> 409 smaller chance of encountering blocked and deadlocked 410 operations. However, by doing things this way, we are able 411 to show transactional atomicity, as well as deadlock 412 handling. 413 </p> 414 <p> 415 To begin, we provide the usual package and import statements, and we declare our class: 416 </p> 417 <pre class="programlisting">package persist.txn; 418 419import com.sleepycat.db.CursorConfig; 420import com.sleepycat.db.DatabaseException; 421import com.sleepycat.db.DeadlockException; 422import com.sleepycat.db.Environment; 423import com.sleepycat.db.Transaction; 424 425import com.sleepycat.persist.EntityCursor; 426import com.sleepycat.persist.EntityStore; 427import com.sleepycat.persist.PrimaryIndex; 428 429import java.util.Iterator; 430import java.util.Random; 431import java.io.UnsupportedEncodingException; 432 433public class StoreWriter extends Thread 434{ </pre> 435 <p> 436 Next we declare our private data members. Notice that we get handles 437 for the environment and the entity store. The random number generator 438 that we instantiate is used 439 to generate unique data for storage in the database. Finally, the 440 <tt class="literal">MAX_RETRY</tt> variable is used to define how many times 441 we will retry a transaction in the face of a deadlock. 442</p> 443 <pre class="programlisting"> private EntityStore myStore = null; 444 private Environment myEnv = null; 445 private PrimaryIndex<Integer,PayloadDataEntity> pdKey; 446 private Random generator = new Random(); 447 private boolean passTxn = false; 448 449 private static final int MAX_RETRY = 20; </pre> 450 <p> 451 Next we implement our class constructor. The most interesting thing about our constructor is 452 that we use it to obtain our entity class's primary index. 453 </p> 454 <pre class="programlisting"> // Constructor. Get our handles from here 455 StoreWriter(Environment env, EntityStore store) 456 457 throws DatabaseException { 458 myStore = store; 459 myEnv = env; 460 461 // Open the data accessor. This is used to store persistent 462 // objects. 463 pdKey = myStore.getPrimaryIndex(Integer.class, 464 PayloadDataEntity.class); 465 } </pre> 466 <p> 467 Now we implement our thread's <tt class="methodname">run()</tt> method. 468 This is the method that is run when <tt class="classname">StoreWriter</tt> 469 threads are started in the main program (see <a href="txnexample_dpl.html#txnguideexample_dpl">TxnGuide.java</a>). 470</p> 471 <pre class="programlisting"> // Thread method that writes a series of records 472 // to the database using transaction protection. 473 // Deadlock handling is demonstrated here. 474 public void run () { </pre> 475 <p> 476 The first thing we do is get a <tt class="literal">null</tt> transaction 477 handle before going into our main loop. We also begin the top transaction loop here that causes our application to 478 perform 50 transactions. 479</p> 480 <pre class="programlisting"> Transaction txn = null; 481 482 // Perform 50 transactions 483 for (int i=0; i<50; i++) { </pre> 484 <p> 485 Next we declare a <tt class="literal">retry</tt> variable. This is used to 486 determine whether a deadlock should result in our retrying the 487 operation. We also declare a <tt class="literal">retry_count</tt> variable 488 that is used to make sure we do not retry a transaction forever in the 489 unlikely event that the thread is unable to ever get a necessary lock. 490 (The only thing that might cause this is if some other thread dies 491 while holding an important lock. This is the only code that we have to 492 guard against that because the simplicity of this application makes it 493 highly unlikely that it will ever occur.) 494</p> 495 <pre class="programlisting"> boolean retry = true; 496 int retry_count = 0; 497 // while loop is used for deadlock retries 498 while (retry) { </pre> 499 <p> 500 Now we go into the <tt class="literal">try</tt> block that we use for 501 deadlock detection. We also begin our transaction here. 502</p> 503 <pre class="programlisting"> // try block used for deadlock detection and 504 // general exception handling 505 try { 506 507 // Get a transaction 508 txn = myEnv.beginTransaction(null, null); </pre> 509 <p> 510 Now we write 10 objects under the transaction that we have just begun. 511 By combining multiple writes together under a single transaction, 512 we increase the likelihood that a deadlock will occur. Normally, 513 you want to reduce the potential for a deadlock and in this case 514 the way to do that is to perform a single write per transaction. In 515 other words, we <span class="emphasis"><em>should</em></span> be using auto commit to 516 write to our database for this workload. 517 </p> 518 <p> 519 However, we want to show deadlock handling and by performing 520 multiple writes per transaction we can actually observe deadlocks 521 occurring. We also want to underscore the idea that you can 522 combing multiple database operations together in a single atomic 523 unit of work. So for our example, we do the (slightly) wrong thing. 524 </p> 525 <pre class="programlisting"> 526 527 // Write 10 PayloadDataEntity objects to the 528 // store for each transaction 529 for (int j = 0; j < 10; j++) { 530 // Instantiate an object 531 PayloadDataEntity pd = new PayloadDataEntity(); 532 533 // Set the Object ID. This is used as the 534 // primary key. 535 pd.setID(i + j); 536 537 // The thread name is used as a secondary key, and 538 // it is retrieved by this class's getName() 539 // method. 540 pd.setThreadName(getName()); 541 542 // The last bit of data that we use is a double 543 // that we generate randomly. This data is not 544 // indexed. 545 pd.setDoubleData(generator.nextDouble()); 546 547 // Do the put 548 pdKey.put(txn, pd); 549 } </pre> 550 <p> 551 Having completed the inner database write loop, we could simply 552 commit the transaction and continue on to the next block of 10 553 writes. However, we want to first illustrate a few points about 554 transactional processing so instead we call our 555 <tt class="function">countObjects()</tt> method before calling the transaction 556 commit. <tt class="function">countObjects()</tt> uses a cursor to read every 557 object in the entity store and return a count of the number of objects 558 that it found. 559 </p> 560 <p> 561 Because 562 <tt class="function">countObjects()</tt> 563 reads every object in the store, if used incorrectly the thread 564 will self-deadlock. The writer thread has just written 500 objects 565 to the database, but because the transaction used for that write 566 has not yet been committed, each of those 500 objects are still 567 locked by the thread's transaction. If we then simply run a 568 non-transactional cursor over the store from within the same 569 thread that has locked those 500 objects, the cursor will 570 block when it tries to read one of those transactional 571 protected records. The thread immediately stops operation at that 572 point while the cursor waits for the read lock it has 573 requested. Because that read lock will never be released (the thread 574 can never make any forward progress), this represents a 575 self-deadlock for the thread. 576 </p> 577 <p> 578 There are three ways to prevent this self-deadlock: 579 </p> 580 <div class="orderedlist"> 581 <ol type="1"> 582 <li> 583 <p> 584 We can move the call to 585 <tt class="function">countObjects()</tt> to a point after the 586 thread's transaction has committed. 587 </p> 588 </li> 589 <li> 590 <p> 591 We can allow <tt class="function">countObjects()</tt> to 592 operate under the same transaction as all of the writes 593 were performed. 594 </p> 595 </li> 596 <li> 597 <p> 598 We can reduce our isolation guarantee for the application 599 by allowing uncommitted reads. 600 </p> 601 </li> 602 </ol> 603 </div> 604 <p> 605 For this example, we choose to use option 3 (uncommitted reads) to avoid 606 the deadlock. This means that we have to open our underlying database such 607 that it supports uncommitted reads, and we have to open our cursor handle 608 so that it knows to perform uncommitted reads. 609 </p> 610 <pre class="programlisting"> // commit 611 System.out.println(getName() + " : committing txn : " + i); 612 System.out.println(getName() + " : Found " + 613 countObjects(txn) + " objects in the store."); </pre> 614 <p> 615 Having performed this somewhat inelegant counting of the objects in the 616 database, we can now commit the transaction. 617</p> 618 <pre class="programlisting"> try { 619 txn.commit(); 620 txn = null; 621 } catch (DatabaseException e) { 622 System.err.println("Error on txn commit: " + 623 e.toString()); 624 } 625 retry = false; </pre> 626 <p> 627 If all goes well with the commit, we are done and we can move on to the 628 next batch of 10 objects to add to the store. However, in the event 629 of an error, we must handle our exceptions correctly. The first of 630 these is a deadlock exception. In the event of a deadlock, we want to 631 abort and retry the transaction, provided that we have not already 632 exceeded our retry limit for this transaction. 633</p> 634 <pre class="programlisting"> } catch (DeadlockException de) { 635 System.out.println("################# " + getName() + 636 " : caught deadlock"); 637 // retry if necessary 638 if (retry_count < MAX_RETRY) { 639 System.err.println(getName() + 640 " : Retrying operation."); 641 retry = true; 642 retry_count++; 643 } else { 644 System.err.println(getName() + 645 " : out of retries. Giving up."); 646 retry = false; 647 } </pre> 648 <p> 649 In the event of a standard, non-specific database exception, we simply 650 log the exception and then give up (the transaction is not retried). 651</p> 652 <pre class="programlisting"> } catch (DatabaseException e) { 653 // abort and don't retry 654 retry = false; 655 System.err.println(getName() + 656 " : caught exception: " + e.toString()); 657 System.err.println(getName() + 658 " : errno: " + e.getErrno()); 659 e.printStackTrace(); </pre> 660 <p> 661 And, finally, we always abort the transaction if the transaction handle 662 is not null. Note that immediately after committing our transaction, we 663 set the transaction handle to null to guard against aborting a 664 transaction that has already been committed. 665</p> 666 <pre class="programlisting"> } finally { 667 if (txn != null) { 668 try { 669 txn.abort(); 670 } catch (Exception e) { 671 System.err.println("Error aborting txn: " + 672 e.toString()); 673 e.printStackTrace(); 674 } 675 } 676 } 677 } 678 } 679 } </pre> 680 <p> 681 The final piece of our <tt class="classname">StoreWriter</tt> class is the 682 <tt class="methodname">countObjects()</tt> implementation. Notice how in 683 this example we open the cursor such that it performs uncommitted 684 reads: 685</p> 686 <pre class="programlisting"> // A method that counts every object in the store. 687 688 private int countObjects(Transaction txn) throws DatabaseException { 689 int count = 0; 690 691 CursorConfig cc = new CursorConfig(); 692 // This is ignored if the store is not opened with uncommitted read 693 // support. 694 cc.setReadUncommitted(true); 695 EntityCursor<PayloadDataEntity> cursor = pdKey.entities(txn, cc); 696 697 try { 698 for (PayloadDataEntity pdi : cursor) { 699 count++; 700 } 701 } finally { 702 if (cursor != null) { 703 cursor.close(); 704 } 705 } 706 707 return count; 708 709 } 710} </pre> 711 </div> 712 <p> 713 This completes our transactional example. If you would like to 714 experiment with this code, you can find the example in the following 715 location in your DB distribution: 716</p> 717 <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_java/src/persist/txn</pre> 718 </div> 719 <div class="navfooter"> 720 <hr /> 721 <table width="100%" summary="Navigation footer"> 722 <tr> 723 <td width="40%" align="left"><a accesskey="p" href="txnexample_java.html">Prev</a>��</td> 724 <td width="20%" align="center"> 725 <a accesskey="u" href="wrapup.html">Up</a> 726 </td> 727 <td width="40%" align="right">��<a accesskey="n" href="inmem_txnexample_java.html">Next</a></td> 728 </tr> 729 <tr> 730 <td width="40%" align="left" valign="top">Base API Transaction Example��</td> 731 <td width="20%" align="center"> 732 <a accesskey="h" href="index.html">Home</a> 733 </td> 734 <td width="40%" align="right" valign="top">��Base API In-Memory Transaction Example</td> 735 </tr> 736 </table> 737 </div> 738 </body> 739</html> 740