From 0544eeb84e18789c4ef6591e5cc07dfaf6351b2b Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Sat, 31 Oct 2026 22:17:25 +0800 Subject: [PATCH] fix: preserve shared Berkeley database handle when clearing --- .../spring/boot/BerkeleyTemplate.java | 34 +++++++++++++++---- .../berkeley/spring/boot/BerkeleyTest.java | 26 +++++++++++--- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/sleepycat/berkeley/spring/boot/BerkeleyTemplate.java b/src/main/java/com/sleepycat/berkeley/spring/boot/BerkeleyTemplate.java index e8b804d..f51f257 100644 --- a/src/main/java/com/sleepycat/berkeley/spring/boot/BerkeleyTemplate.java +++ b/src/main/java/com/sleepycat/berkeley/spring/boot/BerkeleyTemplate.java @@ -161,12 +161,34 @@ public void otherMethod(){ List list = myDbEnvironment.getDatabaseNames();//取得当前环境下数据库名称列表 System.out.println(list); - // JE cannot truncate a database while its handle is open. Keep the - // template usable by reopening the same database after truncation. - com.sleepycat.je.DatabaseConfig config = myDatabase.getConfig(); - myDatabase.close(); - long deleteNum = env.truncateDatabase(null, databaseName, true); - myDatabase = env.openDatabase(null, databaseName, config); + // Delete in a transaction while keeping the shared Database handle open. + Transaction txn = env.beginTransaction(null, null); + Cursor cursor = null; + long deleteNum = 0; + boolean committed = false; + try { + cursor = myDatabase.openCursor(txn, null); + DatabaseEntry key = new DatabaseEntry(); + DatabaseEntry value = new DatabaseEntry(); + while (cursor.getNext(key, value, LockMode.DEFAULT) == OperationStatus.SUCCESS) { + cursor.delete(); + deleteNum++; + } + cursor.close(); + cursor = null; + txn.commit(); + committed = true; + } finally { + try { + if (cursor != null) { + cursor.close(); + } + } finally { + if (!committed) { + txn.abort(); + } + } + } System.out.println(deleteNum); diff --git a/src/test/java/com/sleepycat/berkeley/spring/boot/BerkeleyTest.java b/src/test/java/com/sleepycat/berkeley/spring/boot/BerkeleyTest.java index b23ea45..c71cdcd 100644 --- a/src/test/java/com/sleepycat/berkeley/spring/boot/BerkeleyTest.java +++ b/src/test/java/com/sleepycat/berkeley/spring/boot/BerkeleyTest.java @@ -11,9 +11,10 @@ import org.springframework.test.util.ReflectionTestUtils; import java.nio.file.Path; -import java.util.List; +import java.util.Arrays; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; class BerkeleyTest { @TempDir Path home; @@ -25,6 +26,7 @@ class BerkeleyTest { void openDatabase() { EnvironmentConfig envConfig = new EnvironmentConfig(); envConfig.setAllowCreate(true); + envConfig.setConfigParam("je.freeDisk", "0"); envConfig.setTransactional(true); environment = new Environment(home.toFile(), envConfig); DatabaseConfig dbConfig = new DatabaseConfig(); @@ -51,7 +53,7 @@ void writesReadsListsAndDeletesTransactionalRecords() throws Exception { template.writeToDatabase("key", "second", true); assertEquals("second", template.readFromDatabase("key")); template.writeToDatabase("other", "value", false); - assertEquals(List.of("key", "other"), template.getAllFromDatabase()); + assertEquals(Arrays.asList("key", "other"), template.getAllFromDatabase()); template.deleteFromDatabase("other"); assertEquals("", template.readFromDatabase("other")); } @@ -63,13 +65,29 @@ void storesPrimitiveValues() { assertNotEquals("", template.readFromDatabase("primitive")); } + @Test - void truncatesAndReopensTheDatabase() { + void clearsAllRecordsWithoutReplacingSharedHandle() { template.writeToDatabase("keep", "before", true); + template.writeToDatabase("second", "before", true); template.otherMethod(); - database = template.getDatabase(); + assertSame(database, template.getDatabase()); assertEquals("", template.readFromDatabase("keep")); + assertEquals("", template.readFromDatabase("second")); template.writeToDatabase("after", "value", true); assertEquals("value", template.readFromDatabase("after")); } + @Test + void failedClearLeavesTheSharedHandleUntouched() { + Database failing = mock(Database.class); + when(failing.getDatabaseName()).thenReturn("items"); + when(failing.getEnvironment()).thenReturn(environment); + when(failing.openCursor(any(com.sleepycat.je.Transaction.class), isNull())).thenThrow(new IllegalStateException("cursor unavailable")); + template.setDatabase(failing); + assertThrows(IllegalStateException.class, () -> template.otherMethod()); + assertSame(failing, template.getDatabase()); + template.setDatabase(database); + template.writeToDatabase("after-failure", "valid", true); + assertEquals("valid", template.readFromDatabase("after-failure")); + } }