posts - 71,  comments - 203,  views - 79万

因为ios用的是sqlite数据库,所以公司要求我也用sqlite,大家都知道sqlite的sql不怎么好用,尤其是我们app里至少有46张表,数据量最多能达到2G,所以不重新封装一层,越到后期怕是越难维护吧,代码量也不少。

所以我在sqlite上又封装了一层,参照对象型数据库的原理也做了这样module。类似hibernate。

a.首先是字段咯,entity里的properties不一定全要映射到数据库中,所以我定义了

DBColumn : String columnName ,String type ,Class clz.(如果你需要用index和trigger也是如此)

数据库5种类型可以写个tool和property的数据类型做映射(当然还有1-1,1-*,*-* 关系)

b.接口设计,如果以后不用sqlite,选用其他数据库了,为了这样的考虑,所以接口要设计好

- newOrUpdate(T) - delete(T) -query(sql,String[] args) -query(Class,String[] args) -close()

这里sqlite有replace()方法,会自动去判断是需要insert还是update。推荐使用。

接口定义好了,就是数据库实现了。这里就会有两个问题:

1.insert/update如何构建ContentValues,如果要想automatic,那么必须要用反射了。之前有提到要定义映射的DBColumn,那么有了每个字段的定义,通过反射去拿值构建ContentValues应该不是什么难事了。比较难搞定的是表关系。1-1比较好弄,只是把id存好就可以了。1-*关系 这个关系是存在*的这张表,所以不做任何事情,*-*就比较麻烦,需要存到关联表中。代码如下:

case DBColumn.FIELD_TYPE_TMANY:// save the objects'

List<? extends Persistent> list = (List<? extends Persistent>) method.invoke(t, null);

if (list != null && list.size() > 0) {

ContentValues tValues = null;

HashMap<String, ContentValues> tMap = null;

for (Persistent persistent : list) {

tValues = new ContentValues();

tMap = new HashMap<String, ContentValues>();

tValues.put(Repository.PK1, t.realGuid());

tValues.put(Repository.PK2, persistent.realGuid());

tMap.put(DBUtilities.getAssosiationTableName(clz.column.columnName), tValues);

bindArgs.add(tMap);

}

}

2.query出来如何setValue.这里当然需要Cursor对象咯。同样的,通过反射给DTO对象赋值,普通对象没什么问题,关键是1-1,1-*,*-* 都是其他的DTO对象或者是ArrayList<DTO>,所以这里就引出了很重要的概念:代理(Proxy)。因为Android提供的java代理不能很好用,之后试过CGLib库里的Proxy,虽然好用,但是在Android是不能用,悲剧。所以我leader让我试试AspectJ。会用Spring的人都知道面向切面把。AspectJ就是做这个的。

首先,有这样一个概念,如果一次就把DTO对象里的property都setValue,那么需要查好几次表把。因为有1-1等那些关系。如果每次都这样,是不是会引起不必要的浪费,所以我们需要一个lazy load来加载1-1等那些数据。那么lazyload如何实现呢。有了AspectJ就好办了。首先DTO提供一个有参的构造方法。这里参数当然是id咯。至于1-*等关系,你就需要extends ArrayList了。同样是有参的构造方法。把ids或者querySql传过去,当然还要指明T。然后重写ArrayList的方法,add(T),size(),remove(T)...例如:

@Override

public T get(int location) {

if (cache.containsKey(location)) {

if (cache.get(location) != null) {

return (T) cache.get(location).get();

} else {

cache.remove(location);

return createObjFromCursor(location);

}

} else {// get obj from cursor and save it into map

return createObjFromCursor(location);

}

}

相信看了代码大家都很明白了把,取值是从cache或者db里面取值,然后就是setValue的具体方法了,以下是部分代码:

case DBColumn.FIELD_TYPE_TONE:

Constructor con = column.clz.getConstructor(String.class);

method = targetClass.getMethod(generateMethodName(false, column.columnName,isBooleanType),new Class[] { column.clz });

method.invoke(t, con.newInstance(cursor.getString(index)));

break;

case DBColumn.FIELD_TYPE_TMANY:

method = targetClass.getMethod(generateMethodName(false, column.columnName,isBooleanType),new Class[] { DTOList.class });

method.invoke(t,new DTOArray(column.clz, DBUtilities.getTManyGuidsStmt(targetClass, column),DBUtilities.getTManyQueryStmt(targetClass,column), new String[] { t.realGuid() }));

break;

case DBColumn.FIELD_TYPE_MMANY:

method = targetClass.getMethod(generateMethodName(false, column.columnName,isBooleanType),new Class[] { DTOList.class });

method.invoke(t,new DTOArray(column.clz, null, DBUtilities.getMManyQueryStmt(column.clz),new String[] { t.realGuid(),getObjectType(targetClass) }));

break;

注意Cursor,要选择合适的场合close,否则~你懂的~~

写的这么复杂,确实有点伤脑筋,但是框架搭好了之后(花了大概一个月时间)就在也不用管了。比起用别人的数据库框架,自己封装一个简易的框架是不是更方便更省心更安全呢。

这里推荐一些android的数据库框架:

对象型数据库:Perst,DB4O.

关系型数据库框架:Android orm ,ormlite.

posted on   stay  阅读(1963)  评论(1编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示