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

来源:互联网 发布:奔驰换大灯编程教程 编辑:程序博客网 时间:2024/05/19 22:50

转载自:http://blog.csdn.net/qibin0506/article/details/42773281

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

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


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

[java] view plaincopy
  1. /** 
  2.  * 插入数据 
  3.  * @return 最后插入的id 
  4. */  
  5. public long save() {  
  6.     try {  
  7.         Class<OpenDroid> klass = (Class<OpenDroid>) getClass();  
  8.         ContentValues cv = new ContentValues();  
  9.         generateData(klass, cv);  
  10.               
  11.         return CRUD.insert(klass.getSimpleName(), cv, sSqliteDatabase);  
  12.     } catch (Exception e) {  
  13.         e.printStackTrace();  
  14.     }  
  15.     return -1;  
  16. }  

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

[java] view plaincopy
  1. private static SQLiteDatabase sSqliteDatabase = sOpenHelper.getWritableDatabase();  

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

[java] view plaincopy
  1. private static CreateDB sOpenHelper = new CreateDB();  

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

[java] view plaincopy
  1. public class CreateDB extends SQLiteOpenHelper {  
  2.       
  3.     public CreateDB() {  
  4.         super(DroidApplication.sContext, OpenDroidHelper.getDBInfoBean().getName(),  
  5.                 null, OpenDroidHelper.getDBInfoBean().getVersion(), new DefaultDatabaseErrorHandler());  
  6.     }  
  7.   
  8.     @Override  
  9.     public void onCreate(SQLiteDatabase db) {  
  10.         for(String sql : OpenDroidHelper.getDBInfoBean().getSqls()) {  
  11.             db.execSQL(sql);  
  12.         }  
  13.     }  
  14. }  

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

[java] view plaincopy
  1. public class OpenDroidHelper {  
  2.     public static final String TAG_DROID = "open-droid";  
  3.     public static final String TAG_VERSION = "version";  
  4.     public static final String TAG_NAME = "name";  
  5.     public static final String TAG_MAPPING = "mapping";  
  6.       
  7.     private static DBBean sDBBean;  
  8.       
  9.     public static DBBean getDBInfoBean() {  
  10.         if(sDBBean == null) {  
  11.             generateDBInfoBean();  
  12.         }  
  13.           
  14.         return sDBBean;  
  15.     }  
  16.       
  17.     /** 
  18.      * 解析Asserts目录下的open_droid.xml文件,生成DBInfoBean 
  19.      */  
  20.     private static void generateDBInfoBean() {  
  21.         try {  
  22.             XmlPullParser pullParser = Xml.newPullParser();  
  23.             InputStream inputStream = DroidApplication.sContext.getAssets().open("open_droid.xml");  
  24.             pullParser.setInput(inputStream, "utf-8");  
  25.               
  26.             int type = pullParser.getEventType();  
  27.             String tagName = null;  
  28.               
  29.             while(type != XmlPullParser.END_DOCUMENT) {  
  30.                 if(type == XmlPullParser.START_TAG) {  
  31.                     tagName = pullParser.getName();  
  32.                     if(tagName.equals(TAG_DROID)) {  
  33.                         sDBBean = new DBBean();  
  34.                     }else if(tagName.equals(TAG_VERSION)) {  
  35.                         // 获取版本号  
  36.                         sDBBean.setVersion(Integer.parseInt(pullParser.getAttributeValue(null"value")));  
  37.                     }else if(tagName.equals(TAG_NAME)) {  
  38.                         // 获取数据库名  
  39.                         sDBBean.setName(pullParser.getAttributeValue(null"value"));  
  40.                     }else if(tagName.equals(TAG_MAPPING)) {  
  41.                         // 获取所有建表语句  
  42.                         sDBBean.addSql(generateSql(pullParser));  
  43.                     }  
  44.                 }  
  45.                 type = pullParser.next();  
  46.             }  
  47.         } catch (Exception e) {  
  48.             e.printStackTrace();  
  49.         }  
  50.     }  
  51.   
  52.     /** 
  53.      * 生成建表sql语句 
  54.      * @param pullParser 
  55.      * @return 
  56.      * @throws ClassNotFoundException 
  57.      * @throws XmlPullParserException 
  58.      * @throws IOException 
  59.      */  
  60.     private static String generateSql(XmlPullParser pullParser)  
  61.             throws ClassNotFoundException, XmlPullParserException, IOException {  
  62.         // 反射获取class  
  63.         Class<OpenDroid> klass = (Class<OpenDroid>) Class.forName(pullParser.getAttributeValue(null"class"));  
  64.           
  65.         StringBuilder sql = new StringBuilder("create table ");  
  66.         // 获取类名, getSimpleName获取类名, getName()获取包名+类名  
  67.         sql.append(klass.getSimpleName()).append("(");  
  68.         // 自动创建一个_id  
  69.         sql.append("_id integer primary key autoincrement,");  
  70.           
  71.         // 获取所有的字段  
  72.         Field[] fields = klass.getDeclaredFields();  
  73.         for(Field field : fields) {  
  74.             // 如果是public的, 则表示不是一个表的字段  
  75.             if(field.isAccessible()) {  
  76.                 continue;  
  77.             }  
  78.               
  79.             // 获取字段名  
  80.             String name = field.getName();  
  81.             sql.append(name).append(" ");  
  82.               
  83.             // 获取字段类型  
  84.             Class<?> fieldType = field.getType();  
  85.             if(fieldType == String.class) {  // 如果是String  
  86.                 sql.append("text,");  
  87.             }else if(fieldType == Integer.class || fieldType == int.class) {  
  88.                 sql.append("integer,");  
  89.             }else if(fieldType == Long.class || fieldType == long.class){  
  90.                 sql.append("integer,");  
  91.             }else if(fieldType == Boolean.class || fieldType == boolean.class) {  
  92.                 sql.append("boolean,");  
  93.             }else if(fieldType == Float.class || fieldType == float.class) {  
  94.                 sql.append("float,");  
  95.             }  
  96.         }  
  97.         sql.replace(sql.length() - 1, sql.length(), "");  
  98.         sql.append(");");  
  99.           
  100.         return sql.toString();  
  101.     }  
  102. }  

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

