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