程序员人生 网站导航

打造android ORM框架opendroid(二)――自动创建数据库

栏目:综合技术时间:2015-02-06 08:52:36

在上1篇博客《打造android ORM框架opendroid(1)――ORM框架的使用》中相信你已了解了opendroid的使用,那末从这篇博客开始,我们正式进入opendroid的源码分析,打造1款自己的ORM框架!

在正式开始之前,你需要保证手里有1份opendroid的源码,如果还没下载opendroid,请到http://git.oschina.net/qibin/OpenDroid 下载opendroid的源码。


任何数据库操作都是从创建数据库开始的,今天我们就来看看opendroid是怎样帮我们自动创建数据库的。 还记得关系映照怎样配置吗? 在open-droid.xml中,通过配置mapping节点来告知opendroid我们需要映照的java bean。那末数据库操作是从什么时候开始的呢, 拿insert来讲,就是调用了从OpenDroid继承而来的save()方法!在这之前,我们没有任何数据库方面的操作,那末我们就从save()方法开始,看看opendroid是怎样创建数据库的。

/** * 插入数据 * @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 ⑴; }

第11行,通过调用了CRUD的1个静态方法insert将数据保存到数据库中,insert的最后1个参数sSqliteDatabas是我们关心的,来看看它的定义:

private static SQLiteDatabase sSqliteDatabase = sOpenHelper.getWritableDatabase();

sSqliteDatabase是通过sOpenHelper调用getWriteableDatabase()返回的,相信这里大家应当非常熟习了,再来看看sOpenHelper的定义:

private static CreateDB sOpenHelper = new CreateDB();

在这里直接new了1个CreateDB,通过类名我们完全可以知道CreateDB就是创建数据库的关键。来看看CreateDB吧:

public class CreateDB extends SQLiteOpenHelper { public CreateDB() { super(DroidApplication.sContext, OpenDroidHelper.getDBInfoBean().getName(), null, OpenDroidHelper.getDBInfoBean().getVersion(), new DefaultDatabaseErrorHandler()); } @Override public void onCreate(SQLiteDatabase db) { for(String sql : OpenDroidHelper.getDBInfoBean().getSqls()) { db.execSQL(sql); } } }

这里我只截取了和创建数据库有关的代码, 可以看到CreateDB继承自SQLiteOpenHelper,这里大家肯定也很熟习,先从构造方法开始看, 在构造方法中直接调用了父类的构造方法,第1个参数是1个context对象,这个对象是在DroidApplication中,其实很简单,就是在onCreate中调用getApplicationContext()为DroidApplication中的sContext静态变量赋值,这里就不贴代码了,可以在源码中找到,在看看接下来几个参数,都是通过OpenDroidHelper.getDBInfoBean获得的。再往后看看发现onCreate中也是通过遍历OpenDroidHelper.getDBInfoBean().getSqls()来获得创建表的sql语句,那末现在我们就去OpenDroidHelper看看吧。

public class OpenDroidHelper { public static final String TAG_DROID = "open-droid"; public static final String TAG_VERSION = "version"; public static final String TAG_NAME = "name"; public static final String TAG_MAPPING = "mapping"; private static DBBean sDBBean; public static DBBean getDBInfoBean() { if(sDBBean == null) { generateDBInfoBean(); } return sDBBean; } /** * 解析Asserts目录下的open_droid.xml文件,生成DBInfoBean */ private static void generateDBInfoBean() { try { XmlPullParser pullParser = Xml.newPullParser(); InputStream inputStream = DroidApplication.sContext.getAssets().open("open_droid.xml"); pullParser.setInput(inputStream, "utf⑻"); int type = pullParser.getEventType(); String tagName = null; while(type != XmlPullParser.END_DOCUMENT) { if(type == XmlPullParser.START_TAG) { tagName = pullParser.getName(); if(tagName.equals(TAG_DROID)) { sDBBean = new DBBean(); }else if(tagName.equals(TAG_VERSION)) { // 获得版本号 sDBBean.setVersion(Integer.parseInt(pullParser.getAttributeValue(null, "value"))); }else if(tagName.equals(TAG_NAME)) { // 获得数据库名 sDBBean.setName(pullParser.getAttributeValue(null, "value")); }else if(tagName.equals(TAG_MAPPING)) { // 获得所有建表语句 sDBBean.addSql(generateSql(pullParser)); } } type = pullParser.next(); } } catch (Exception e) { e.printStackTrace(); } } /** * 生成建表sql语句 * @param pullParser * @return * @throws ClassNotFoundException * @throws XmlPullParserException * @throws IOException */ private static String generateSql(XmlPullParser pullParser) throws ClassNotFoundException, XmlPullParserException, IOException { // 反射获得class Class<OpenDroid> klass = (Class<OpenDroid>) Class.forName(pullParser.getAttributeValue(null, "class")); StringBuilder sql = new StringBuilder("create table "); // 获得类名, getSimpleName获得类名, getName()获得包名+类名 sql.append(klass.getSimpleName()).append("("); // 自动创建1个_id sql.append("_id integer primary key autoincrement,"); // 获得所有的字段 Field[] fields = klass.getDeclaredFields(); for(Field field : fields) { // 如果是public的, 则表示不是1个表的字段 if(field.isAccessible()) { continue; } // 获得字段名 String name = field.getName(); sql.append(name).append(" "); // 获得字段类型 Class<?> fieldType = field.getType(); if(fieldType == String.class) { // 如果是String sql.append("text,"); }else if(fieldType == Integer.class || fieldType == int.class) { sql.append("integer,"); }else if(fieldType == Long.class || fieldType == long.class){ sql.append("integer,"); }else if(fieldType == Boolean.class || fieldType == boolean.class) { sql.append("boolean,"); }else if(fieldType == Float.class || fieldType == float.class) { sql.append("float,"); } } sql.replace(sql.length() - 1, sql.length(), ""); sql.append(");"); return sql.toString(); } }

额,代码有点小长, 我们渐渐来看。首先来看看我们之前调用的getDBInfoBean(),这个方法很简单,就是返回了1个DBBean对象,不过,它还调用了generateDBInfoBean()方法,通过方法名可以看出,它的作用是生成DBBean的。

/** * 解析Asserts目录下的open_droid.xml文件,生成DBInfoBean */ private static void generateDBInfoBean() { try { XmlPullParser pullParser = Xml.newPullParser(); InputStream inputStream = DroidApplication.sContext.getAssets().open("open_droid.xml"); pullParser.setInput(inputStream, "utf⑻"); int type = pullParser.getEventType(); String tagName = null; while(type != XmlPullParser.END_DOCUMENT) { if(type == XmlPullParser.START_TAG) { tagName = pullParser.getName(); if(tagName.equals(TAG_DROID)) { sDBBean = new DBBean(); }else if(tagName.equals(TAG_VERSION)) { // 获得版本号 sDBBean.setVersion(Integer.parseInt(pullParser.getAttributeValue(null, "value"))); }else if(tagName.equals(TAG_NAME)) { // 获得数据库名 sDBBean.setName(pullParser.getAttributeValue(null, "value")); }else if(tagName.equals(TAG_MAPPING)) { // 获得所有建表语句 sDBBean.addSql(generateSql(pullParser)); } } type = pullParser.next(); } } catch (Exception e) { e.printStackTrace(); } }

恩,在generateDBInfoBean这个方法中,都是我们熟习的XMLPullParser的代码,作用就是去解析open-droid.xml文件,获得数据库名称、数据库版本和数据表的信息。虽然很长,但是都很简单,20行,我们获得了数据库的版本号,并保存到了DBBean中,一样的23行获得了数据库的名称,注意第26行,我们想DBBean中添加的sql语句,那添加的甚么sql语句呢? 肯定是建表的sql语句了。来看看generateSql()方法。


/** * 生成建表sql语句 * @param pullParser * @return * @throws ClassNotFoundException * @throws XmlPullParserException * @throws IOException */ private static String generateSql(XmlPullParser pullParser) throws ClassNotFoundException, XmlPullParserException, IOException { // 反射获得class Class<OpenDroid> klass = (Class<OpenDroid>) Class.forName(pullParser.getAttributeValue(null, "class")); StringBuilder sql = new StringBuilder("create table "); // 获得类名, getSimpleName获得类名, getName()获得包名+类名 sql.append(klass.getSimpleName()).append("("); // 自动创建1个_id sql.append("_id integer primary key autoincrement,"); // 获得所有的字段 Field[] fields = klass.getDeclaredFields(); for(Field field : fields) { // 如果是public的, 则表示不是1个表的字段 if(field.isAccessible()) { continue; } // 获得字段名 String name = field.getName(); sql.append(name).append(" "); // 获得字段类型 Class<?> fieldType = field.getType(); if(fieldType == String.class) { // 如果是String sql.append("text,"); }else if(fieldType == Integer.class || fieldType == int.class) { sql.append("integer,"); }else if(fieldType == Long.class || fieldType == long.class){ sql.append("integer,"); }else if(fieldType == Boolean.class || fieldType == boolean.class) { sql.append("boolean,"); }else if(fieldType == Float.class || fieldType == float.class) { sql.append("float,"); } } sql.replace(sql.length() - 1, sql.length(), ""); sql.append(");"); return sql.toString(); }

generateSql()里面全是反射的代码,如果你对反射还不熟习,建议你先去看看java反射,由于opendroid中大量使用了反射机制,

12行,通过反射获得我们要映照的class,然后14~18行,是初始化创建表的sql语句,并且可以看到opendroid会自动为我们添加1个_id字段,所以在定义bean的时候,我们不需要再次定义了。

21行,获得了这个类中定义的所有字段,并在22行循环遍历这些字段。

14~26行,可以看到,如果字段是public的,那就疏忽它,所以如果你不想把某个字段映照到数据库中,就定义成public的。

29~30行,是向创建表的sql语句中追加表名。

33~44行,通过判断这个字段类型,来向创建表的sql语句中追加该字段的类型。

46行的作用是删除最后1个的“,”

47行,就完成了该表的创建sql语句。


至此,opendroid的自动创建数据库的流程我们就分析完了,现在来总结1下:

1、数据库的创建我们借助了android API的SQLiteOpenHelper。

2、在SQLiteOpenHelper的onCreate中遍历我们自动生成的建表语句并履行。

3、如何生成建表语句? 在OpenDroidHelper中通过反射获得类的类名和字段名,并通过StringBuilder来拼凑建表语句。

4、为了方便,我们在OpenDroidHelper中将解析出来的数据名、数据库版本和建表语句都放到了DBBean中,那末我们在CreateDB类中就能够通过DBBean方便的获得数据库的信息了。


ok,数据库的自动创建大体流程就是这样,在接下来的博客中,我们还会去逐一介绍opendroid的CRUD操作和它的数据库升级机制。


opendroid的开源地址:http://git.oschina.net/qibin/OpenDroid

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