程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> J2ME >> JAVAME的RMS通用持久化框架

JAVAME的RMS通用持久化框架

編輯:J2ME

  在寫JAVAME程序的時候,我們經常需要保存一些數據到手機裡面,也經常希望能把對象也保存到手機裡面,但是JAVAME裡面沒有反射機制,也沒有Java.io.Serializable接口,所以沒有序列化的機制,要保存對象的話,就得自己動手了。

  在JAVAME中,程序的數據保存的地方,無外乎兩種,一種是把數據保存在RMS裡面,這是所有的JAVAME的手機都支持的,還有一種就是把數據保存在手機的文件系統裡面,這個不是所有手機都能支持的,只有支持JSR075的手機,才支持把數據保存在文件系統裡面,並且如果你的程序沒有經過簽名的話,你每次保存或者讀取,手機都會彈出惱人的提示,是否允許程序訪問文件系統。所在我一般都是把數據存在RMS裡面,因為讀寫RMS是安全的,並且也是不需要手機提示的。因為我們的RMS數據是存在一個特殊的地方。但是JavaME的RMS功能非常底層,為了保存一些數據,我們必須和byte[]打交道,所以我就產生了,在此之前封裝一層自己的程序的想法,這樣封裝好以後,使用起來就非常方便了。只要實現了相關接口,就可以享受到比較易用的方法了。

  此框架總共包括了四個類,分別如下:

  Serializable類,它是一個接口,類似於JAVASE裡面的Serializable接口,唯一不同的就是,JavaSE裡面的接口是一個空接口,只做標記用的,而這裡的這個接口是有方法需要實現的。

  Lazy類,它也是一個接口,它定義了一些方法,如果你的對象比較大,需要惰性加載的時候,可以實現此接口,並且此接口是Serializable接口的子類,也就是說實現了Lazy接口,你就相當於實現了Serializable接口。

  RMSUtil類,此類是一個工具類,用於統一進行RMS的相關操作,也是此框架的核心類。

  RecordFetcher類,也是一個接口,它繼承了RecordComparator, RecordFilter接口,在取數據的時候,需要用到它。

  好了,下面我們就開始看代碼吧。

 1 /*
 2  * To change this template, choose Tools | Templates
 3  * and open the template in the editor.
 4  */
 5 package com.hadeslee.mobile.rms;
 6
 7 import Java.io.IOException;
 8
 9 /**
10  * 一個可自己串行化的類所要實現的接口
11  * @author hadeslee
12  */
13 public interface Serializable {
14
15     /**
16      * 把自己編碼成字節數組的格式
17      * @return 字節數組
18      */
19     public byte[] serialize() throws IOException;
20
21     /**
22      * 把一個對象用此字節數組進行重裝
23      * @param data 字節數組
24      */
25     public void unSerialize(byte[] data) throws IOException;
26
27     /**
28      * 設置此對象序列化後對應的存儲對象的ID
29      * @param id ID
30      */
31     public void setId(int id);
32
33     /**
34      * 得到此對象序列化後的ID
35      * 此方法唯有在反序列化後的對象上調用才有效
36      * 如果一個對象是沒有序列化的,那麼它的ID是-1;
37      * @return ID
38      */
39     public int getId();
40 }
41
 1 /*
 2  * To change this template, choose Tools | Templates
 3  * and open the template in the editor.
 4  */
 5 package com.hadeslee.mobile.rms;
 6
 7 import Java.io.IOException;
 8
 9 /**
10  * 可以延遲加載的對象必須要實現的接口
11  * @author binfeng.li
12  */
13 public interface Lazy extends Serializable {
14
15     /**
16      * 實現此接口的類要實現的方法
17      * 可以用於延遲加載某些屬性。比如
18      * get("ImgData"),get("fullImage")..等等
19      * 由於J2ME不支持注釋也不支持反射,所以只能以
20      * 此種方法來進行模擬了
21      * 此方法是RMSUtil要存對象的時候調用的,這樣就可以把
22      * 一個對象的不同部份存到不同的RMS裡面去了
23      * @param key 要得到的某性的鍵
24      * @return 其對應的值
25      * @throws IOException
26      */
27     public byte[] getAttach(Object key)throws IOException;
28
29     /**
30      * 當把某個附屬的對象保存進去以後,所要調用的
31      * 方法,此方法告訴主體,它的那個附件被保存後
32      * 在RMS裡面對應的ID是多少
33      * @param key
34      * @param id
35      */
36     public void savedAttach(Object key, int id);
37
38     /**
39      * 得到此對象所支持的所有的key的數組
40      * @return KEY的數組,不能為NULL
41      */
42     public Object[] getAttachKeys();
43
44     /**
45      * 此對象的附屬對象所存的RMS的名字
46      * @return RMS的名字
47      */
48     public String getNameOfAttachRMS();
49 }
50
 1 /*
 2  * To change this template, choose Tools | Templates
 3  * and open the template in the editor.
 4  */
 5 package com.hadeslee.mobile.rms;
 6
 7 import Javax.microedition.rms.RecordComparator;
 8 import Javax.microedition.rms.RecordFilter;
 9
