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