(二)——自动创建数据库

最后更新于:2022-04-01 06:33:55

在上一篇博客[《打造android ORM框架opendroid(一)——ORM框架的使用》](http://blog.csdn.net/qibin0506/article/details/42736807)中相信你已经了解了opendroid的使用,那么从这篇博客开始,我们正式进入opendroid的源码分析,打造一款自己的ORM框架! 在正式开始之前,你需要保证手里有一份opendroid的源码,如果还没下载opendroid,请到[http://git.oschina.net/qibin/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 klass = (Class) getClass();           ContentValues cv = new ContentValues();           generateData(klass, cv);                          return CRUD.insert(klass.getSimpleName(), cv, sSqliteDatabase);       } catch (Exception e) {           e.printStackTrace();       }       return -1;   }   ~~~ 第11行,通过调用了CRUD的一个静态方法insert将数据保存到数据库中,insert的最后一个参数sSqliteDatabas是我们关心的,来看看它的定义: ~~~ private static SQLiteDatabase sSqliteDatabase = sOpenHelper.getWritableDatabase();   ~~~ sSqliteDatabase是通过sOpenHelper调用getWriteableDatabase()返回的,相信这里大家应该非常熟悉了,再来看看sOpenHelper的定义: ` private static CreateDB sOpenHelper = new CreateDB();  ` 在这里直接new了一个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,这里大家肯定也很熟悉,先从构造方法开始看, 在构造方法中直接调用了父类的构造方法,第一个参数是一个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-8");                              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 klass = (Class) Class.forName(pullParser.getAttributeValue(null, "class"));                      StringBuilder sql = new StringBuilder("create table ");           // 获取类名, getSimpleName获取类名, getName()获取包名+类名           sql.append(klass.getSimpleName()).append("(");           // 自动创建一个_id           sql.append("_id integer primary key autoincrement,");                      // 获取所有的字段           Field[] fields = klass.getDeclaredFields();           for(Field field : fields) {               // 如果是public的, 则表示不是一个表的字段               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(),这个方法很简单,就是返回了一个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-8");                          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 klass = (Class) Class.forName(pullParser.getAttributeValue(null, "class"));                  StringBuilder sql = new StringBuilder("create table ");       // 获取类名, getSimpleName获取类名, getName()获取包名+类名       sql.append(klass.getSimpleName()).append("(");       // 自动创建一个_id       sql.append("_id integer primary key autoincrement,");                  // 获取所有的字段       Field[] fields = klass.getDeclaredFields();       for(Field field : fields) {           // 如果是public的, 则表示不是一个表的字段           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会自动为我们添加一个_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操作和它的数据库升级机制。 马上继续[《打造android ORM框架opendroid(三)——持久化数据》](http://blog.csdn.net/qibin0506/article/details/42872361) opendroid的开源地址:[http://git.oschina.net/qibin/OpenDroid](http://git.oschina.net/qibin/OpenDroid)
';