10 /**
11  * 此類是一個繼承了兩個接口的接口,並且添加了自己
12  * 的方法,自己的方法是用於通知數量以及開始取的位置
13  * 只是為了方便於傳遞參數以及以後擴展
14  * @author binfeng.li
15  */
16 public interface RecordFetcher extends RecordComparator, RecordFilter {
17
18     /**
19      * 從哪個下標開始取
20      * @return 下標
21      */
22     public int getFromIndex();
23
24     /**
25      * 最多取多少條記錄
26      * @return 記錄
27      */
28     public int getMaxRecordSize();
29 }
30
  1 /*
  2  * To change this template, choose Tools | Templates
  3  * and open the template in the editor.
  4  */
  5 package com.hadeslee.mobile.rms;
  6
  7 import com.hadeslee.mobile.log.LogManager;
  8 import Java.util.Enumeration;
  9 import Java.util.Hashtable;
 10 import Java.util.Vector;
 11 import Javax.microedition.rms.RecordEnumeration;
 12 import Javax.microedition.rms.RecordStore;
 13 import Javax.microedition.rms.RecordStoreException;
 14
 15 /**
 16  * 一個專門用來操作RMS的工具類,通過這個類
 17  * 可以把RMS封裝起來,上層調用就更方便了
 18  * @author binfeng.li
 19  */
 20 public class RMSUtil {
 21
 22     /**
 23      * 用於緩存生命周期之內的所有的RecordStore的表,當MIDlet要退出的
 24      * 時候,調用此類的關閉方法,使RMS正確地被關閉
 25      */
 26     private static Hashtable rmsCache = new Hashtable();
 27
 28     private RMSUtil() {
 29     }
 30
 31     /**
 32      * 插入一個對象到一個RMS的數據庫裡面,如果此數據庫不存在
 33      * 則自動創建一個對於MIDlet私有的數據庫。如果存在,則直接
 34      * 插在此數據庫的最後面
 35      * @param ser 要插入的數據,必須是實現了Serializable接口的類
 36      * @return 是否插入成功
 37      */
 38     public static boolean insertObject(Serializable ser) {
 39         RecordStore rs = null;
 40         try {
 41             rs = getRecordStore(ser.getClass().getName());
 42             if (ser instanceof Lazy) {
 43                 Lazy lazy = (Lazy) ser;
 44                 insertAttachDatas(lazy);
 45             }
 46             byte[] data = ser.serialize();
 47             int id = rs.addRecord(data, 0, data.length);
 48             ser.setId(id);
 49             return true;
 50         } catch (Exception exe) {
 51             exe.printStackTrace();
 52             LogManager.error("RMSUtil.insertObject(),ser = " + ser + ",exe = " + exe);
 53             return false;
 54         }
 55     }
 56
 57     /**
 58      * 更新某個對象到RMS裡面去,
 59      * @param ser 要更新的對象
 60      * @return 是否成功
 61      */
 62     public static boolean updateObject(Serializable ser) {
 63         RecordStore rs = null;
 64         try {
 65             rs = getRecordStore(ser.getClass().getName());
 66             byte[] data = ser.serialize();
 67             rs.setRecord(ser.getId(), data, 0, data.length);
 68             return true;
 69         } catch (Exception exe) {
 70             exe.printStackTrace();
 71             LogManager.error("RMSUtil.updateObject(),ser = " + ser + ",exe = " + exe);
 72             return false;
 73         }
 74     }
 75
 76     /**
 77      * 從RMS裡面刪除某個對象
 78      * @param ser 要刪除的對象
 79      * @return 是否成功
 80      */
 81     public static boolean deleteObject(Serializable ser) {
 82         if (ser.getId() == -1) {
 83             return false;
 84         }
 85         RecordStore rs = null;
 86         try {
 87             rs = getRecordStore(ser.getClass().getName());
 88             int id = ser.getId();
 89             rs.deleteRecord(id);
 90             ser.setId(-1);
 91             return true;
 92         } catch (Exception exe) {
 93             exe.printStackTrace();
 94             LogManager.error("RMSUtil.deleteObject(),ser = " + ser + ",exe = " + exe);
 95             return false;
 96         }
 97     }
 98
 99     /**
100      * 從某個數據庫裡面讀取某個對象
101      * @param id 此對象的ID
102      * @param clz 對應的類
103      * @return 此對象,如果發生任何異常,則返回null
104      */
105     public static Serializable readObject(int id, Class clz) {
106         RecordStore rs = null;
107         try {
108             rs = getRecordStore(clz.getName());
109             byte[] data = rs.getRecord(id);
110             Serializable ser = (Serializable) clz.newInstance();
111             ser.unSerialize(data);
112             ser.setId(id);
113             return ser;
114         } catch (Exception exe) {
115             //如果讀取對象失敗,則可能是有東西被刪了或者版本不一樣,此時就應該刪掉
116             exe.printStackTrace();
117             LogManager.error("RMSUtil.readObject(),id = " + id + ",Class = " + clz + ",exe= " + exe);
118             if (rs != null) {
119                 try {
120                     rs.deleteRecord(id);
121                 } catch (Exception ex) {
122                     ex.printStackTrace();
123                     LogManager.error("RMSUtil.readObject$rs.deleteRecord(id),id = " + id + ",exe = " + ex);
124                 }
125             }
126             return null;
127         }
128     }
129
130     /**
131      * 得到某個類存在RMS裡面的總數,這樣便於分段取
132      * @param cls 類名
133      * @return 有效記錄總數
134      */
135     public static int getStoreSize(Class cls) {
136         try {
137             RecordStore rs = getRecordStore(cls.getName());
138             return rs.getNumRecords();
139         } catch (Exception exe) {
140             exe.printStackTrace();
141             LogManager.error("RMSUtil.getStoreSize(),Class = " + cls + ",exe = " + exe);
142             return -1;
143         }
144     }
145
146     /**
147      * 列出某個類的對象的集合,最多取多少個對象
148      * @param cls 類名
149      * @param from 從第幾個開始取
150      * @param maxSize 最多取多少個對象
151      * @return 取到的列表
152      */
153     public static Vector listObjects(Class cls, int from, int maxSize) {
154         System.out.println("class="+cls);
155         if (from < 0 || maxSize < 1) {
156             throw new IllegalArgumentException("from can not less than 0 and maxSize must greater than 0");
157         }
158         Vector v = new Vector();
159         RecordEnumeration ren = null;
160         try {
161             RecordStore rs = getRecordStore(cls.getName());
162             ren = rs.enumerateRecords(null, null, false);
163             fetchRecord(v, cls, ren, from, maxSize);
164         } catch (Exception exe) {
165             LogManager.error("RMSUtil.listObjects(),Class = " + cls + ",from = " + from + ",maxSize = " + maxSize + ",exe = " + exe);
166             exe.printStackTrace();
167         } finally {
168             ren.destroy();
169         }
170         return v;
171     }
172
173     /**
174      * 用於前面一個方法和後面一個方法的共用方法,
175      * 它用來從特定的記錄枚舉裡面去取特定的記錄,
176      * 並放到特定的地方
177      * @param v 要保存的地方
178      * @param cls 要實例化的類
179      * @param ren 記錄的枚舉
180      * @param from 從哪裡開始取
181      * @param maxSize 要取多少條記錄
182      * @throws Java.lang.Exception 可能會拋出的異常
183      */
184     private static void fetchRecord(Vector v, Class cls, RecordEnumeration ren, int from, int maxSize) throws Exception {
185         int index = 0;
186         int size = 0;
187         while (ren.hasNextElement()) {
188             int id = ren.nextRecordId();
189             if (index >= from) {
190                 if (size < maxSize) {
191                     Serializable ser = readObject(id, cls);
192                     if (ser != null) {
193                         v.addElement(ser);
194                         size++;
195                     }
196                 } else {
197                     break;
198                 }
199             }
200             index++;
201         }
202     }
203
204     /**
205      * 列出某個類的對象,並用一種過濾以及排序的方法來進行過濾或者排序
206      * @param cls 類名
207      * @param fetcher 取記錄的方法
208      * @return 記錄列表
209      */
210     public static Vector listObjects(Class cls, RecordFetcher fetcher) {
211         System.out.println("fetcher class="+cls);
212         int from = fetcher.getFromIndex();
213         int maxSize = fetcher.getMaxRecordSize();
214         if (from < 0 || maxSize < 1) {
215             throw new IllegalArgumentException("from can not less than 0 and maxSize must greater than 0");
216         }
217         Vector v = new Vector();
218         RecordEnumeration ren = null;
219         try {
220             RecordStore rs = getRecordStore(cls.getName());
221             ren = rs.enumerateRecords(fetcher, fetcher, false);
222             fetchRecord(v, cls, ren, from, maxSize);
223         } catch (Exception exe) {
224             LogManager.error("RMSUtil.listObjects(),Class = " + cls + ",exe = " + exe);
225             exe.printStackTrace();
226         } finally {
227             ren.destroy();
228         }
229         return v;
230     }
231
232     /**
233      * 插入某個可延遲加載的對象的所有附件到數據庫裡面去
234      * 插入完成後,此lazy對象將變得很完整,因為此時它的
235      * 附件對象的ID都已經設置好了
236      * @param lazy 要插入附件的主對象
237      * @return 是否插入成功
238      */
239     private static boolean insertAttachDatas(Lazy lazy) {
240         try {
241             Object[] attachKeys = lazy.getAttachKeys();
242             RecordStore rs = getRecordStore(lazy.getNameOfAttachRMS());
243             for (int i = 0; i < attachKeys.length; i++) {
244                 Object key = attachKeys[i];
245                 byte[] data = lazy.getAttach(key);
246                 int id = rs.addRecord(data, 0, data.length);
247                 lazy.savedAttach(key, id);
248             }
249             return true;
250         } catch (Exception exe) {
251             exe.printStackTrace();
252             LogManager.error("RMSUtil.insertAttachDatas(),Lazy = " + lazy + ",exe = " + exe);
253             return false;
254         }
255     }
256
257     /**
258      * 得到某個可以延遲加載的對象的某個附件的字節數組內容
259      * TODO 如果能把此方法變成私有,那就更好了
260      * 此方法是專門供lazy對象調用的,這樣的話,實體類裡面就出現了
261      * 讀取數據的方法,但是由於J2ME不支持反射,只能這樣實現了
262      * @param lazy 可以延遲加載的對象
263      * @param id 附件的ID
264      * @return 對應的數組
265      */
266     public static byte[] getAttachData(Lazy lazy, int id) {
267         try {
268             return getRecordStore(lazy.getNameOfAttachRMS()).getRecord(id);
269         } catch (Exception exe) {
270             exe.printStackTrace();
271             LogManager.error("RMSUtil.getAttachData(),Lazy = " + lazy + ",id = " + id + ",exe = " + exe);
272             return null;
273         }
274     }
275
276     /**
277      * 更新某個對象的附件
278      * TODO 如果能把此方法變成私有就更好了
279      * @param lazy 可延遲加載的對象
280      * @param id 附件的ID
281      * @param data 附件的內容
282      * @return 是否成功
283      */
284     public static boolean updateAttachData(Lazy lazy, int id, byte[] data) {
285         try {
286             RecordStore rs = getRecordStore(lazy.getNameOfAttachRMS());
287             rs.setRecord(id, data, 0, data.length);
288             return true;
289         } catch (Exception exe) {
290             exe.printStackTrace();
291             LogManager.error("RMSUtil.updateAttachData(),Lazy = " + lazy + ",exe = " + exe);
292             return false;
293         }
294     }
295
296     /**
297      * 從附件數據庫中刪除某個附件
298      * @param lazy 主對象
299      * @param id 附件的ID
300      * @return 是否刪除成功
301      */
302     public static boolean deleteAttachData(Lazy lazy, int id) {
303         try {
304             RecordStore rs = getRecordStore(lazy.getNameOfAttachRMS());
305             rs.deleteRecord(id);
306             return true;
307         } catch (Exception exe) {
308             exe.printStackTrace();
309             LogManager.error("RMSUtil.deleteAttachData(),Lazy = " + lazy + ",id = " + id + ",exe = " + exe);
310             return false;
311         }
312     }
313
314     /**
315      * 關閉所有的RMS
316      */
317     public static void closeAllRMS() {
318         Enumeration en = rmsCache.elements();
319         while (en.hasMoreElements()) {
320             RecordStore rs = (RecordStore) en.nextElement();
321             closeRecordStore(rs);
322         }
323         rmsCache.clear();
324     }
325
326     public static void deleteRecord(Class cls, int id) {
327         deleteRecord(cls.getName(), id);
328     }
329
330     /**
331      * 刪除某個倉庫裡面的某條記錄
332      * @param rsName 倉庫的名字
333      * @param id 記錄的ID
334      */
335     public static void deleteRecord(String rsName, int id) {
336         try {
337             RecordStore rs = RecordStore.openRecordStore(rsName, false);
338             if (rs != null) {
339                 rs.deleteRecord(id);
340             }
341             rs.closeRecordStore();
342         } catch (Exception exe) {
343         }
344     }
345
346     /**
347      * 一個簡單的方法用於關閉RecordStore
348      * @param rs 要關閉的RecordStore
349      */
350     private static void closeRecordStore(RecordStore rs) {
351         try {
352             rs.closeRecordStore();
353         } catch (Exception exe) {
354             LogManager.error("RMSUtil.closeRecordStore(),rs = " + rs + ",exe = " + exe);
355             exe.printStackTrace();
356         }
357     }
358
359     /**
360      * 得到某個RMS的存儲數據庫,先從緩存裡面去找,如果沒有找到則生成
361      * 一個並把它放到緩存裡面,還有,因為RMS的名字,最長只支持32位
362      * @param name 數據庫的名字
363      * @return 找到的RMS數據庫
364      */
365     private synchronized static RecordStore getRecordStore(String name) throws RecordStoreException {
366         if (name.length() > 32) {
367             name = name.substring(name.length()-32, name.length());
368         }
369         if (rmsCache.containsKey(name)) {
370             return (RecordStore) rmsCache.get(name);
371         } else {
372             RecordStore rs = RecordStore.openRecordStore(name, true);
373             rmsCache.put(name, rs);
374             return rs;
375         }
376     }
377 }
378

  相信看完代碼以後,大家應該知道如何使用它吧。如果有需要持久化的類,那麼就需要實現Serializable接口,然後只要調用RMSUtil.insertObject()就可以了,同理,查找也是一樣的,你可以查找同一個類的一系列對象,也可以自己定義記錄查詢器,在裡面設置查詢條件。

  目前JAVAME的持久化框架,也有用其它的方法實現的,比如動態插入代碼的方法,也就是你在寫好了JavaME的代碼以後,在編譯的過程中,它自動幫你加上相應的方法,我看了一個他們的源碼,其實也就是它們自己幫你實現了一個相當於Serializable接口,我覺得這樣不好的地方在於,它會為你的類添加方法,萬一你的類裡面原來就有那個方法的時候,那就會出現不可意料的情況了,還有,我覺得自己的數據還是自己一個一個把它寫出來,這樣心裡更踏實一些。我一直都認為,封裝得有一個度,不能過度的封裝,過度封裝表面上看是編碼更方便了,但是寫的時候,自己心裡也更沒底了,因為你不知道別人的代碼都做了一些什麼。因為別人的代碼做的事情太多了。呵呵,純屬個人意見。

  大家如果有什麼自己的看法,歡迎留言。

  還有,此代碼用到了我的另外一個通用框架,那就是LOG框架,所以如果直接下載的話,可能會編譯不過了,只要注釋掉LogManager的調用就可以了。LOG框架的說明點擊這裡,這個LOG框架現在正在改動中,以使它更好用,更沒有侵入性。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved