我的博客:http://mrfufufu.github.io/
上次我们主要是对 KopDB
框架的使用进行了分析,它是非常简单有用的。这次主要是对它的源码进行分析,进一步的来了解它的真面目。
因为 KopDB
采用的是对象关系映射(ORM)模式,即我们使用的编程语言是面向对象语言,而我们使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了。
使用 ORM 模式,主要是因为我们平时多用面向对象编程,只有少部分人才会比较精通关系型数据库。通过这种模式,可以让我们不考虑 SQL 语言的具体实现,从而专注于业务实现。
根据 ORM 模式的理念,我们每张表都应该对应一个模型,即,如果我们想要建一张 Person 表,就应该有一个 Person 类。
所有的模型都应继承这个类,这个类的主要有两个方法:
getContentValues(): ContentValues
:为将要进行的数据库操作获取表的字段和对应的值。方法是将对应的 model 通过反射的形式获取到它的列名和值,返回的是 ContentValues 对象。
getModels(Cursor curor):List<T>
: 为查询数据库操作以后得到的数据进行解析,返回对应的 model 类型 的List。方法是将查询完数据库以后会得到一个 Cursor,所有的查询操作得到值都通过这个变量返回,对这个 cursor 中的对应列的值映射到对应的 model 中去。
最主要用到的类就是DatabaseManager
类的,通过这类,我们完全了所有的数据库的操作,包括建表,增删查改等操作。注意,需要通过单例得到它的实例,主要方法有:
initDataBase(Context context, String dbName, int version, List<Class<?>> models)
数据库的初始化相关的操作,dbName为数据库的名称,version 就是数据库的版本,models 为需要映射到数据库中去的 model,具体映射方法我们在DatabaseTools.java
类中来讲。
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 数据,即完成。
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)
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;
}
大部分和 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);
}
}
replace 的操作和 DatabaseManager
中的 insert
四个重载方法类似,只是Message 中的类型不同:
msg.what = DatabaseOptionType.OPTION_REPLACE.VALUE;
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);
}
}
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);
}
}
});
}
}
int count = database.update(tableName, condition.contentValues, condition.whereClause, condition.whereArgs);
if(count > 0){//更新条数大于0,表示更新成功
successModels.add(condition.model);
}else{//否则表示更新失败
failModels.add(condition.model);
}
long id = database.replace(tableName, null, condition.contentValues);
if(id != -1){
successModels.add(condition.model);
}else{
failModels.add(condition.model);
}
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);
}
});
}
}
}
}
}
DatabaseTools.java
这个类的作用是:
onCreate(...)
方法onUpgrade(...)
方法alterTable(...)
下面我们具体来分析:
数据库创建时的相关操作:
先建表,调用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 框架的分析到这里就结束啦~~~
原文地址:http://blog.csdn.net/fu222cs98/article/details/46424651