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>Secondary Database 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" /> 10 <link rel="up" href="indexes.html" title="Chapter 10. Secondary Databases" /> 11 <link rel="prev" href="joins.html" title="Database Joins" /> 12 <link rel="next" href="dbconfig.html" title="Chapter 11. Database Configuration" /> 13 </head> 14 <body> 15 <div class="navheader"> 16 <table width="100%" summary="Navigation header"> 17 <tr> 18 <th colspan="3" align="center">Secondary Database Example</th> 19 </tr> 20 <tr> 21 <td width="20%" align="left"><a accesskey="p" href="joins.html">Prev</a> </td> 22 <th width="60%" align="center">Chapter 10. Secondary Databases</th> 23 <td width="20%" align="right"> <a accesskey="n" href="dbconfig.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="javaindexusage"></a>Secondary Database Example</h2> 33 </div> 34 </div> 35 </div> 36 <div class="toc"> 37 <dl> 38 <dt> 39 <span class="sect2"> 40 <a href="javaindexusage.html#secondaryMyDbs">Opening Secondary Databases with MyDbs</a> 41 </span> 42 </dt> 43 <dt> 44 <span class="sect2"> 45 <a href="javaindexusage.html#exampleReadJavaSecondaries">Using Secondary Databases with ExampleDatabaseRead</a> 46 </span> 47 </dt> 48 </dl> 49 </div> 50 <p>In previous chapters in this book, we built applications that load 51 and display several DB databases. In this example, we will extend those 52 examples to use secondary databases. Specifically:</p> 53 <div class="itemizedlist"> 54 <ul type="disc"> 55 <li> 56 <p>In <a class="xref" href="dbtJavaUsage.html#dbsStoredClass" title="Example 8.4 Stored Class Catalog Management with MyDbs">Stored Class Catalog Management with MyDbs</a> we built a 57 class that we can use to open several <code class="classname">Database</code> objects. 58 In <a class="xref" href="javaindexusage.html#secondaryMyDbs" title="Opening Secondary Databases with MyDbs">Opening Secondary Databases with MyDbs</a> we will extend 59 that class to also open and manage a <code class="classname">SecondaryDatabase</code>. 60 </p> 61 </li> 62 <li> 63 <p>In <a class="xref" href="cursorJavaUsage.html" title="Cursor Example">Cursor Example</a> we 64 built an application to display our inventory database (and related 65 vendor information). In <a class="xref" href="javaindexusage.html#exampleReadJavaSecondaries" title="Using Secondary Databases with ExampleDatabaseRead">Using Secondary Databases with ExampleDatabaseRead</a> we will extend that application to 66 show inventory records based on the index we cause to be loaded using 67 <code class="classname">ExampleDatabaseLoad</code>. 68 </p> 69 </li> 70 </ul> 71 </div> 72 <p> 73 Before we can use a secondary database, we must implement a class to extract secondary keys for us. 74 We use <code class="classname">ItemNameKeyCreator</code> for this purpose. 75 </p> 76 <div class="example"> 77 <a id="ItemNameKeyCreator-Java"></a> 78 <p class="title"> 79 <b>Example 10.1 ItemNameKeyCreator.java</b> 80 </p> 81 <div class="example-contents"> 82 <p> 83 This class assumes the primary database 84 uses <code class="classname">Inventory</code> objects for the record data. The 85 <code class="classname">Inventory</code> class is described in <a class="xref" href="dbtJavaUsage.html#inventoryjava" title="Example 8.1 Inventory.java">Inventory.java</a>.</p> 86 <p>In our key creator class, we make use of a custom tuple binding 87 called <code class="classname">InventoryBinding</code>. This class is described in <a class="xref" href="dbtJavaUsage.html#InventoryJavaBinding" title="Example 8.3 InventoryBinding.java">InventoryBinding.java</a>.</p> 88 <p>You can find <code class="filename">InventoryBinding.java</code> in: </p> 89 <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_java/db/GettingStarted</pre> 90 <p> 91 where <code class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></code> is the location where you 92 placed your DB distribution. 93 </p> 94 <a id="java_index11"></a> 95 <pre class="programlisting">package db.GettingStarted; 96 97import com.sleepycat.db.DatabaseEntry; 98import com.sleepycat.db.DatabaseException; 99import com.sleepycat.db.SecondaryDatabase; 100import com.sleepycat.db.SecondaryKeyCreator; 101import com.sleepycat.bind.tuple.TupleBinding; 102 103import java.io.IOException; 104 105public class ItemNameKeyCreator implements SecondaryKeyCreator { 106 107 private TupleBinding theBinding; 108 109 // Use the constructor to set the tuple binding 110 ItemNameKeyCreator(TupleBinding binding) { 111 theBinding = binding; 112 } 113 114 // Abstract method that we must implement 115 public boolean createSecondaryKey(SecondaryDatabase secDb, 116 DatabaseEntry keyEntry, // From the primary 117 DatabaseEntry dataEntry, // From the primary 118 DatabaseEntry resultEntry) // set the key data on this. 119 throws DatabaseException { 120 121 try { 122 // Convert dataEntry to an Inventory object 123 Inventory inventoryItem = 124 (Inventory) theBinding.entryToObject(dataEntry); 125 // Get the item name and use that as the key 126 String theItem = inventoryItem.getItemName(); 127 resultEntry.setData(theItem.getBytes("UTF-8")); 128 } catch (IOException willNeverOccur) {} 129 130 return true; 131 } 132} </pre> 133 </div> 134 </div> 135 <br class="example-break" /> 136 <p> 137 Now that we have a key creator, we can use it to generate keys for a 138 secondary database. We will now extend <code class="classname">MyDbs</code> 139 to manage a secondary database, and to use 140 <code class="classname">ItemNameKeyCreator</code> to generate keys for that 141 secondary database. 142 </p> 143 <div class="sect2" lang="en" xml:lang="en"> 144 <div class="titlepage"> 145 <div> 146 <div> 147 <h3 class="title"><a id="secondaryMyDbs"></a>Opening Secondary Databases with MyDbs</h3> 148 </div> 149 </div> 150 </div> 151 <p>In <a class="xref" href="dbtJavaUsage.html#dbsStoredClass" title="Example 8.4 Stored Class Catalog Management with MyDbs">Stored Class Catalog Management with MyDbs</a> we built 152 <code class="classname">MyDbs</code> as an example of a class that 153 encapsulates 154 <code class="classname">Database</code> opens and closes. We will now extend 155 that class to manage a <code class="classname">SecondaryDatabase</code>.</p> 156 <div class="example"> 157 <a id="mydbsSecondary"></a> 158 <p class="title"> 159 <b>Example 10.2 SecondaryDatabase Management with MyDbs</b> 160 </p> 161 <div class="example-contents"> 162 <p> 163 We start by importing two additional classes needed to support secondary databases. 164 We also add a global variable to use as a handle for our secondary database. 165 </p> 166 <a id="java_index12"></a> 167 <pre class="programlisting">// File MyDbs.java 168package db.GettingStarted; 169 170import java.io.FileNotFoundException; 171 172import com.sleepycat.bind.serial.StoredClassCatalog; 173import com.sleepycat.bind.tuple.TupleBinding; 174import com.sleepycat.db.Database; 175import com.sleepycat.db.DatabaseConfig; 176import com.sleepycat.db.DatabaseException; 177import com.sleepycat.db.DatabaseType; 178<strong class="userinput"><code>import com.sleepycat.db.SecondaryConfig; 179import com.sleepycat.db.SecondaryDatabase;</code></strong> 180 181public class MyDbs { 182 183 // The databases that our application uses 184 private Database vendorDb = null; 185 private Database inventoryDb = null; 186 private Database classCatalogDb = null; 187 <strong class="userinput"><code>private SecondaryDatabase itemNameIndexDb = null;</code></strong> 188 189 private String vendordb = "VendorDB.db"; 190 private String inventorydb = "InventoryDB.db"; 191 private String classcatalogdb = "ClassCatalogDB.db"; 192 <strong class="userinput"><code>private String itemnameindexdb = "ItemNameIndexDB.db";</code></strong> 193 194 // Needed for object serialization 195 private StoredClassCatalog classCatalog; 196 197 // Our constructor does nothing 198 public MyDbs() {} </pre> 199 <p> 200 Next we update the <code class="methodname">MyDbs.setup()</code> method to open the 201 secondary database. As a part of this, we have to pass an 202 <code class="classname">ItemNameKeyCreator</code> object on the call to open the secondary 203 database. Also, in order to instantiate <code class="classname">ItemNameKeyCreator</code>, we need an 204 <code class="classname">InventoryBinding</code> object (we described this class in 205 <a class="xref" href="dbtJavaUsage.html#InventoryJavaBinding" title="Example 8.3 InventoryBinding.java">InventoryBinding.java</a>). 206 We do all this work together inside of <code class="methodname">MyDbs.setup()</code>. 207 </p> 208 <a id="java_index13"></a> 209 <pre class="programlisting"> public void setup(String databasesHome) 210 throws DatabaseException { 211 DatabaseConfig myDbConfig = new DatabaseConfig(); 212 <strong class="userinput"><code>SecondaryConfig mySecConfig = new SecondaryConfig();</code></strong> 213 214 myDbConfig.setErrorStream(System.err); 215 <strong class="userinput"><code>mySecConfig.setErrorStream(System.err);</code></strong> 216 myDbConfig.setErrorPrefix("MyDbs"); 217 <strong class="userinput"><code>mySecConfig.setErrorPrefix("MyDbs");</code></strong> 218 myDbConfig.setType(DatabaseType.BTREE); 219 <strong class="userinput"><code>mySecConfig.setType(DatabaseType.BTREE);</code></strong> 220 myDbConfig.setAllowCreate(true); 221 <strong class="userinput"><code>mySecConfig.setAllowCreate(true);</code></strong> 222 223 // Now open, or create and open, our databases 224 // Open the vendors and inventory databases 225 try { 226 vendordb = databasesHome + "/" + vendordb; 227 vendorDb = new Database(vendordb, 228 null, 229 myDbConfig); 230 231 inventorydb = databasesHome + "/" + inventorydb; 232 inventoryDb = new Database(inventorydb, 233 null, 234 myDbConfig); 235 236 // Open the class catalog db. This is used to 237 // optimize class serialization. 238 classcatalogdb = databasesHome + "/" + classcatalogdb; 239 classCatalogDb = new Database(classcatalogdb, 240 null, 241 myDbConfig); 242 } catch(FileNotFoundException fnfe) { 243 System.err.println("MyDbs: " + fnfe.toString()); 244 System.exit(-1); 245 } 246 247 // Create our class catalog 248 classCatalog = new StoredClassCatalog(classCatalogDb); 249 250 // Need a tuple binding for the Inventory class. 251 // We use the InventoryBinding class 252 // that we implemented for this purpose. 253 TupleBinding inventoryBinding = new InventoryBinding(); 254 255 <strong class="userinput"><code>// Open the secondary database. We use this to create a 256 // secondary index for the inventory database 257 258 // We want to maintain an index for the inventory entries based 259 // on the item name. So, instantiate the appropriate key creator 260 // and open a secondary database. 261 ItemNameKeyCreator keyCreator = 262 new ItemNameKeyCreator(new InventoryBinding()); 263 264 // Set up additional secondary properties 265 // Need to allow duplicates for our secondary database 266 mySecConfig.setSortedDuplicates(true); 267 mySecConfig.setAllowPopulate(true); // Allow autopopulate 268 mySecConfig.setKeyCreator(keyCreator); 269 // Now open it 270 try { 271 itemnameindexdb = databasesHome + "/" + itemnameindexdb; 272 itemNameIndexDb = new SecondaryDatabase(itemnameindexdb, 273 null, 274 inventoryDb, 275 mySecConfig); 276 } catch(FileNotFoundException fnfe) { 277 System.err.println("MyDbs: " + fnfe.toString()); 278 System.exit(-1); 279 }</code></strong> 280 } 281 </pre> 282 <p> 283 Next we need an additional getter method for returning the secondary database. 284 </p> 285 <a id="java_index14"></a> 286 <pre class="programlisting"> public SecondaryDatabase getNameIndexDB() { 287 return itemNameIndexDb; 288 } </pre> 289 <p>Finally, we need to update the <code class="methodname">MyDbs.close()</code> 290 method to close the new secondary database. We want to make sure that 291 the secondary is closed before the primaries. While 292 this is not necessary for this example because our 293 closes are single-threaded, it is still a good habit to adopt.</p> 294 <a id="java_index15"></a> 295 <pre class="programlisting"> public void close() { 296 try { 297 <strong class="userinput"><code>if (itemNameIndexDb != null) { 298 itemNameIndexDb.close(); 299 }</code></strong> 300 301 if (vendorDb != null) { 302 vendorDb.close(); 303 } 304 305 if (inventoryDb != null) { 306 inventoryDb.close(); 307 } 308 309 if (classCatalogDb != null) { 310 classCatalogDb.close(); 311 } 312 313 } catch(DatabaseException dbe) { 314 System.err.println("Error closing MyDbs: " + 315 dbe.toString()); 316 System.exit(-1); 317 } 318 } 319} </pre> 320 <p>That completes our update to <code class="classname">MyDbs</code>. You 321 can find the complete class implementation in: 322 </p> 323 <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_java/db/GettingStarted</pre> 324 <p> 325 where <code class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></code> is the location where you 326 placed your DB distribution. 327 </p> 328 </div> 329 </div> 330 <br class="example-break" /> 331 </div> 332 <div class="sect2" lang="en" xml:lang="en"> 333 <div class="titlepage"> 334 <div> 335 <div> 336 <h3 class="title"><a id="exampleReadJavaSecondaries"></a>Using Secondary Databases with ExampleDatabaseRead</h3> 337 </div> 338 </div> 339 </div> 340 <p>Because we performed all our secondary database configuration management in 341 <code class="classname">MyDbs</code>, we do not need to modify <code class="classname">ExampleDatabaseLoad</code> at all in 342 order to create our secondary indices. When <code class="classname">ExampleDatabaseLoad</code> calls 343 <code class="methodname">MyDbs.setup()</code>, all of the necessary work is performed for us. 344 </p> 345 <p> 346 However, we still need to take advantage of the new secondary indices. We do this by updating 347 <code class="classname">ExampleDatabaseRead</code> to allow us to query for an inventory record based on its name. 348 Remember that the primary key for an inventory record is the item's SKU. The item's name is contained in the 349 <code class="classname">Inventory</code> object that is stored as each record's data in the inventory database. But 350 our new secondary index now allows us to easily query based on the item's name. 351 </p> 352 <p> 353 For this update, we modify 354 <code class="classname">ExampleDatabaseRead</code> to accept a new command line switch, 355 <code class="literal">-s</code>, whose argument is the name of an inventory item. 356 If the switch is present on the command line call to 357 <code class="classname">ExampleDatabaseRead</code>, then the application will 358 use the secondary database to look up and display all the inventory 359 records with that item name. Note that we use a <code class="classname">SecondaryCursor</code> 360 to seek to the item name key and then display all matching records. 361 </p> 362 <p>Remember that you can find <code class="filename">ExampleDatabaseRead.java</code> in: </p> 363 <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_java/db/GettingStarted</pre> 364 <p> 365 where <code class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></code> is the location where you 366 placed your DB distribution. 367 </p> 368 <div class="example"> 369 <a id="secondaryWithEDR"></a> 370 <p class="title"> 371 <b>Example 10.3 SecondaryDatabase usage with ExampleDatabaseRead</b> 372 </p> 373 <div class="example-contents"> 374 <p> 375 First we need to import an additional class in order to use the secondary cursor: 376 </p> 377 <a id="java_index16"></a> 378 <pre class="programlisting">package db.GettingStarted; 379 380import java.io.IOException; 381 382import com.sleepycat.bind.EntryBinding; 383import com.sleepycat.bind.serial.SerialBinding; 384import com.sleepycat.bind.tuple.TupleBinding; 385import com.sleepycat.db.Cursor; 386import com.sleepycat.db.DatabaseEntry; 387import com.sleepycat.db.DatabaseException; 388import com.sleepycat.db.LockMode; 389import com.sleepycat.db.OperationStatus; 390<strong class="userinput"><code>import com.sleepycat.db.SecondaryCursor;</code></strong> </pre> 391 <p>Next we add a single global variable:</p> 392 <a id="java_index17"></a> 393 <pre class="programlisting"> public class ExampleDatabaseRead { 394 395 private static String myDbsPath = "./"; 396 397 // Encapsulates the database environment and databases. 398 private static MyDbs myDbs = new MyDbs(); 399 400 private static TupleBinding inventoryBinding; 401 private static EntryBinding vendorBinding; 402 403 <strong class="userinput"><code>// The item to locate if the -s switch is used 404 private static String locateItem;</code></strong> </pre> 405 <p>Next we update <code class="methodname">ExampleDatabaseRead.run()</code> to 406 check to see if the <code class="literal">locateItem</code> global variable has a 407 value. If it does, then we show just those records related to the item 408 name passed on the <code class="literal">-s</code> switch.</p> 409 <a id="java_index18"></a> 410 <pre class="programlisting"> private void run(String args[]) 411 throws DatabaseException { 412 // Parse the arguments list 413 parseArgs(args); 414 415 myDbs.setup(myDbsPath); 416 417 // Setup our bindings. 418 inventoryBinding = new InventoryBinding(); 419 vendorBinding = 420 new SerialBinding(myDbs.getClassCatalog(), 421 Vendor.class); 422 423 <strong class="userinput"><code>if (locateItem != null) { 424 showItem(); 425 } else {</code></strong> 426 showAllInventory(); 427 <strong class="userinput"><code>}</code></strong> 428 } </pre> 429 <p> 430 Finally, we need to implement <code class="methodname">ExampleDatabaseRead.showItem()</code>. 431 This is a fairly simple method that opens a secondary cursor, 432 and then displays every primary record that is related to the secondary 433 key identified by the <code class="literal">locateItem</code> global variable. 434 </p> 435 <a id="java_index19"></a> 436 <pre class="programlisting"> private void showItem() throws DatabaseException { 437 SecondaryCursor secCursor = null; 438 try { 439 // searchKey is the key that we want to find in the 440 // secondary db. 441 DatabaseEntry searchKey = 442 new DatabaseEntry(locateItem.getBytes("UTF-8")); 443 444 // foundKey and foundData are populated from the primary 445 // entry that is associated with the secondary db key. 446 DatabaseEntry foundKey = new DatabaseEntry(); 447 DatabaseEntry foundData = new DatabaseEntry(); 448 449 // open a secondary cursor 450 secCursor = 451 myDbs.getNameIndexDB().openSecondaryCursor(null, null); 452 453 // Search for the secondary database entry. 454 OperationStatus retVal = 455 secCursor.getSearchKey(searchKey, foundKey, 456 foundData, LockMode.DEFAULT); 457 458 // Display the entry, if one is found. Repeat until no more 459 // secondary duplicate entries are found 460 while(retVal == OperationStatus.SUCCESS) { 461 Inventory theInventory = 462 (Inventory)inventoryBinding.entryToObject(foundData); 463 displayInventoryRecord(foundKey, theInventory); 464 retVal = secCursor.getNextDup(searchKey, foundKey, 465 foundData, LockMode.DEFAULT); 466 } 467 } catch (Exception e) { 468 System.err.println("Error on inventory secondary cursor:"); 469 System.err.println(e.toString()); 470 e.printStackTrace(); 471 } finally { 472 if (secCursor != null) { 473 secCursor.close(); 474 } 475 } 476 477 }</pre> 478 <p>The only other thing left to do is to update 479 <code class="methodname">ExampleDatabaseRead.parseArgs()</code> to support the <code class="literal">-s</code> command 480 line switch. To see how this is done, see 481 <code class="filename">ExampleDatabaseRead.java</code> in: 482 </p> 483 <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_java/db/GettingStarted</pre> 484 <p> 485 where <code class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></code> is the location where you 486 placed your DB distribution. 487 </p> 488 </div> 489 </div> 490 <br class="example-break" /> 491 </div> 492 </div> 493 <div class="navfooter"> 494 <hr /> 495 <table width="100%" summary="Navigation footer"> 496 <tr> 497 <td width="40%" align="left"><a accesskey="p" href="joins.html">Prev</a> </td> 498 <td width="20%" align="center"> 499 <a accesskey="u" href="indexes.html">Up</a> 500 </td> 501 <td width="40%" align="right"> <a accesskey="n" href="dbconfig.html">Next</a></td> 502 </tr> 503 <tr> 504 <td width="40%" align="left" valign="top">Database Joins </td> 505 <td width="20%" align="center"> 506 <a accesskey="h" href="index.html">Home</a> 507 </td> 508 <td width="40%" align="right" valign="top"> Chapter 11. Database Configuration</td> 509 </tr> 510 </table> 511 </div> 512 </body> 513</html> 514