在上一篇博客《打造android ORM框架opendroid(二)——自动创建数据库》中,我们介绍了opendroid是怎么做到自动帮我们创建好数据库并通过反射拼凑出创建数据库的SQL语句,接着上面的博客,今天要来介绍一下opendroid数据库持久化(也就是insert操作)是怎么一个流程。
废话不多少,我们马上进入主题。
...
还记得通过opendroid我们是如何将数据保存到数据库的吗? 当时是调用了从OpenDroid类继承过来的save方法,来回顾一下吧。
Student stu = new Student(); stu.setStuName("亓斌"); stu.setStuAge(18); stu.save();
按照惯例,我们定位到save()的源码。
/** * 插入数据 * @return 最后插入的id */ public long save() { try { Class<OpenDroid> klass = (Class<OpenDroid>) getClass(); ContentValues cv = new ContentValues(); generateData(klass, cv); return CRUD.insert(klass.getSimpleName(), cv, sSqliteDatabase); } catch (Exception e) { e.printStackTrace(); } return -1; }
首先第7行,通过getClass方法获取了当前代表当前对象的Class。
接着第8行new了一个ContentValues,相信大家对它肯定再熟悉不过了。
9行,调用了generateData方法,这个方法有两个参数,第一个是一个Class对象,第7行的时候我们已经获取了,第二个参数是一个ContentValues,用来保存存放将要插入数据库的数据。
接着在11行,调用了CRUD类中的insert静态方法来保存数据,insert方法的第一个参数是要插入的表名,因为我们的表和bean是一一对应的,所以当前类名就是我们要操作的表名,第二个参数是一个SQLiteDatabase对象。
CRUD.insert方法会返回一个long类型的返回值,相信大家已经猜到了,返回值就是我们新插入数据的id。
分析完这个方法,接下来我们就来看看第9行中调用的generateData方法.
/** * 生成数据 * @param tableName 要获取的表名 * @param values 要获取的数据 * @throws NoSuchMethodException * @throws IllegalAccessException * @throws InvocationTargetException */ private void generateData(Class<OpenDroid> klass, ContentValues values) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Field[] fields = klass.getDeclaredFields(); // 获取类中的所有字段 Method m; String fieldName; String methodName; for (Field field : fields) { // 如果是public,则忽略 if (field.isAccessible()) { continue; } // 获取字段的类型 Class<?> fieldType = field.getType(); fieldName = field.getName(); // 获取字段名称 // 将字段名称的首字母大写,准备拼装getter(getName) methodName = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1); // 这里还要判断一下类型是不是boolean, // 如果是boolean, 就不是getXXX了,而是isXXX if (fieldType == Boolean.class || fieldType == boolean.class) { m = klass.getDeclaredMethod("is" + methodName); } else { m = klass.getDeclaredMethod("get" + methodName); // 获取方法 } // 获取方法的返回值 Object value = m.invoke(this); // 对于一些类型不支持。。 // 未找到解决方案 if (value == null) { // 如果是null,则在contentValues里添加一个null // values.putNull(fieldName); continue; } // 通过判断field的类型,向contentValues插入对应的数据 if (fieldType == Integer.class || fieldType == int.class) { values.put(fieldName, Integer.parseInt(m.invoke(this).toString())); } else if (fieldType == Boolean.class || fieldType == boolean.class) { values.put(fieldName, Boolean.parseBoolean(m.invoke(this).toString())); } else { values.put(fieldName, m.invoke(this).toString()); } } }
方法的参数我们在上面已经说明了,这里就不重复了,在方法刚开始,接连不断的定义了4个变量,第一个变量是我们通过klass获取到的该类中所有的字段,接下来我们要通过这些字段来获取要保存的值和字段名。剩下的几个变量我们在用到的时候再来说。
17行,进入了一个for循环,遍历的所有的字段,循环中,19~21行,我们依然去判断该字段是不是public,如果是public,则证明该字段并没有映射到数据库中。
24行,获取了字段的类型,因为在下面我们要通过类型来设置值。
26~28行,我们准备去拼凑getter方法。
接着32~36行,又是一个判断,主要是为了我们java bean定义的规范,如果是boolean类型的,我们在定义方法的时候难道不是isXXX吗?
ok,我们通过反射获取了将要调用的的方法,接下来39行就要通过getter方法来获取具体的值了。
42~46行是一个败笔,还未能结果对于没考虑到的类型的怎么去支持,不过也不影响,常用的类型已经支持了。
49~55行,通过字段的类型,来向ContentValues中保存不同类型的值,可以看到我们使用调用m.invoke方法来执行反射出来的方法。
至此,我们已经将数据都保存进ContentValues中了,接下来就是调用android原生的api代码,将数据库保存进数据库就ok了。
而保存进数据库肯定就是调用了CRUD.insert方法,赶紧来看看这个方法吧。
/** * 插入数据 * @param t 对应的bean * @param db 数据库操作 * @return 最新插入的id */ protected static long insert(String tableName, ContentValues cv, SQLiteDatabase db) { long id = db.insert(tableName, null, cv); return id; }
好,opendroid的数据持久化流程就说到这里,在接下来的博客中我们还会去了解opendroid的其他操作。
opendroid的开源地址:http://git.oschina.net/qibin/OpenDroid