示例#1
0
  public void testTruncate() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();
    Transaction txn = txnBegin();

    for (int i = 0; i < NUM_RECS; i += 1) {
      priDb.put(txn, entry(i), entry(i));
    }
    verifyRecords(txn, priDb, NUM_RECS, false);
    verifyRecords(txn, secDb, NUM_RECS, true);
    txnCommit(txn);
    secDb.close();
    priDb.close();

    txn = txnBegin();
    assertEquals(NUM_RECS, env.truncateDatabase(txn, "testDB", true));
    assertEquals(NUM_RECS, env.truncateDatabase(txn, "testSecDB", true));
    txnCommit(txn);

    secDb = initDb();
    priDb = secDb.getPrimaryDatabase();

    txn = txnBegin();
    verifyRecords(txn, priDb, 0, false);
    verifyRecords(txn, secDb, 0, true);
    txnCommit(txn);

    secDb.close();
    priDb.close();
  }
示例#2
0
  /**
   * Returns a read-only keys index that maps secondary key to primary key. When accessing the keys
   * index, the primary key is returned rather than the entity. When only the primary key is needed
   * and not the entire entity, using the keys index is less expensive than using the secondary
   * index because the primary index does not have to be accessed.
   *
   * <p>Note the following in the unusual case that you are <em>not</em> using an <code>EntityStore
   * </code>: This method will open the keys database, a second database handle for the secondary
   * database, if it is not already open. In this case, if you are <em>not</em> using an <code>
   * EntityStore</code>, then you are responsible for closing the database returned by {@link
   * #getKeysDatabase} before closing the environment. If you <em>are</em> using an <code>
   * EntityStore</code>, the keys database will be closed automatically by {@link
   * EntityStore#close}.
   *
   * @return the keys index.
   */
  public synchronized EntityIndex<SK, PK> keysIndex() throws DatabaseException {

    if (keysIndex == null) {
      if (keysDb == null) {
        DatabaseConfig config = secDb.getConfig();
        config.setReadOnly(true);
        keysDb = db.getEnvironment().openDatabase(null, secDb.getDatabaseName(), config);
      }
      keysIndex =
          new KeysIndex<SK, PK>(
              keysDb, keyClass, keyBinding, priIndex.getKeyClass(), priIndex.getKeyBinding());
    }
    return keysIndex;
  }
示例#3
0
  public void testOpenAndClose() throws DatabaseException {

    Database priDb = openDatabase(false, "testDB", false);

    /* Open two secondaries as regular databases and as secondaries. */
    Database secDbDetached = openDatabase(true, "testSecDB", false);
    SecondaryDatabase secDb = openSecondary(priDb, true, "testSecDB", false, false);
    Database secDb2Detached = openDatabase(true, "testSecDB2", false);
    SecondaryDatabase secDb2 = openSecondary(priDb, true, "testSecDB2", false, false);
    assertEquals(
        priDb.getSecondaryDatabases(), Arrays.asList(new SecondaryDatabase[] {secDb, secDb2}));

    Transaction txn = txnBegin();

    /* Check that primary writes to both secondaries. */
    checkSecondaryUpdate(txn, priDb, 1, secDbDetached, true, secDb2Detached, true);

    /* New txn before closing database. */
    txnCommit(txn);
    txn = txnBegin();

    /* Close 2nd secondary. */
    secDb2.close();
    assertEquals(priDb.getSecondaryDatabases(), Arrays.asList(new SecondaryDatabase[] {secDb}));

    /* Check that primary writes to 1st secondary only. */
    checkSecondaryUpdate(txn, priDb, 2, secDbDetached, true, secDb2Detached, false);

    /* New txn before closing database. */
    txnCommit(txn);
    txn = txnBegin();

    /* Close 1st secondary. */
    secDb.close();
    assertEquals(0, priDb.getSecondaryDatabases().size());

    /* Check that primary writes to no secondaries. */
    checkSecondaryUpdate(txn, priDb, 3, secDbDetached, false, secDb2Detached, false);

    /* Open the two secondaries again. */
    secDb = openSecondary(priDb, true, "testSecDB", false, false);
    secDb2 = openSecondary(priDb, true, "testSecDB2", false, false);
    assertEquals(
        priDb.getSecondaryDatabases(), Arrays.asList(new SecondaryDatabase[] {secDb, secDb2}));

    /* Check that primary writes to both secondaries. */
    checkSecondaryUpdate(txn, priDb, 4, secDbDetached, true, secDb2Detached, true);

    /* Close the primary first to disassociate secondaries. */
    txnCommit(txn);
    priDb.close();
    assertNull(secDb.getPrimaryDatabase());
    assertNull(secDb2.getPrimaryDatabase());
    secDb2.close();
    secDb.close();

    secDb2Detached.close();
    secDbDetached.close();
  }
示例#4
0
  public BDBCreator(String tableName, ArrayList<Integer> keyPositions, int secondaryIndex) {
    /* Creates and loads a BDB table with a secondary index on the column defined by*/
    this.tableName = tableName.toLowerCase();
    this.keyPositions = keyPositions;
    this.secIndexPos = secondaryIndex;
    this.tMap = Main.indexTypeMaps.get(this.tableName);
    setEnvironment();

    dbConfig = new DatabaseConfig();
    dbConfig.setAllowCreate(true);
    myDB = myDbEnvironment.openDatabase(null, this.tableName, dbConfig);
    SecondaryConfig secConfig = new SecondaryConfig();
    secConfig.setAllowCreate(true);
    secConfig.setSortedDuplicates(true);

    createSecDB(secConfig);
    setConfig(dbConfig, secConfig);

    loadData();

    if (secDB != null) {
      secDB.close();
    }
    if (myDB != null) {
      myDB.close();
    }

    closeEnvironment();
  }
