首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

J2ME RMS 進階應用:構建基於 String 鍵值的靈活存儲框架,突破 RecordID 單一索引限制

     編輯:Java 程式設計入門

前言:RMS 是小型數據庫,但索引太「原始」

MIDP 標準持久化方案 RMS 以 RecordStore 為表,RecordID (int) 為主鍵,byte[] 為內容。**缺陷**:只能按 int ID 精確查找或全表掃描 byte[],無法用業務鍵 (如 "user.info", "settings") 直接訪問。本文設計 StorageHandler 框架,引入索引表實現 String 鍵值查找,配合 Storable 接口實現異構對象序列化。

一、架構設計:三層協作

+------------------+     +------------------+     +------------------+
|  業務層          |     |  StorageHandler  |     |  RMS 底層        |
|  (UserInfo,      |---->|  (單例門面)       |---->|  (RecordStore)   |
|   Settings, ...) |     |                  |     |                  |
|  實現 Storable   |     |  - indexStore    |     |  - indexStore    |
|                  |     |  - dataStore(s)  |     |  - dataStore(s)  |
+------------------+     +------------------+     +------------------+
        ^                        ^
        | getKey()               | IndexEntry 映射
        | getData()/setData()    | String Key ↔ RecordID

二、核心組件詳解

1. 索引條目

class IndexEntry {
    private int selfId;    // 索引記錄自身 ID
    private int recordId;  // 指向數據記錄的 ID
    private String key;    // 業務鍵 (如 "user.info")
    
    // 序列化/反序列化方法省略
}

2. 查找流程 (三步走)

  1. 在 indexStore 中按 key 查找 IndexEntry (啟動時載入 Vector 內存索引,O(1) 查找)
  2. 取出 recordId
  3. 按 recordId 從對應 dataStore 讀取 byte[],反序列化為業務對象

3. Storable 接口:統一序列化契約

public interface Storable {
    String getKey();                              // 業務唯一鍵
    void getData(DataOutputStream out) throws IOException; // 序列化
    void setData(DataInputStream in) throws IOException;   // 反序列化
}

採用 DataOutputStream/DataInputStream 而非 Java 序列化:體積小、速度快、無 serialVersionUID 煩惱、跨語言友好。

三、業務對象實現示例

public class UserInfo implements Storable {
    private String username;
    private String password;
    private boolean autoLogin;

    @Override
    public String getKey() { return "user.info"; }

    @Override
    public void getData(DataOutputStream out) throws IOException {
        out.writeUTF(username);
        out.writeUTF(password);
        out.writeBoolean(autoLogin);
    }

    @Override
    public void setData(DataInputStream in) throws IOException {
        username = in.readUTF();
        password = in.readUTF();
        autoLogin = in.readBoolean();
    }
    // getter/setter 省略
}

四、StorageHandler 核心邏輯

public class StorageHandler {
    private static StorageHandler instance = new StorageHandler();
    private RecordStore indexStore;
    private RecordStore dataStore;
    private Vector indexCache; // 內存索引

    private StorageHandler() {
        try {
            indexStore = RecordStore.openRecordStore("index", true);
            dataStore = RecordStore.openRecordStore("data", true);
            loadIndexCache(); // 啟動時全量載入
        } catch (RecordStoreException e) { /* 致命錯誤處理 */ }
    }

    public static StorageHandler getInstance() { return instance; }

    // 保存/更新
    public void save(Storable obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(baos);
            obj.getData(dos);
            dos.flush();
            byte[] data = baos.toByteArray();

            String key = obj.getKey();
            IndexEntry entry = findIndexEntry(key);
            if (entry == null) {
                // 新增
                int recordId = dataStore.addRecord(data, 0, data.length);
                entry = new IndexEntry();
                entry.key = key;
                entry.recordId = recordId;
                addIndexEntry(entry);
            } else {
                // 更新
                dataStore.setRecord(entry.recordId, data, 0, data.length);
            }
            syncIndexCache(); // 持久化索引變更
        } catch (Exception e) { /* 異常處理 */ }
    }

    // 讀取
    public void load(Storable obj) {
        try {
            IndexEntry entry = findIndexEntry(obj.getKey());
            if (entry != null) {
                byte[] data = dataStore.getRecord(entry.recordId);
                ByteArrayInputStream bais = new ByteArrayInputStream(data);
                DataInputStream dis = new DataInputStream(bais);
                obj.setData(dis);
            }
        } catch (Exception e) { /* 異常處理 */ }
    }

    // 刪除
    public void delete(String key) { /* 刪除數據記錄 + 索引記錄 + 更新緩存 */ }

    // 內存索引操作 (O(1) 查找)
    private IndexEntry findIndexEntry(String key) {
        for (IndexEntry e : indexCache) if (e.key.equals(key)) return e;
        return null;
    }
    // loadIndexCache, addIndexEntry, syncIndexCache 等輔助方法省略
}

五、關鍵設計決策權衡

六、擴展:多數據存儲分區

// 按業務域分離 RecordStore,降低單表大小、提升查找速度
private RecordStore userDataStore;   // 用戶相關
private RecordStore gameDataStore;   // 遊戲檔案
private RecordStore configDataStore; // 設置配置
// IndexEntry 新增 storeName 字段指向具體 Store

七、結語

StorageHandler + IndexEntry + Storable 三件套,將 RMS 從「只能按 int ID 讀寫 byte[]」的原始狀態,升級為**面向業務鍵值、支持異構對象、內存索引加速**的靈活存儲框架。這正是 J2ME 開發中「在約束中創造自由」的典範:用有限的 RMS 原語,構建出類似嵌入式 Key-Value 數據庫 (如 LevelDB/RocksDB 簡化版) 的使用體驗。建議在項目啟動期引入此框架,統一持久化入口,避免業務代碼散落 RMS 細節。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
決策點方案理由
索引存儲獨立 RecordStore ("index")職責分離,索引小可全量緩存
內存索引結構Vector 線性查找索引量通常 < 100,線性足夠;若大量可換 HashMap
序列化格式DataOutputStream (UTF/Boolean/Int)J2ME 標準、無反射、字節序固定、體積最小
異構對象支持Storable 接口 + 不同 RecordStore每類對象可獨立數據存儲,互不干擾
併發安全單線程 MIDlet 模型天然安全若多線程需加 synchronized