/** * Opens a cursor for a given database, dup'ing an existing CDB cursor if one is open for the * current thread. */ Cursor openCursor(Database db, CursorConfig cursorConfig, boolean writeCursor, Transaction txn) throws DatabaseException { if (cdbMode) { CdbCursors cdbCursors = null; WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get(); if (cdbCursorsMap == null) { cdbCursorsMap = new WeakHashMap(); localCdbCursors.set(cdbCursorsMap); } else { cdbCursors = (CdbCursors) cdbCursorsMap.get(db); } if (cdbCursors == null) { cdbCursors = new CdbCursors(); cdbCursorsMap.put(db, cdbCursors); } /* * In CDB mode the cursorConfig specified by the user is ignored * and only the writeCursor parameter is honored. This is the only * meaningful cursor attribute for CDB, and here we count on * writeCursor flag being set correctly by the caller. */ List cursors; CursorConfig cdbConfig; if (writeCursor) { if (cdbCursors.readCursors.size() > 0) { /* * Although CDB allows opening a write cursor when a read * cursor is open, a self-deadlock will occur if a write is * attempted for a record that is read-locked; we should * avoid self-deadlocks at all costs */ throw new IllegalStateException("cannot open CDB write cursor when read cursor is open"); } cursors = cdbCursors.writeCursors; cdbConfig = new CursorConfig(); DbCompat.setWriteCursor(cdbConfig, true); } else { cursors = cdbCursors.readCursors; cdbConfig = null; } Cursor cursor; if (cursors.size() > 0) { Cursor other = ((Cursor) cursors.get(0)); cursor = other.dup(false); } else { cursor = db.openCursor(null, cdbConfig); } cursors.add(cursor); return cursor; } else { return db.openCursor(txn, cursorConfig); } }
/** * Inserts an entity and returns null, or updates it if the primary key already exists and returns * the existing entity. * * <p>If a {@link PrimaryKey#sequence} is used and the primary key field of the given entity is * null or zero, this method will assign the next value from the sequence to the primary key field * of the given entity. * * @param txn the transaction used to protect this operation, null to use auto-commit, or null if * the store is non-transactional. * @param entity the entity to be inserted or updated. * @return the existing entity that was updated, or null if the entity was inserted. * @throws DatabaseException the base class for all BDB exceptions. */ public E put(Transaction txn, E entity) throws DatabaseException { DatabaseEntry keyEntry = new DatabaseEntry(); DatabaseEntry dataEntry = new DatabaseEntry(); assignKey(entity, keyEntry); boolean autoCommit = false; Environment env = db.getEnvironment(); if (transactional && txn == null && DbCompat.getThreadTransaction(env) == null) { txn = env.beginTransaction(null, getAutoCommitTransactionConfig()); autoCommit = true; } CursorConfig cursorConfig = null; if (concurrentDB) { cursorConfig = new CursorConfig(); DbCompat.setWriteCursor(cursorConfig, true); } boolean failed = true; Cursor cursor = db.openCursor(txn, cursorConfig); LockMode lockMode = locking ? LockMode.RMW : null; try { while (true) { OperationStatus status = cursor.getSearchKey(keyEntry, dataEntry, lockMode); if (status == OperationStatus.SUCCESS) { E existing = entityBinding.entryToObject(keyEntry, dataEntry); entityBinding.objectToData(entity, dataEntry); cursor.put(keyEntry, dataEntry); failed = false; return existing; } else { entityBinding.objectToData(entity, dataEntry); status = cursor.putNoOverwrite(keyEntry, dataEntry); if (status != OperationStatus.KEYEXIST) { failed = false; return null; } } } } finally { cursor.close(); if (autoCommit) { if (failed) { txn.abort(); } else { txn.commit(); } } } }