示例#5
0
  private SecondaryDatabase openSecondary(
      Database priDb,
      boolean allowDuplicates,
      String dbName,
      boolean allowPopulate,
      boolean readOnly)
      throws DatabaseException {

    List secListBefore = priDb.getSecondaryDatabases();
    SecondaryConfig dbConfig = new SecondaryConfig();
    dbConfig.setTransactional(isTransactional);
    dbConfig.setAllowCreate(true);
    dbConfig.setSortedDuplicates(allowDuplicates);
    dbConfig.setReadOnly(readOnly);
    dbConfig.setAllowPopulate(allowPopulate);
    if (!readOnly) {
      if (useMultiKey) {
        dbConfig.setMultiKeyCreator(new SimpleMultiKeyCreator(new MyKeyCreator()));
      } else {
        dbConfig.setKeyCreator(new MyKeyCreator());
      }
    }
    Transaction txn = txnBegin();
    SecondaryDatabase secDb;
    try {
      secDb = env.openSecondaryDatabase(txn, dbName, priDb, dbConfig);
    } finally {
      txnCommit(txn);
    }
    assertNotNull(secDb);

    /* Check configuration. */
    assertSame(priDb, secDb.getPrimaryDatabase());
    SecondaryConfig config2 = secDb.getSecondaryConfig();
    assertEquals(allowPopulate, config2.getAllowPopulate());
    assertEquals(dbConfig.getKeyCreator(), config2.getKeyCreator());

    /* Make sure the new secondary is added to the primary's list. */
    List secListAfter = priDb.getSecondaryDatabases();
    assertTrue(secListAfter.remove(secDb));
    assertEquals(secListBefore, secListAfter);

    return secDb;
  }
示例#6
0
  public void testPopulate() throws DatabaseException {

    Database priDb = openDatabase(false, "testDB", false);
    Transaction txn = txnBegin();

    /* Test population of newly created secondary database. */

    for (int i = 0; i < NUM_RECS; i += 1) {
      assertSame(OperationStatus.SUCCESS, priDb.put(txn, entry(i), entry(i)));
    }
    txnCommit(txn);

    SecondaryDatabase secDb = openSecondary(priDb, true, "testSecDB", true, false);
    txn = txnBegin();
    verifyRecords(txn, secDb, NUM_RECS, true);
    txnCommit(txn);

    /*
     * Clear secondary and perform populate again, to test the case where
     * an existing database is opened, and therefore a write txn will only
     * be created in order to populate it
     */
    Database secDbDetached = openDatabase(true, "testSecDB", false);
    secDb.close();
    txn = txnBegin();
    for (int i = 0; i < NUM_RECS; i += 1) {
      assertSame(OperationStatus.SUCCESS, secDbDetached.delete(txn, entry(i + KEY_OFFSET)));
    }
    verifyRecords(txn, secDbDetached, 0, true);
    txnCommit(txn);
    secDb = openSecondary(priDb, true, "testSecDB", true, false);
    txn = txnBegin();
    verifyRecords(txn, secDb, NUM_RECS, true);
    verifyRecords(txn, secDbDetached, NUM_RECS, true);

    txnCommit(txn);
    secDbDetached.close();
    secDb.close();
    priDb.close();
  }
示例#7
0
  public void testReadOnly() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();
    OperationStatus status;
    Transaction txn = txnBegin();

    for (int i = 0; i < NUM_RECS; i += 1) {
      status = priDb.put(txn, entry(i), entry(i));
      assertSame(OperationStatus.SUCCESS, status);
    }

    /*
     * Secondaries can be opened without a key creator if the primary is
     * read only.  openSecondary will specify a null key creator if the
     * readOnly param is false.
     */
    Database readOnlyPriDb = openDatabase(false, "testDB", true);
    SecondaryDatabase readOnlySecDb = openSecondary(readOnlyPriDb, true, "testSecDB", false, true);
    assertNull(readOnlySecDb.getSecondaryConfig().getKeyCreator());
    verifyRecords(txn, readOnlySecDb, NUM_RECS, true);

    txnCommit(txn);
    readOnlySecDb.close();
    readOnlyPriDb.close();
    secDb.close();
    priDb.close();
  }
示例#8
0
  public E get(Transaction txn, SK key, LockMode lockMode) throws DatabaseException {

    DatabaseEntry keyEntry = new DatabaseEntry();
    DatabaseEntry pkeyEntry = new DatabaseEntry();
    DatabaseEntry dataEntry = new DatabaseEntry();
    keyBinding.objectToEntry(key, keyEntry);

    OperationStatus status = secDb.get(txn, keyEntry, pkeyEntry, dataEntry, lockMode);

    if (status == OperationStatus.SUCCESS) {
      return (E) entityBinding.entryToObject(pkeyEntry, dataEntry);
    } else {
      return null;
    }
  }
