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