JAVAME的RMS通用持久化框架

JAVAME的RMS通用持久化框架
在写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框架现在正在改动中,以使它更好用,更没有侵入性。

Netbeans项目工程打包下载, 请点击这里。此工程中还有LOG框架在里面。



尽管千里冰封
依然拥有晴空

你我共同品味JAVA的浓香.

你可能感兴趣的:(JAVAME的RMS通用持久化框架)