[java] view plaincopy
  1. /** 
  2. * 解析Asserts目录下的open_droid.xml文件,生成DBInfoBean 
  3. */  
  4. private static void generateDBInfoBean() {  
  5.     try {  
  6.         XmlPullParser pullParser = Xml.newPullParser();  
  7.         InputStream inputStream = DroidApplication.sContext.getAssets().open("open_droid.xml");  
  8.         pullParser.setInput(inputStream, "utf-8");  
  9.               
  10.         int type = pullParser.getEventType();  
  11.         String tagName = null;  
  12.               
  13.         while(type != XmlPullParser.END_DOCUMENT) {  
  14.             if(type == XmlPullParser.START_TAG) {  
  15.                 tagName = pullParser.getName();  
  16.                 if(tagName.equals(TAG_DROID)) {  
  17.                     sDBBean = new DBBean();  
  18.                 }else if(tagName.equals(TAG_VERSION)) {  
  19.                     // 获取版本号  
  20.                     sDBBean.setVersion(Integer.parseInt(pullParser.getAttributeValue(null"value")));  
  21.                 }else if(tagName.equals(TAG_NAME)) {  
  22.                     // 获取数据库名  
  23.                     sDBBean.setName(pullParser.getAttributeValue(null"value"));  
  24.                 }else if(tagName.equals(TAG_MAPPING)) {  
  25.                     // 获取所有建表语句  
  26.                     sDBBean.addSql(generateSql(pullParser));  
  27.                 }  
  28.             }  
  29.             type = pullParser.next();  
  30.         }  
  31.     } catch (Exception e) {  
  32.         e.printStackTrace();  
  33.     }  
  34. }  

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


[java] view plaincopy
  1. /** 
  2.  * 生成建表sql语句 
  3.  * @param pullParser 
  4.  * @return 
  5.  * @throws ClassNotFoundException 
  6.  * @throws XmlPullParserException 
  7.  * @throws IOException 
  8.  */  
  9. private static String generateSql(XmlPullParser pullParser)  
  10.         throws ClassNotFoundException, XmlPullParserException, IOException {  
  11.     // 反射获取class  
  12.     Class<OpenDroid> klass = (Class<OpenDroid>) Class.forName(pullParser.getAttributeValue(null"class"));  
  13.           
  14.     StringBuilder sql = new StringBuilder("create table ");  
  15.     // 获取类名, getSimpleName获取类名, getName()获取包名+类名  
  16.     sql.append(klass.getSimpleName()).append("(");  
  17.     // 自动创建一个_id  
  18.     sql.append("_id integer primary key autoincrement,");  
  19.           
  20.     // 获取所有的字段  
  21.     Field[] fields = klass.getDeclaredFields();  
  22.     for(Field field : fields) {  
  23.         // 如果是public的, 则表示不是一个表的字段  
  24.         if(field.isAccessible()) {  
  25.             continue;  
  26.         }  
  27.               
  28.         // 获取字段名  
  29.         String name = field.getName();  
  30.         sql.append(name).append(" ");  
  31.               
  32.         // 获取字段类型  
  33.         Class<?> fieldType = field.getType();  
  34.         if(fieldType == String.class) {  // 如果是String  
  35.             sql.append("text,");  
  36.         }else if(fieldType == Integer.class || fieldType == int.class) {  
  37.             sql.append("integer,");  
  38.         }else if(fieldType == Long.class || fieldType == long.class){  
  39.             sql.append("integer,");  
  40.         }else if(fieldType == Boolean.class || fieldType == boolean.class) {  
  41.             sql.append("boolean,");  
  42.         }else if(fieldType == Float.class || fieldType == float.class) {  
  43.             sql.append("float,");  
  44.         }  
  45.     }  
  46.     sql.replace(sql.length() - 1, sql.length(), "");  
  47.     sql.append(");");  
  48.           
  49.     return sql.toString();  
  50. }  

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

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

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

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

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

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

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

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


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

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

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

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

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


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


0 0
原创粉丝点击