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

Java ME RMS 通用持久化框架設計:Serializable/Lazy/RecordFetcher/RMSUtil 四層架構

     編輯:J2ME

Java ME 無反射、無 Serializable、無自動序列化,直接操作 byte[] 極易出錯。本文提出一套輕量持久化框架,四大核心介面/類:

  • Serializable:定義 serialize()/unserialize()/getId()/setId(),類似 Java SE 標記介面但含契約方法。
  • Lazy extends Serializable:大物件分片存儲支援,getAttach(key)/savedAttach(key,id)/getAttachKeys()/getNameOfAttachRMS()。
  • RecordFetcher extends RecordFilter, RecordComparator:查詢參數封裝,含分頁 getFromIndex()/getMaxRecordSize()。
  • RMSUtil:單例緩存 RecordStore,提供 insert/update/delete/read/list 全套靜態方法,自動處理 Lazy 附件分庫存儲。

核心介面定義

Serializable


public interface Serializable {
    byte[] serialize() throws IOException;
    void unSerialize(byte[] data) throws IOException;
    void setId(int id);
    int getId(); // 未持久化時回傳 -1
}

Lazy(大物件分片)


public interface Lazy extends Serializable {
    byte[] getAttach(Object key) throws IOException; // 取得分片字節陣列
    void savedAttach(Object key, int id);            // 儲存後回調,記錄分片 RMS ID
    Object[] getAttachKeys();                        // 所有分片鍵
    String getNameOfAttachRMS();                     // 分片存儲的 RecordStore 名稱
}

RecordFetcher(查詢封裝)


public interface RecordFetcher extends RecordFilter, RecordComparator {
    int getFromIndex();      // 起始索引(分頁)
    int getMaxRecordSize();  // 最大回傳條數
}

RMSUtil 核心功能

關鍵實現細節

RecordStore 緩存(避免重複開啟開銷)


private static Hashtable rmsCache = new Hashtable();

private static synchronized RecordStore getRecordStore(String name) throws RecordStoreException {
    if (name.length() > 32) name = name.substring(name.length() - 32); // RMS 名稱長度限制
    if (rmsCache.containsKey(name)) return (RecordStore) rmsCache.get(name);
    RecordStore rs = RecordStore.openRecordStore(name, true);
    rmsCache.put(name, rs);
    return rs;
}

Lazy 附件自動分庫存儲


private static boolean insertAttachDatas(Lazy lazy) {
    RecordStore rs = getRecordStore(lazy.getNameOfAttachRMS());
    Object[] keys = lazy.getAttachKeys();
    for (Object key : keys) {
        byte[] data = lazy.getAttach(key);
        int id = rs.addRecord(data, 0, data.length);
        lazy.savedAttach(key, id); // 回調物件記錄分片 ID
    }
    return true;
}

讀取時自動清理髒數據


public static Serializable readObject(int id, Class clz) {
    try {
        RecordStore rs = getRecordStore(clz.getName());
        byte[] data = rs.getRecord(id);
        Serializable ser = (Serializable) clz.newInstance();
        ser.unSerialize(data);
        ser.setId(id);
        return ser;
    } catch (Exception e) {
        // 反序列化失敗(版本不兼容/數據損壞)→ 刪除該記錄防止下次再失敗
        if (rs != null) { try { rs.deleteRecord(id); } catch (Exception ignored) {} }
        return null;
    }
}

使用範例


// 1. 定義實體實現 Serializable
public class User implements Serializable {
    private int id = -1;
    private String name;
    private int level;

    public byte[] serialize() {
        // 手動序列化:name 長度(2B) + name bytes + level(4B)
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        dos.writeUTF(name);
        dos.writeInt(level);
        return baos.toByteArray();
    }

    public void unSerialize(byte[] data) {
        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
        name = dis.readUTF();
        level = dis.readInt();
    }
    // getId/setId ...
}

// 2. 業務代碼
User u = new User(); u.name = "Hero"; u.level = 99;
RMSUtil.insertObject(u); // 自動分配 ID 並寫入
// ...
User loaded = (User) RMSUtil.readObject(u.getId(), User.class);

設計權衡與建議

  • 優點:零反射、零註解、編譯期類型安全、分片支援大物件、查詢介面靈活。
  • 缺點:需手寫序列化代碼(但比動態字節碼注入更可控、無方法名衝突風險)。
  • 適用場景:中大型專案、需跨版本升級、大物件(圖片、地圖)分片存儲。
  • 擴展建議:加入版本號欄位、支援 Schema 遷移、整合加密存儲。

框架依賴自製 LogManager,直接註釋相關行即可編譯。源碼約 380 行,無第三方依賴,適合直接複製進專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方法說明
insertObject(Serializable)插入物件,自動處理 Lazy 附件寫入
updateObject(Serializable)更新主記錄
deleteObject(Serializable)刪除主記錄與附件
readObject(id, Class)反序列化單個物件,失敗自動清理髒數據
listObjects(Class, from, maxSize)分頁列舉,可選 RecordFetcher 過濾/排序
getStoreSize(Class)該類記錄總數
closeAllRMS()MIDlet 銷毀時調用,關閉所有緩存的 RecordStore