示例#9
0
  /** [#14966] */
  public void testDirtyReadPartialGet() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();

    DatabaseEntry data = new DatabaseEntry();
    DatabaseEntry key = new DatabaseEntry();
    DatabaseEntry secKey = new DatabaseEntry();
    OperationStatus status;

    /* Put a record */
    Transaction txn = txnBegin();
    status = priDb.put(txn, entry(0), entry(0));
    assertSame(OperationStatus.SUCCESS, status);
    txnCommit(txn);

    /* Regular get */
    status = secDb.get(null, entry(0 + KEY_OFFSET), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.SUCCESS, status);
    assertDataEquals(entry(0), key);
    assertDataEquals(entry(0), data);

    /* Dirty read returning no data */
    data.setPartial(0, 0, true);
    status = secDb.get(null, entry(0 + KEY_OFFSET), key, data, LockMode.READ_UNCOMMITTED);
    assertSame(OperationStatus.SUCCESS, status);
    assertDataEquals(entry(0), key);
    assertEquals(0, data.getData().length);
    assertEquals(0, data.getSize());

    /* Dirty read returning partial data */
    data.setPartial(0, 1, true);
    status = secDb.get(null, entry(0 + KEY_OFFSET), key, data, LockMode.READ_UNCOMMITTED);
    assertSame(OperationStatus.SUCCESS, status);
    assertDataEquals(entry(0), key);
    assertEquals(1, data.getData().length);
    assertEquals(1, data.getSize());

    secDb.close();
    priDb.close();
  }
