打造android ORM架構opendroid(二)——自動建立資料庫

來源:互聯網
上載者:User

標籤:架構   orm   opendroid   

在上一篇部落格《打造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是怎麼建立資料庫的。

/** * 插入資料 * @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;}

第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());}@Overridepublic 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 {// 反射擷取classClass<OpenDroid> klass = (Class<OpenDroid>) Class.forName(pullParser.getAttributeValue(null, "class"));StringBuilder sql = new StringBuilder("create table ");// 擷取類名, getSimpleName擷取類名, getName()擷取包名+類名sql.append(klass.getSimpleName()).append("(");// 自動建立一個_idsql.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) {  // 如果是Stringsql.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 {// 反射擷取classClass<OpenDroid> klass = (Class<OpenDroid>) Class.forName(pullParser.getAttributeValue(null, "class"));StringBuilder sql = new StringBuilder("create table ");// 擷取類名, getSimpleName擷取類名, getName()擷取包名+類名sql.append(klass.getSimpleName()).append("(");// 自動建立一個_idsql.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) {  // 如果是Stringsql.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操作和它的資料庫升級機制。


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

打造android ORM架構opendroid(二)——自動建立資料庫

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.