千里冰封
java 浓香四溢
posts - 151,comments - 2801,trackbacks - 0
在写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(nullnullfalse);
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的浓香.
posted on 2009-03-01 10:13 千里冰封 阅读(4963) 评论(3)  编辑  收藏 所属分类: javame

feedback:
# re: javame的rms通用持久化框架[未登录]
2009-03-01 08:38 |
谢谢楼主的分享,非常好的文章  回复  
  
# re: javame的rms通用持久化框架
2009-03-01 09:58 |
楼主学习的速度很快啊。  回复  
  
# re: javame的rms通用持久化框架
2009-05-20 15:57 |
javame已死  回复  
  

只有注册用户后才能发表评论。


网站导航:
              
 
网站地图