打造android ORM框架opendroid(二)――自動創建數據庫
來源:程序員人生 發布時間:2015-02-06 08:52:36 閱讀次數:3232次
在上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
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