«

KopDB 框架学习2——源码分析

时间:2024-3-2 18:54     作者:韩俊     分类: Android


我的博客:http://mrfufufu.github.io/

上次我们主要是对 KopDB 框架的使用进行了分析,它是非常简单有用的。这次主要是对它的源码进行分析,进一步的来了解它的真面目。

点击这里去往 “KopDB 框架学习1——使用”

因为 KopDB 采用的是对象关系映射(ORM)模式,即我们使用的编程语言是面向对象语言,而我们使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了。

使用 ORM 模式,主要是因为我们平时多用面向对象编程,只有少部分人才会比较精通关系型数据库。通过这种模式,可以让我们不考虑 SQL 语言的具体实现,从而专注于业务实现。

根据 ORM 模式的理念,我们每张表都应该对应一个模型,即,如果我们想要建一张 Person 表,就应该有一个 Person 类。

类关系图

核心类功能的介绍:

  1. BaseModel.java

所有的模型都应继承这个类,这个类的主要有两个方法:

getContentValues(): ContentValues:为将要进行的数据库操作获取表的字段和对应的值。方法是将对应的 model 通过反射的形式获取到它的列名和值,返回的是 ContentValues 对象。

getModels(Cursor curor):List<T>: 为查询数据库操作以后得到的数据进行解析,返回对应的 model 类型 的List。方法是将查询完数据库以后会得到一个 Cursor,所有的查询操作得到值都通过这个变量返回,对这个 cursor 中的对应列的值映射到对应的 model 中去。

  1. DatabaseManager.java

最主要用到的类就是DatabaseManager类的,通过这类,我们完全了所有的数据库的操作,包括建表,增删查改等操作。注意,需要通过单例得到它的实例,主要方法有:

a. 初始化操作

initDataBase(Context context, String dbName, int version, List<Class<?>> models)

数据库的初始化相关的操作,dbName为数据库的名称,version 就是数据库的版本,models 为需要映射到数据库中去的 model,具体映射方法我们在DatabaseTools.java类中来讲。

b. 查询操作

