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