示例#10
0
  /** Test that null can be passed for the LockMode to all get methods. */
  public void testNullLockMode() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();
    Transaction txn = txnBegin();

    DatabaseEntry key = entry(0);
    DatabaseEntry data = entry(0);
    DatabaseEntry secKey = entry(KEY_OFFSET);
    DatabaseEntry found = new DatabaseEntry();
    DatabaseEntry found2 = new DatabaseEntry();
    DatabaseEntry found3 = new DatabaseEntry();

    assertEquals(OperationStatus.SUCCESS, priDb.put(txn, key, data));
    assertEquals(OperationStatus.SUCCESS, priDb.put(txn, entry(1), data));
    assertEquals(OperationStatus.SUCCESS, priDb.put(txn, entry(2), entry(2)));

    /* Database operations. */

    assertEquals(OperationStatus.SUCCESS, priDb.get(txn, key, found, null));
    assertEquals(OperationStatus.SUCCESS, priDb.getSearchBoth(txn, key, data, null));
    assertEquals(OperationStatus.SUCCESS, secDb.get(txn, secKey, found, null));
    assertEquals(OperationStatus.SUCCESS, secDb.get(txn, secKey, found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secDb.getSearchBoth(txn, secKey, key, found, null));

    /* Cursor operations. */

    txnCommit(txn);
    txn = txnBeginCursor();
    Cursor cursor = priDb.openCursor(txn, null);
    SecondaryCursor secCursor = secDb.openSecondaryCursor(txn, null);

    assertEquals(OperationStatus.SUCCESS, cursor.getSearchKey(key, found, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getSearchBoth(key, data, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getSearchKeyRange(key, found, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getSearchBothRange(key, data, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getFirst(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getNext(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getPrev(found, found2, null));
    assertEquals(OperationStatus.NOTFOUND, cursor.getNextDup(found, found2, null));
    assertEquals(OperationStatus.NOTFOUND, cursor.getPrevDup(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getNextNoDup(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getPrevNoDup(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, cursor.getLast(found, found2, null));

    assertEquals(OperationStatus.SUCCESS, secCursor.getSearchKey(secKey, found, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getSearchKeyRange(secKey, found, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getFirst(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getNext(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getPrev(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getNextDup(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getPrevDup(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getNextNoDup(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getPrevNoDup(found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getLast(found, found2, null));

    assertEquals(OperationStatus.SUCCESS, secCursor.getSearchKey(secKey, found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getSearchBoth(secKey, data, found, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getSearchKeyRange(secKey, found, found2, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getSearchBothRange(secKey, data, found, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getFirst(found, found2, found3, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getNext(found, found2, found3, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getPrev(found, found2, found3, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getNextDup(found, found2, found3, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getPrevDup(found, found2, found3, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getNextNoDup(found, found2, found3, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getPrevNoDup(found, found2, found3, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getLast(found, found2, found3, null));

    secCursor.close();
    cursor.close();
    txnCommit(txn);
    secDb.close();
    priDb.close();
    env.close();
    env = null;
  }
示例#11
0
  /** @deprecated use of Database.truncate */
  public void testOperationsNotAllowed() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();
    Transaction txn = txnBegin();

    /* Open secondary without a key creator. */
    try {
      env.openSecondaryDatabase(txn, "xxx", priDb, null);
      fail();
    } catch (NullPointerException expected) {
    }
    try {
      env.openSecondaryDatabase(txn, "xxx", priDb, new SecondaryConfig());
      fail();
    } catch (NullPointerException expected) {
    }

    /* Open secondary with both single and multi key creators. */
    SecondaryConfig config = new SecondaryConfig();
    config.setKeyCreator(new MyKeyCreator());
    config.setMultiKeyCreator(new SimpleMultiKeyCreator(new MyKeyCreator()));
    try {
      env.openSecondaryDatabase(txn, "xxx", priDb, config);
      fail();
    } catch (IllegalArgumentException expected) {
    }

    /* Database operations. */

    DatabaseEntry key = entry(1);
    DatabaseEntry data = entry(2);

    try {
      secDb.getSearchBoth(txn, key, data, LockMode.DEFAULT);
      fail();
    } catch (UnsupportedOperationException expected) {
    }

    try {
      secDb.put(txn, key, data);
      fail();
    } catch (UnsupportedOperationException expected) {
    }

    try {
      secDb.putNoOverwrite(txn, key, data);
      fail();
    } catch (UnsupportedOperationException expected) {
    }

    try {
      secDb.putNoDupData(txn, key, data);
      fail();
    } catch (UnsupportedOperationException expected) {
    }

    try {
      secDb.truncate(txn, true);
      fail();
    } catch (UnsupportedOperationException expected) {
    }

    try {
      secDb.join(new Cursor[0], null);
      fail();
    } catch (UnsupportedOperationException expected) {
    }

    /* Cursor operations. */

    txnCommit(txn);
    txn = txnBeginCursor();

    SecondaryCursor cursor = null;
    try {
      cursor = secDb.openSecondaryCursor(txn, null);

      try {
        cursor.getSearchBoth(key, data, LockMode.DEFAULT);
        fail();
      } catch (UnsupportedOperationException expected) {
      }

      try {
        cursor.getSearchBothRange(key, data, LockMode.DEFAULT);
        fail();
      } catch (UnsupportedOperationException expected) {
      }

      try {
        cursor.putCurrent(data);
        fail();
      } catch (UnsupportedOperationException expected) {
      }

      try {
        cursor.put(key, data);
        fail();
      } catch (UnsupportedOperationException expected) {
      }

      try {
        cursor.putNoOverwrite(key, data);
        fail();
      } catch (UnsupportedOperationException expected) {
      }

      try {
        cursor.putNoDupData(key, data);
        fail();
      } catch (UnsupportedOperationException expected) {
      }
    } finally {
      if (cursor != null) {
        cursor.close();
      }
    }

    txnCommit(txn);
    secDb.close();
    priDb.close();

    /* Primary with duplicates. */
    priDb = openDatabase(true, "testDBWithDups", false);
    try {
      openSecondary(priDb, true, "testSecDB", false, false);
      fail();
    } catch (IllegalArgumentException expected) {
    }

    priDb.close();

    /* Single secondary with two primaries.*/
    Database pri1 = openDatabase(false, "pri1", false);
    Database pri2 = openDatabase(false, "pri2", false);
    Database sec1 = openSecondary(pri1, false, "sec", false, false);
    try {
      openSecondary(pri2, false, "sec", false, false);
      fail();
    } catch (IllegalArgumentException expected) {
    }
    sec1.close();
    pri1.close();
    pri2.close();
  }
示例#12
0
  public void testUniqueSecondaryKey() throws DatabaseException {

    Database priDb = openDatabase(false, "testDB", false);
    SecondaryDatabase secDb = openSecondary(priDb, false, "testSecDB", false, false);
    DatabaseEntry key;
    DatabaseEntry data;
    DatabaseEntry pkey = new DatabaseEntry();
    Transaction txn;

    /* Put {0, 0} */
    txn = txnBegin();
    key = entry(0);
    data = entry(0);
    priDb.put(txn, key, data);
    txnCommit(txn);
    assertEquals(OperationStatus.SUCCESS, secDb.get(null, entry(0 + KEY_OFFSET), pkey, data, null));
    assertEquals(0, TestUtils.getTestVal(pkey.getData()));
    assertEquals(0, TestUtils.getTestVal(data.getData()));

    /* Put {1, 1} */
    txn = txnBegin();
    key = entry(1);
    data = entry(1);
    priDb.put(txn, key, data);
    txnCommit(txn);
    assertEquals(OperationStatus.SUCCESS, secDb.get(null, entry(1 + KEY_OFFSET), pkey, data, null));
    assertEquals(1, TestUtils.getTestVal(pkey.getData()));
    assertEquals(1, TestUtils.getTestVal(data.getData()));

    /* Put {2, 0} */
    txn = txnBegin();
    key = entry(2);
    data = entry(0);
    try {
      priDb.put(txn, key, data);
      /* Expect exception because secondary key must be unique. */
      fail();
    } catch (DatabaseException e) {
      txnAbort(txn);
      /* Ensure that primary record was not inserted. */
      assertEquals(OperationStatus.NOTFOUND, secDb.get(null, key, data, null));
      /* Ensure that secondary record has not changed. */
      assertEquals(
          OperationStatus.SUCCESS, secDb.get(null, entry(0 + KEY_OFFSET), pkey, data, null));
      assertEquals(0, TestUtils.getTestVal(pkey.getData()));
      assertEquals(0, TestUtils.getTestVal(data.getData()));
    }

    /* Overwrite {1, 1} */
    txn = txnBegin();
    key = entry(1);
    data = entry(1);
    priDb.put(txn, key, data);
    txnCommit(txn);
    assertEquals(OperationStatus.SUCCESS, secDb.get(null, entry(1 + KEY_OFFSET), pkey, data, null));
    assertEquals(1, TestUtils.getTestVal(pkey.getData()));
    assertEquals(1, TestUtils.getTestVal(data.getData()));

    /* Modify secondary key to {1, 3} */
    txn = txnBegin();
    key = entry(1);
    data = entry(3);
    priDb.put(txn, key, data);
    txnCommit(txn);
    assertEquals(OperationStatus.SUCCESS, secDb.get(null, entry(3 + KEY_OFFSET), pkey, data, null));
    assertEquals(1, TestUtils.getTestVal(pkey.getData()));
    assertEquals(3, TestUtils.getTestVal(data.getData()));

    secDb.close();
    priDb.close();
  }
示例#13
0
  public void testPutAndDelete() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();

    DatabaseEntry data = new DatabaseEntry();
    DatabaseEntry key = new DatabaseEntry();
    OperationStatus status;
    Transaction txn = txnBegin();

    /* Database.put() */
    status = priDb.put(txn, entry(1), entry(2));
    assertSame(OperationStatus.SUCCESS, status);
    status = secDb.get(txn, entry(102), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.SUCCESS, status);
    assertDataEquals(entry(1), key);
    assertDataEquals(entry(2), data);

    /* Database.putNoOverwrite() */
    status = priDb.putNoOverwrite(txn, entry(1), entry(1));
    assertSame(OperationStatus.KEYEXIST, status);
    status = secDb.get(txn, entry(102), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.SUCCESS, status);
    assertDataEquals(entry(1), key);
    assertDataEquals(entry(2), data);

    /* Database.put() overwrite */
    status = priDb.put(txn, entry(1), entry(3));
    assertSame(OperationStatus.SUCCESS, status);
    status = secDb.get(txn, entry(102), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.NOTFOUND, status);
    status = secDb.get(txn, entry(103), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.SUCCESS, status);
    assertDataEquals(entry(1), key);
    assertDataEquals(entry(3), data);

    /* Database.delete() */
    status = priDb.delete(txn, entry(1));
    assertSame(OperationStatus.SUCCESS, status);
    status = priDb.delete(txn, entry(1));
    assertSame(OperationStatus.NOTFOUND, status);
    status = secDb.get(txn, entry(103), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.NOTFOUND, status);

    /* SecondaryDatabase.delete() */
    status = priDb.put(txn, entry(1), entry(1));
    assertSame(OperationStatus.SUCCESS, status);
    status = priDb.put(txn, entry(2), entry(1));
    assertSame(OperationStatus.SUCCESS, status);
    status = secDb.get(txn, entry(101), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.SUCCESS, status);
    assertDataEquals(entry(1), key);
    assertDataEquals(entry(1), data);
    status = secDb.delete(txn, entry(101));
    assertSame(OperationStatus.SUCCESS, status);
    status = secDb.delete(txn, entry(101));
    assertSame(OperationStatus.NOTFOUND, status);
    status = secDb.get(txn, entry(101), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.NOTFOUND, status);
    status = priDb.get(txn, entry(1), data, LockMode.DEFAULT);
    assertSame(OperationStatus.NOTFOUND, status);
    status = priDb.get(txn, entry(2), data, LockMode.DEFAULT);
    assertSame(OperationStatus.NOTFOUND, status);

    /*
     * Database.putNoDupData() cannot be called since the primary cannot be
     * configured for duplicates.
     */

    /* Primary and secondary are empty now. */

    /* Get a txn for a cursor. */
    txnCommit(txn);
    txn = txnBeginCursor();

    Cursor priCursor = null;
    SecondaryCursor secCursor = null;
    try {
      priCursor = priDb.openCursor(txn, null);
      secCursor = secDb.openSecondaryCursor(txn, null);

      /* Cursor.putNoOverwrite() */
      status = priCursor.putNoOverwrite(entry(1), entry(2));
      assertSame(OperationStatus.SUCCESS, status);
      status = secCursor.getSearchKey(entry(102), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(1), key);
      assertDataEquals(entry(2), data);

      /* Cursor.putCurrent() */
      status = priCursor.putCurrent(entry(3));
      assertSame(OperationStatus.SUCCESS, status);
      status = secCursor.getSearchKey(entry(102), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);
      status = secCursor.getSearchKey(entry(103), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(1), key);
      assertDataEquals(entry(3), data);

      /* Cursor.delete() */
      status = priCursor.delete();
      assertSame(OperationStatus.SUCCESS, status);
      status = priCursor.delete();
      assertSame(OperationStatus.KEYEMPTY, status);
      status = secCursor.getSearchKey(entry(103), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);
      status = priCursor.getSearchKey(entry(1), data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);

      /* Cursor.put() */
      status = priCursor.put(entry(1), entry(4));
      assertSame(OperationStatus.SUCCESS, status);
      status = secCursor.getSearchKey(entry(104), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(1), key);
      assertDataEquals(entry(4), data);

      /* SecondaryCursor.delete() */
      status = secCursor.delete();
      assertSame(OperationStatus.SUCCESS, status);
      status = secCursor.delete();
      assertSame(OperationStatus.KEYEMPTY, status);
      status = secCursor.getCurrent(new DatabaseEntry(), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.KEYEMPTY, status);
      status = secCursor.getSearchKey(entry(104), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);
      status = priCursor.getSearchKey(entry(1), data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);

      /*
       * Cursor.putNoDupData() cannot be called since the primary cannot
       * be configured for duplicates.
       */

      /* Primary and secondary are empty now. */
    } finally {
      if (secCursor != null) {
        secCursor.close();
      }
      if (priCursor != null) {
        priCursor.close();
      }
    }

    txnCommit(txn);
    secDb.close();
    priDb.close();
  }
示例#14
0
  public void testGet() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();

    DatabaseEntry data = new DatabaseEntry();
    DatabaseEntry key = new DatabaseEntry();
    DatabaseEntry secKey = new DatabaseEntry();
    OperationStatus status;
    Transaction txn = txnBegin();

    /*
     * For parameters that do not require initialization with a non-null
     * data array, we set them to null to make sure this works. [#12121]
     */

    /* Add one record for each key with one data/duplicate. */
    for (int i = 0; i < NUM_RECS; i += 1) {
      status = priDb.put(txn, entry(i), entry(i));
      assertSame(OperationStatus.SUCCESS, status);
    }

    /* SecondaryDatabase.get() */
    for (int i = 0; i < NUM_RECS; i += 1) {

      data.setData(null);
      status = secDb.get(txn, entry(i + KEY_OFFSET), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(i), key);
      assertDataEquals(entry(i), data);
    }
    data.setData(null);
    status = secDb.get(txn, entry(NUM_RECS + KEY_OFFSET), key, data, LockMode.DEFAULT);
    assertSame(OperationStatus.NOTFOUND, status);

    /* SecondaryDatabase.getSearchBoth() */
    for (int i = 0; i < NUM_RECS; i += 1) {
      data.setData(null);
      status = secDb.getSearchBoth(txn, entry(i + KEY_OFFSET), entry(i), data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(i), data);
    }
    data.setData(null);
    status =
        secDb.getSearchBoth(
            txn, entry(NUM_RECS + KEY_OFFSET), entry(NUM_RECS), data, LockMode.DEFAULT);
    assertSame(OperationStatus.NOTFOUND, status);

    /* Get a cursor txn. */
    txnCommit(txn);
    txn = txnBeginCursor();

    SecondaryCursor cursor = secDb.openSecondaryCursor(txn, null);
    try {
      /* SecondaryCursor.getFirst()/getNext() */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getFirst(secKey, key, data, LockMode.DEFAULT);
      for (int i = 0; i < NUM_RECS; i += 1) {
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getNext(secKey, key, data, LockMode.DEFAULT);
      }
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getCurrent() (last) */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getCurrent(secKey, key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(NUM_RECS - 1 + KEY_OFFSET), secKey);
      assertDataEquals(entry(NUM_RECS - 1), key);
      assertDataEquals(entry(NUM_RECS - 1), data);
      assertPriLocked(priDb, key);

      /* SecondaryCursor.getLast()/getPrev() */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getLast(secKey, key, data, LockMode.DEFAULT);
      for (int i = NUM_RECS - 1; i >= 0; i -= 1) {
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getPrev(secKey, key, data, LockMode.DEFAULT);
      }
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getCurrent() (first) */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getCurrent(secKey, key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(0 + KEY_OFFSET), secKey);
      assertDataEquals(entry(0), key);
      assertDataEquals(entry(0), data);
      assertPriLocked(priDb, key);

      /* SecondaryCursor.getSearchKey() */
      key.setData(null);
      data.setData(null);
      status = cursor.getSearchKey(entry(KEY_OFFSET - 1), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);
      for (int i = 0; i < NUM_RECS; i += 1) {
        key.setData(null);
        data.setData(null);
        status = cursor.getSearchKey(entry(i + KEY_OFFSET), key, data, LockMode.DEFAULT);
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key);
      }
      key.setData(null);
      data.setData(null);
      status = cursor.getSearchKey(entry(NUM_RECS + KEY_OFFSET), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getSearchBoth() */
      data.setData(null);
      status = cursor.getSearchKey(entry(KEY_OFFSET - 1), entry(0), data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);
      for (int i = 0; i < NUM_RECS; i += 1) {
        data.setData(null);
        status = cursor.getSearchBoth(entry(i + KEY_OFFSET), entry(i), data, LockMode.DEFAULT);
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, entry(i));
      }
      data.setData(null);
      status =
          cursor.getSearchBoth(
              entry(NUM_RECS + KEY_OFFSET), entry(NUM_RECS), data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getSearchKeyRange() */
      key.setData(null);
      data.setData(null);
      status = cursor.getSearchKeyRange(entry(KEY_OFFSET - 1), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(0), key);
      assertDataEquals(entry(0), data);
      assertPriLocked(priDb, key);
      for (int i = 0; i < NUM_RECS; i += 1) {
        key.setData(null);
        data.setData(null);
        status = cursor.getSearchKeyRange(entry(i + KEY_OFFSET), key, data, LockMode.DEFAULT);
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key);
      }
      key.setData(null);
      data.setData(null);
      status = cursor.getSearchKeyRange(entry(NUM_RECS + KEY_OFFSET), key, data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getSearchBothRange() */
      data.setData(null);
      status = cursor.getSearchBothRange(entry(1 + KEY_OFFSET), entry(1), data, LockMode.DEFAULT);
      assertSame(OperationStatus.SUCCESS, status);
      assertDataEquals(entry(1), data);
      assertPriLocked(priDb, entry(1));
      for (int i = 0; i < NUM_RECS; i += 1) {
        data.setData(null);
        status = cursor.getSearchBothRange(entry(i + KEY_OFFSET), entry(i), data, LockMode.DEFAULT);
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, entry(i));
      }
      data.setData(null);
      status =
          cursor.getSearchBothRange(
              entry(NUM_RECS + KEY_OFFSET), entry(NUM_RECS), data, LockMode.DEFAULT);
      assertSame(OperationStatus.NOTFOUND, status);

      /* Add one duplicate for each key. */
      Cursor priCursor = priDb.openCursor(txn, null);
      try {
        for (int i = 0; i < NUM_RECS; i += 1) {
          status = priCursor.put(entry(i + KEY_OFFSET), entry(i));
          assertSame(OperationStatus.SUCCESS, status);
        }
      } finally {
        priCursor.close();
      }

      /* SecondaryCursor.getNextDup() */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getFirst(secKey, key, data, LockMode.DEFAULT);
      for (int i = 0; i < NUM_RECS; i += 1) {
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key, data);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getNextDup(secKey, key, data, LockMode.DEFAULT);
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i + KEY_OFFSET), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key, data);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getNextDup(secKey, key, data, LockMode.DEFAULT);
        assertSame(OperationStatus.NOTFOUND, status);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getNext(secKey, key, data, LockMode.DEFAULT);
      }
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getNextNoDup() */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getFirst(secKey, key, data, LockMode.DEFAULT);
      for (int i = 0; i < NUM_RECS; i += 1) {
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key, data);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getNextNoDup(secKey, key, data, LockMode.DEFAULT);
      }
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getPrevDup() */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getLast(secKey, key, data, LockMode.DEFAULT);
      for (int i = NUM_RECS - 1; i >= 0; i -= 1) {
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i + KEY_OFFSET), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key, data);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getPrevDup(secKey, key, data, LockMode.DEFAULT);
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key, data);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getPrevDup(secKey, key, data, LockMode.DEFAULT);
        assertSame(OperationStatus.NOTFOUND, status);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getPrev(secKey, key, data, LockMode.DEFAULT);
      }
      assertSame(OperationStatus.NOTFOUND, status);

      /* SecondaryCursor.getPrevNoDup() */
      secKey.setData(null);
      key.setData(null);
      data.setData(null);
      status = cursor.getLast(secKey, key, data, LockMode.DEFAULT);
      for (int i = NUM_RECS - 1; i >= 0; i -= 1) {
        assertSame(OperationStatus.SUCCESS, status);
        assertDataEquals(entry(i + KEY_OFFSET), secKey);
        assertDataEquals(entry(i + KEY_OFFSET), key);
        assertDataEquals(entry(i), data);
        assertPriLocked(priDb, key, data);
        secKey.setData(null);
        key.setData(null);
        data.setData(null);
        status = cursor.getPrevNoDup(secKey, key, data, LockMode.DEFAULT);
      }
      assertSame(OperationStatus.NOTFOUND, status);
    } finally {
      cursor.close();
    }

    txnCommit(txn);
    secDb.close();
    priDb.close();
  }
示例#15
0
  /**
   * Test that an exception is thrown when a cursor is used in the wrong state. No put or get is
   * allowed in the closed state, and certain gets and puts are not allowed in the uninitialized
   * state.
   */
  public void testCursorState() throws DatabaseException {

    SecondaryDatabase secDb = initDb();
    Database priDb = secDb.getPrimaryDatabase();
    Transaction txn = txnBegin();

    DatabaseEntry key = entry(0);
    DatabaseEntry data = entry(0);
    DatabaseEntry secKey = entry(KEY_OFFSET);
    DatabaseEntry found = new DatabaseEntry();
    DatabaseEntry found2 = new DatabaseEntry();

    assertEquals(OperationStatus.SUCCESS, priDb.put(txn, key, data));

    txnCommit(txn);
    txn = txnBeginCursor();
    Cursor cursor = priDb.openCursor(txn, null);
    SecondaryCursor secCursor = secDb.openSecondaryCursor(txn, null);

    /* Check the uninitialized state for certain operations. */

    try {
      cursor.count();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.delete();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.putCurrent(data);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getCurrent(key, data, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getNextDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getPrevDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }

    try {
      secCursor.count();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.delete();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getCurrent(key, data, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getNextDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getPrevDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }

    /* Initialize, then close, then check all operations. */

    assertEquals(OperationStatus.SUCCESS, cursor.getSearchKey(key, found, null));
    assertEquals(OperationStatus.SUCCESS, secCursor.getSearchKey(secKey, found, null));
    secCursor.close();
    cursor.close();

    try {
      cursor.close();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.count();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.delete();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.put(key, data);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.putNoOverwrite(key, data);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.putNoDupData(key, data);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.putCurrent(data);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getCurrent(key, data, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getSearchKey(key, found, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getSearchBoth(key, data, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getSearchKeyRange(key, found, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getSearchBothRange(key, data, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getFirst(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getNext(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getPrev(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getNextDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getPrevDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getNextNoDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getPrevNoDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      cursor.getLast(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }

    try {
      secCursor.close();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.count();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.delete();
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getCurrent(key, data, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getSearchKey(secKey, found, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getSearchKeyRange(secKey, found, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getFirst(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getNext(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getPrev(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getNextDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getPrevDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getNextNoDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getPrevNoDup(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }
    try {
      secCursor.getLast(found, found2, null);
      fail();
    } catch (DatabaseException expected) {
    }

    txnCommit(txn);
    secDb.close();
    priDb.close();
    env.close();
    env = null;
  }
  /** Insert or retrieve data. */
  public void run() throws DatabaseException {

    /* Create a new, transactional database environment. */
    EnvironmentConfig envConfig = new EnvironmentConfig();
    envConfig.setTransactional(true);
    envConfig.setAllowCreate(true);
    Environment exampleEnv = new Environment(envDir, envConfig);

    /*
     * Make a database within that environment. Because this will be used
     * as a primary database, it must not allow duplicates. The primary key
     * of a primary database must be unique.
     */
    Transaction txn = exampleEnv.beginTransaction(null, null);
    DatabaseConfig dbConfig = new DatabaseConfig();
    dbConfig.setTransactional(true);
    dbConfig.setAllowCreate(true);
    Database exampleDb = exampleEnv.openDatabase(txn, "bindingsDb", dbConfig);

    /*
     * In our example, the database record is composed of an integer key
     * and and instance of the MyData class as data.
     *
     * A class catalog database is needed for storing class descriptions
     * for the serial binding used below.  This avoids storing class
     * descriptions redundantly in each record.
     */
    DatabaseConfig catalogConfig = new DatabaseConfig();
    catalogConfig.setTransactional(true);
    catalogConfig.setAllowCreate(true);
    Database catalogDb = exampleEnv.openDatabase(txn, "catalogDb", catalogConfig);
    StoredClassCatalog catalog = new StoredClassCatalog(catalogDb);

    /*
     * Create a serial binding for MyData data objects.  Serial
     * bindings can be used to store any Serializable object.
     */
    EntryBinding<MyData> dataBinding = new SerialBinding<MyData>(catalog, MyData.class);

    /*
     * Further below we'll use a tuple binding (IntegerBinding
     * specifically) for integer keys.  Tuples, unlike serialized
     * Java objects, have a well defined sort order.
     */

    /*
     * Define a String tuple binding for a secondary key.  The
     * secondary key is the msg field of the MyData object.
     */
    EntryBinding<String> secKeyBinding = TupleBinding.getPrimitiveBinding(String.class);

    /*
     * Open a secondary database to allow accessing the primary
     * database by the secondary key value.
     */
    SecondaryConfig secConfig = new SecondaryConfig();
    secConfig.setTransactional(true);
    secConfig.setAllowCreate(true);
    secConfig.setSortedDuplicates(true);
    secConfig.setKeyCreator(new MyKeyCreator(secKeyBinding, dataBinding));
    SecondaryDatabase exampleSecDb =
        exampleEnv.openSecondaryDatabase(txn, "bindingsSecDb", exampleDb, secConfig);
    txn.commit();

    /* DatabaseEntry represents the key and data of each record. */
    DatabaseEntry keyEntry = new DatabaseEntry();
    DatabaseEntry dataEntry = new DatabaseEntry();

    if (doInsert) {

      /*
       * Put some data in.  Note that the primary database is always used
       * to add data.  Adding or changing data in the secondary database
       * is not allowed; however, deleting through the secondary database
       * is allowed.
       */
      for (int i = offset; i < numRecords + offset; i++) {
        txn = exampleEnv.beginTransaction(null, null);
        StringBuffer stars = new StringBuffer();
        for (int j = 0; j < i; j++) {
          stars.append('*');
        }
        MyData data = new MyData(i, stars.toString());

        IntegerBinding.intToEntry(i, keyEntry);
        dataBinding.objectToEntry(data, dataEntry);

        OperationStatus status = exampleDb.put(txn, keyEntry, dataEntry);

        /*
         * Note that put will throw a DatabaseException when error
         * conditions are found such as deadlock.  However, the status
         * return conveys a variety of information. For example, the
         * put might succeed, or it might not succeed if the record
         * exists and duplicates were not
         */
        if (status != OperationStatus.SUCCESS) {
          throw new RuntimeException("Data insertion got status " + status);
        }
        txn.commit();
      }
    } else {

      /*
       * Retrieve the data by secondary key by opening a cursor on the
       * secondary database.  The key parameter for a secondary cursor is
       * always the secondary key, but the data parameter is always the
       * data of the primary database.  You can cast the cursor to a
       * SecondaryCursor and use additional method signatures for
       * retrieving the primary key also.  Or you can call
       * openSecondaryCursor() to avoid casting.
       */
      txn = exampleEnv.beginTransaction(null, null);
      Cursor cursor = exampleSecDb.openCursor(txn, null);

      while (cursor.getNext(keyEntry, dataEntry, LockMode.DEFAULT) == OperationStatus.SUCCESS) {

        String key = secKeyBinding.entryToObject(keyEntry);
        MyData data = dataBinding.entryToObject(dataEntry);

        System.out.println("key=" + key + " data=" + data);
      }
      cursor.close();
      txn.commit();
    }

    /*
     * Always close secondary databases before closing their associated
     * primary database.
     */
    catalogDb.close();
    exampleSecDb.close();
    exampleDb.close();
    exampleEnv.close();
  }