select(Class<T> claz, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

直接通过 claz 拿到数据库名字,根据 SQLiteDatabase 的实例直接去进行 query 的操作,query 的到 Cursor 的对象,所有的数据都在这里面,然后调用BaseModel.java中的getModels(Cursor curor)方法得到 List 数据,即完成。

c. 插入操作

insert 插入操作,有4个重载方法,这里只分析

insert(Class<T> claz, T t, DBOperateAsyncListener listener)

方法的实现,其他的大同小异。先是创建了 DBMsgObject 对象,用来作为 Message 的 obj,所有的操作数据库操作都会到对应的 handle 去进行。具体 insert 方法如下:

public <T extends BaseModel> void insert(Class<T> claz, T t, DBOperateAsyncListener listener) {
    if (claz != null && t != null) {
        DBMsgObject<T> msgObj = new DBMsgObject<T>();//创建DBMsgObject 对象,作为Message 的 obj 
        msgObj.claz = claz;
        ContentValues contentValues = null;
        try {
            contentValues = t.getContentValues();//拿到 BaseModel 的 ContentValues
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        if (contentValues != null) {
            //ContentCondition 主要为插入数据库的条件,值
            List<DBMsgObject.ContentCondition<T>> contentConditionList = new ArrayList<DBMsgObject.ContentCondition<T>>();
            DBMsgObject.ContentCondition<T> condition = new DBMsgObject.ContentCondition<T>();
            condition.model = t;//对应的 model
            condition.contentValues = contentValues;//要插入的值(键值对的形式,key 就是列名)
            contentConditionList.add(condition);//这里是单个 model 所有只需要 add 一次
            msgObj.contentConditionList = contentConditionList;
            msgObj.listener = listener;//对应的监听
            Message msg = new Message();
            msg.what = DatabaseOptionType.OPTION_INSERT.VALUE;//消息类型
            msg.obj = msgObj;
            handler.sendMessage(msg);//在 handler 中处理
        }
    }
}

如果是如下重载方法的话,只是对 models 多了一个 for 循环,具体代码就不再给出

insert(Class<T> claz, List<T> models, DBOperateAsyncListener listener)

d. 同步插入操作

syncInsert 同步插入操作,即在当前线程中去操作,而不是在子线程进行。除了syncInsert,其他所有的操作都是在 Handler 中完成的。代码如下:

public  <T extends BaseModel> long syncInsert( Class<T> claz, T value,boolean bNeedNotify) {
    if(appAppDatabase == null){
        return -1;
    }
    long retValue=-1;
    try{
        if (  value != null) {
            Message msg = new Message();
            String table = DatabaseTools.getTableName(claz);
            ContentValues contentValues = null;
            try {
                contentValues = value.getContentValues();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
//              if (false == isWithinTransaction()) {
                SQLiteDatabase database = appAppDatabase
                        .getWritableDatabase();
                if (database == null) {
                    return -1;
                }
                database.beginTransaction();
                try {
//                      retValue=DatabaseUtil.insertNoTx(msg, database);
                    database.insert(table, null, contentValues);
                    //dataChange(table);
                    database.setTransactionSuccessful();
                } finally {
                    database.endTransaction();
                }
//                  if(bNeedNotify){
//                      dataChange(table);
//                  }
//              } else {
//                  TLSTransactionObject tlsObj = (TLSTransactionObject) mTls
//                          .get();
//                  tlsObj.setSyncOpDB(true);
//                  tlsObj.getMsgList().add(msg);
//              }
        }
        return retValue;
    } catch (Exception e) {
        e.printStackTrace();
    } catch (Throwable t) {
        t.printStackTrace();
    }
    return retValue;
}

e.更新操作

大部分和 insert 相同,这里给出不同的地方,只要是增加了条件,需要更新的条件通过调用getUniqueColumn(claz);方法得到唯一列,getUniqueColumn(claz);内部是通过调用getDatabaseFields(claz);方法得到HashMap

private <T extends BaseModel> String getUniqueColumn(Class<T> claz) {
    String unique = uniqueMap.get(claz);
    if (TextUtils.isEmpty(unique)) {
        HashMap<DatabaseField, String> map = DatabaseTools.getDatabaseFields(claz);//调用这个方法获得类的映射结构
        if (map != null && map.size() > 0) {
            Iterator<Entry<DatabaseField, String>> iterator = map.entrySet().iterator();
            String tempUnique = "";
            if (iterator != null) {
                while (iterator.hasNext()) {//遍历
                    Entry<DatabaseField, String> entry = iterator.next();
                    DatabaseField field = entry.getKey();
                    if (field != null) {
                        if (TextUtils.isEmpty(tempUnique)) {
                            tempUnique = field.columnName();
                        }
                        if (field.unique()) {//判断是是否是为 true 表示唯一
                            unique = field.columnName();
                            uniqueMap.put(claz, unique);
                            break;
                        }
                    }
                }
            }
            // 如果没有unique的列,就默认第一列作为unique列
            if (TextUtils.isEmpty(unique) && !TextUtils.isEmpty(tempUnique)) {
                unique = tempUnique;
                uniqueMap.put(claz, unique);
            }
        }
    }
    return unique;
}

/**
 * 根据数据模型返回map : key是有效的列 value是列的数据类型
 * @param claz
 * @return
 */
public static HashMap<DatabaseField,String> getDatabaseFields(Class<?> claz){
    HashMap<DatabaseField,String> map = null;
    if(claz != null){
        for(Class<?> clazz = claz ; clazz != Object.class ; clazz = clazz.getSuperclass()) {
            Field[] fields = clazz.getDeclaredFields();
            if (fields != null && fields.length > 0) {
                for (Field field : fields) {
                    if (field != null) {
                        DatabaseField dbField = field.getAnnotation(DatabaseField.class);
                        if (dbField != null) {
                            String columnName = dbField.columnName();
                            if (TextUtils.isEmpty(columnName)) {
                                continue;
                            }
                            String typeSQL = getTypeSQL(field);
                            if (TextUtils.isEmpty(typeSQL)) {
                                continue;
                            }
                            if(map == null){
                                map = new HashMap<DatabaseField, String>();
                            }
                            map.put(dbField, typeSQL);
                        }
                    }
                }
            }
        }
    }
    return map;
}

/**
*获取字段对应的类型
*/
public static String getTypeSQL(Field field) {
    String sql = "";
    if(field != null){
        if (field.getType() == long.class 
                || field.getType() == int.class
                || field.getType() == short.class
                || field.getType() == byte.class
                || field.getType() == boolean.class
                || field.getType() == char.class) {
            sql = "integer";
        } else if (field.getType() == float.class
                || field.getType() == double.class) {
            sql = "real";
        } else if (field.getType() == String.class) {
            sql = "text";
        }else{//全部用gjson转换成字符串
            sql = "text";
        }
    }
    return sql;
}

Update 的具体不同点的操作如下:完整的方法可以看 Github 上 lib 中的实现

if (contentValues != null) {
    String unique = getUniqueColumn(claz);//获取唯一列的列名,这个唯一列,是在 model 里定义的
    if (!TextUtils.isEmpty(unique)) {
        List<DBMsgObject.ContentCondition<T>> contentConditionList = new ArrayList<DBMsgObject.ContentCondition<T>>();
        DBMsgObject.ContentCondition<T> condition = new DBMsgObject.ContentCondition<T>();
        condition.contentValues = contentValues;
        condition.model = t;
        condition.whereClause = unique + " = ?";//增加 update 条件,即更新对应列的数据
        condition.whereArgs = new String[] { contentValues.getAsString(unique) };//condition.whereClause 中的参数值
        contentConditionList.add(condition);
        msgObj.contentConditionList = contentConditionList;
        msgObj.listener = listener;
        Message msg = new Message();
        msg.what = DatabaseOptionType.OPTION_UPDATE.VALUE;//操作类型为更新操作
        msg.obj = msgObj;
        handler.sendMessage(msg);
    }
}

f.替换操作

replace 的操作和 DatabaseManager中的 insert 四个重载方法类似,只是Message 中的类型不同:

msg.what = DatabaseOptionType.OPTION_REPLACE.VALUE;

g.删除操作

delete 的实现方式也比较简单,主要就是加入了删除的条件,具体可以看下面的代码,

public <T extends BaseModel> void delete(Class<T> claz, String whereClause, String[] whereArgs, DBOperateDeleteListener listener) {
    if (claz != null) {
        DBMsgObject<T> msgObj = new DBMsgObject<T>();
        msgObj.claz = claz;
        List<DBMsgObject.ContentCondition<T>> contentConditionList = new ArrayList<DBMsgObject.ContentCondition<T>>();
        DBMsgObject.ContentCondition<T> condition = new DBMsgObject.ContentCondition<T>();
        contentConditionList.add(condition);
        if (!TextUtils.isEmpty(whereClause)) {
            condition.whereClause = whereClause;//删除的条件
        }
        if (whereArgs != null && whereArgs.length > 0) {
            condition.whereArgs = whereArgs;//对应 whereClause 中的占位符
        }
        msgObj.contentConditionList = contentConditionList;
        msgObj.deleteListener = listener;//注意:这里是删除的监听,这里和其他的监听是不同的,DBOperateDeleteListener 中的 onDeleteCallback 主要是反馈了删除的行数。
        Message msg = new Message();
        msg.what = DatabaseOptionType.OPTION_DELETE.VALUE;
        msg.obj = msgObj;
        handler.sendMessage(msg);
    }
}

3、DataBaseHandler.java

这个类的作用就是进行数据库的insert,update,replace,delete的处理。

我们通过 insert 方法来进行分析

private <T extends BaseModel> void insert(Message msg){
    if(msg.obj == null){
        return;
    }
    @SuppressWarnings("unchecked")
    DBMsgObject<T> msgObj = (DBMsgObject<T>)msg.obj;//获取到消息对象
    String tableName = DatabaseTools.getTableName(msgObj.claz);//拿到对应表名
    if(!TextUtils.isEmpty(tableName) && msgObj.contentConditionList != null && msgObj.contentConditionList.size() > 0){
        SQLiteDatabase database = appAppDatabase.getWritableDatabase();
        if (database != null) {
            List<T> successModels = new ArrayList<T>();
            List<T> failModels = new ArrayList<T>();
            database.beginTransaction();
            try{
                for(ContentCondition<T> condition : msgObj.contentConditionList){
                    if (condition != null && condition.contentValues != null) {
                        long id = database.insert(tableName, null, condition.contentValues);//进行插入操作
                        if(id != -1){//插入成功,将对应的model 值放入到 successModels 中
                            successModels.add(condition.model);
                        }else{//失败,放入到failModels
                            failModels.add(condition.model);
                        }
                    }
                }
                database.setTransactionSuccessful();// 设置事务处理成功,不设置会自动回滚不提交
            }finally{
                database.endTransaction();
                postToMainLoop(msgObj.listener,DatabaseOptionType.OPTION_INSERT ,msgObj.claz,successModels, failModels);//失败回调到主线程
            }
        }
    }
}

四个步骤,1.得到 Message 的消息对象,这个 Message 就是我们在DatabaseManager.java中设置的 Message;2. 根据 Class 得到表名;3. 进行数据库插入操作;4. 调用 postToMainLoop(...) 方法回调到主线程中去,postToMainLoop(...)方法如下:

private <T extends BaseModel> void postToMainLoop(final DBOperateAsyncListener listener,final DatabaseOptionType type,final Class<T> claz,final List<T> successModels,final List<T> failModels){
    if(listener != null){
        uiHandler.post(new Runnable() {
            @Override
            public void run() {
                try{
                listener.onPostExecute(type,claz,successModels,failModels);
                }catch(Throwable t){
                    AZusLog.e("DBHandler", t);
                }
            }
        });
    }
}

update 操作在数据库操作的时候增加了条件判断,如下:

int count = database.update(tableName, condition.contentValues, condition.whereClause, condition.whereArgs);
if(count > 0){//更新条数大于0,表示更新成功
    successModels.add(condition.model);
}else{//否则表示更新失败
    failModels.add(condition.model);
}

replace 操作就更加简单啦,直接把 insert 换个单词就行 ^ ^

long id = database.replace(tableName, null, condition.contentValues);
if(id != -1){
    successModels.add(condition.model);
}else{
    failModels.add(condition.model);
}

delete 操作,我们直接代码搞起:

private <T extends BaseModel> void delete(Message msg){
    if(msg.obj == null){
        return;
    }
    @SuppressWarnings("unchecked")
    final DBMsgObject<T> msgObj = (DBMsgObject<T>)msg.obj;
    String tableName = DatabaseTools.getTableName(msgObj.claz);
    if(!TextUtils.isEmpty(tableName) && msgObj.contentConditionList != null && msgObj.contentConditionList.size() > 0){
        SQLiteDatabase database = appAppDatabase.getWritableDatabase();
        if (database != null) {
            int rows = 0;//删除的行数,用于回调到主线程的时候使用
            database.beginTransaction();
            try{
                for(ContentCondition<T> condition : msgObj.contentConditionList){
                    if (condition != null) {//删除操作
                        rows += database.delete(tableName, condition.whereClause, condition.whereArgs);
                    }
                }
                database.setTransactionSuccessful();// 设置事务处理成功,不设置会自动回滚不提交
            }finally{
                database.endTransaction();
                if(msgObj.deleteListener != null){
                    final int tempRows = rows;
                    uiHandler.post(new Runnable() {

                        @Override
                        public void run() {
                            msgObj.deleteListener.onDeleteCallback(msgObj.claz, tempRows);
                        }
                    });
                }
            }
        }
    }
}
  1. DatabaseTools.java

这个类的作用是:

数据库创建时进行建表,建索引: onCreate(...) 方法
数据库升级时,进行升级操作:onUpgrade(...)方法
删除表:alterTable(...)
根据 Class 获取数据库表名

下面我们具体来分析:
数据库创建时的相关操作:
先建表,调用generateCreateTableSQL(claz);方法,再建索引generateCreateTableIndexSQL(claz)方法。具体实现如下:

/**
 * 创建表和相应的索引
 * @param claz
 * @param db
 */
public static void onCreate(SQLiteDatabase db,Class<?> claz){
    String tableSQL = generateCreateTableSQL(claz);//根据数据模型获取创建表的sql语句
    if(!TextUtils.isEmpty(tableSQL) && db != null){
        db.execSQL(tableSQL);//执行 SQL
    }
    ArrayList<String> indexSQLList = generateCreateTableIndexSQL(claz);//根据数据模型获取表的索引
    if(indexSQLList != null && indexSQLList.size() > 0 && db != null){
        for(String indexSQL : indexSQLList){
            if(!TextUtils.isEmpty(indexSQL)){
                db.execSQL(indexSQL);
            }
        }
    }
}
/**
 * 根据数据模型获取创建表的sql语句
 */
public static String generateCreateTableSQL(Class<?> claz) {
    String sql = "";
    ArrayList<String> fieldsSQL = getFieldsSQL(claz);//通过类反射的方式获取建表的字段名称
    String tableName = getTableName(claz);
    if(fieldsSQL != null && fieldsSQL.size() > 0 && !TextUtils.isEmpty(tableName)){//拼接字符串
        sql = "create table if not exists " + tableName + " (";
        for(String temp : fieldsSQL){
            sql += temp;
        }
        int index = sql.lastIndexOf(",");
        if (index != -1) {
            sql = sql.substring(0, index);
        }
        sql += ");";
    }
    return sql;
}
/**
 * 根据数据模型获取表的索引
 * @param claz
 * @return
 */
public static ArrayList<String> generateCreateTableIndexSQL(Class<?> claz) {
    ArrayList<String> indexList = null;
    if (claz != null) {
        String tableName = getTableName(claz);
        if (!TextUtils.isEmpty(tableName)) {
            HashMap<DatabaseField,String> map = getDatabaseFields(claz);
            if(map != null && map.size() > 0){
                Iterator<Entry<DatabaseField,String>> iterator = map.entrySet().iterator();
                if(iterator != null){
                    while(iterator.hasNext()){
                        Entry<DatabaseField,String> entry = iterator.next();
                        if(entry != null){
                            DatabaseField key = entry.getKey();
                            if(key != null && key.index()){
                                if (indexList == null) {
                                    indexList = new ArrayList<String>();
                                }
                                String columnName = key.columnName();
                                String createIndex = "create index if not exists index_"
                                        + columnName
                                        + " on "
                                        + tableName
                                        + "(" + columnName + ");";
                                indexList.add(createIndex);
                            }
                        }
                    }
                }
            }
        }
    }
    return indexList;
}

主要的代码都已经给出,应该都能理解,文章有点长,但其实代码占了大部分的篇幅。整个项目并不是非常复杂,甚至连流程图都不需要画。

嗯,KopDB 框架的分析到这里就结束啦~~~

标签: android

热门推荐