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