1. 程式人生 > >Android SQLite資料儲存的通用設計

Android SQLite資料儲存的通用設計

SQLite用於儲存一些資料量較多,結構比較複雜情況,使用的時候只需要實現SQLiteOpenHelper,在onCreate建立資料表,onUpgrade做升級處理

通過Helper例項對DB進行資料處理,例如,database = dbHelper.getWritableDatabase();  獲取DB物件進行插入,更新,刪除操作,dbHelper.getReadableDatabase()

進行資料查詢,在此不必多說,這樣實現一個數據庫並不複雜,但是對不同物件儲存操作還需要分別各自去自己實現,比較麻煩,能不能用一種通用設計實現呢?

其實存入DB內的資料都是要分隔成基本String, int , long, double等,在android中可以使用資料集ContentValues進行儲存,ContentValues可以儲存基本型別,類似於Map

ContentValues是可直接用於SQLiteDatabase,ContentProvider中進行批量處理,這是android為此設計的,在SQLiteDatabase我們使用ContentValues是非常簡單的。

好了下面我們需要對通用資料操作定義一種規則,我們只需要傳入tableName,whereArgs(篩選條件),ContentValues即可實現insert, quere,delete,update功能

介面定義如下:

public interface IDBDelegate {

    public boolean insertData(String tableName,String nullColumnHack,ContentValues values);

    public boolean deleteData(String tableName,String whereClause, String[] whereArgs);

    public boolean updateData(String tableName,ContentValues values, String whereClause, String[] whereArgs);

    //查詢單條資料集
    public Map<String, String> getRowData(String tableName, String selection, String[] selectionArgs);

    //查詢多條資料集
    public List<Map<String, String>> getListData(String tableName, String selection, String[] selectionArgs);

}


實現類

public class DBDelegateImpl implements IDBDelegate{

    private final Object obj=new Object();
    private DBHelper dbHelper=null;

    public DBDelegateImpl(Context context) {
        dbHelper=DBHelper.getInstance(context);
    }

    @Override
    public boolean insertData(String tableName,  String nullColumnHack,ContentValues values) {
        synchronized (obj) {
            boolean flag = false;
            SQLiteDatabase database = null;
            long id = -1;
            try {
                database = dbHelper.getWritableDatabase();
                id = database.insert(tableName, nullColumnHack, values);
                flag = (id != -1);
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                if (database != null) {
                    database.close();
                }
            }
            return flag;
        }
    }

    public int insertBatchData(String tableName,  String nullColumnHack,List<ContentValues> values) {
        int count=0;
        if(values!=null && values.size()>0){
            SQLiteDatabase db = dbHelper.getWritableDatabase();
            try {
                db.beginTransaction();
                ContentValues cv = null;
                for (int i = 0; i < values.size(); i++) {
                    cv = values.get(i);
                    db.insert(tableName, nullColumnHack, cv);
                }
                db.setTransactionSuccessful();
            }finally {
                if(db!=null) {
                    db.endTransaction();
                }
            }
        }
       return count;
    }

    @Override
    public boolean deleteData(String tableName, String whereClause, String[] whereArgs) {
        synchronized (obj) {
            boolean flag = false;
            SQLiteDatabase database = null;
            int count = 0;
            try {
                database = dbHelper.getWritableDatabase();
                count = database.delete(tableName, whereClause, whereArgs);
                flag = (count > 0);
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                if (database != null) {
                    database.close();
                }
            }
            return flag;
        }
    }

    @Override
    public boolean updateData(String tableName, ContentValues values, String whereClause, String[] whereArgs) {
        synchronized (obj) {
            boolean flag = false;
            SQLiteDatabase database = null;
            int count = 0;
            try {
                database = dbHelper.getWritableDatabase();
                count = database.update(tableName, values, whereClause, whereArgs);
                flag = (count > 0);
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                if (database != null) {
                    database.close();
                }
            }
            return flag;
        }
    }

    @Override
    public Map<String, String> getRowData(String tableName, String selection, String[] selectionArgs) {
        SQLiteDatabase database = null;
        Cursor cursor = null;
        Map<String, String> map = new HashMap<String, String>();

        try {
            database = dbHelper.getReadableDatabase();
            cursor = database.query(true, tableName, null, selection, selectionArgs, null,
                    null, null, null); //查詢單條記錄,記錄是唯一的,所以第一個引數置為 true.
            int cols_len = cursor.getColumnCount();
            while (cursor.moveToNext()) {
                for (int i = 0; i < cols_len; i++) {
                    String cols_name = cursor.getColumnName(i);
                    String cols_values = cursor.getString(cursor.getColumnIndex(cols_name));
                    if (cols_values == null) {
                        cols_values = "";
                    }
                    map.put(cols_name, cols_values);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(cursor!=null){
                cursor.close();
            }
            if (database != null) {
                database.close();
            }
        }
        return map;
    }

    @Override
    public List<Map<String, String>> getListData(String tableName, String selection, String[] selectionArgs) {
        SQLiteDatabase database = null;
        Cursor cursor = null;
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        try {
            database = dbHelper.getReadableDatabase();
            cursor = database.query(false, tableName, null, selection, selectionArgs, null,
                    null, null, null); //查詢所有記錄,所以有重複的資料也要全部檢出,所以第一引數置為false.
            int cols_len = cursor.getColumnCount();
            while (cursor.moveToNext()) {
                Map<String, String> map = new HashMap<String, String>();
                for (int i = 0; i < cols_len; i++) {
                    String cols_name = cursor.getColumnName(i);
                    String cols_values = cursor.getString(cursor.getColumnIndex(cols_name));
                    if (cols_values == null) {
                        cols_values = "";
                    }
                    map.put(cols_name, cols_values);
                }
                list.add(map);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(cursor!=null){
                cursor.close();
            }
            if (database != null) {
                database.close();
            }
        }
        return list;
    }

}


嗯,比較優雅的實現了通用設計實現,還不錯

DBHelper實現:

public class DBHelper extends SQLiteOpenHelper {

    private final static String DB_NAME = "Program_db";
    public final static int DB_VERSION = 1;
    private static DBHelper mInstance=null;
    private DBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    public static  DBHelper getInstance(Context context) {
        if (mInstance == null) {
            synchronized (DBHelper.class) {
                if (mInstance == null) {
                    mInstance = new DBHelper(context);
                }
            }
        }
        return mInstance;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        final String sql2="CREATE TABLE "+TABLE_SMILFILE+"(" + SID  
                + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
                FILE_VER+" TEXT,"+
                FILE_SYNC+" TEXT,"+
                FILE_DURATION+" INTEGER,"+
                FILE_SRC+" TEXT"+
                ");";
    //    LogUtils.debug("create taskTable", "smilSql="+sql2);
        db.execSQL(sql2); 

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      //  db.execSQL("DROP TABLE IF EXISTS " + TABLE_PROGRAM);
    }

}


使用案例:
 public void DBInsertTest(Context ctx){
        IDBDelegate dao=new DBDelegateImpl(ctx);
        ContentValues values=new ContentValues();
        values.put("TASK_NAME", "PlayerName");
        dao.insertData("table1",null,values);
    }

    public void DBUpdateTest(Context ctx,String sid){
        IDBDelegate dao=new DBDelegateImpl(ctx);
        ContentValues values=new ContentValues();
        values.put("TASK_NAME", "PlayerName1");
        values.put("TASK_TYPE", "Type1");
        dao.updateData("table1",values,"sid=?",new String[]{sid});
    }
    public void DBDeleteTest(Context ctx,String sid){
        IDBDelegate dao=new DBDelegateImpl(ctx);
        dao.deleteData("table1","sid=?",new String[]{sid});
    }
    
    public void DBQureTest(){
        List<Map<String, String>> listTypeData=null;
        //" id = ? ", new String[] { "2" }  
        IDBDelegate dao=new DBDelegateImpl(ctx);
        listTypeData=dao.getListData(AdsDatabase.TABLE_PROGRAM, AdsDatabase.TASK_TYPE+" = ?",
                new String[] {String.valueOf(Constance.PLAY_TYPE_INSERT)});
    }


2016-08-14 22:20更新說明

以上資料庫還可以優化一點處理,在資料庫讀取與寫入時沒有必要同步處理(即讀取資料庫不加鎖,對資料庫內容修改操作加鎖處理)採用讀寫分離實現,也稱為COW模式。這時可能會產生讀取是舊資料,修改內容後新資料,無法更新,我們可以使用observe來資料內容變化監聽來及時確保資料為最新。其實在多執行緒併發訪問時這種全部通過一個物件鎖實現效率很低,讀寫分離能夠明顯提高程式效率。

SqlLite一些問題:

1,使用多個SQLiteOpenHelper問題,例如:

// Thread 1
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();
 // Thread 2
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();

使用多個DBHelper寫入資料時,會發生寫入失敗問題,程式並不會報異常,Logcat會有一個輸出

android.database.sqlite.SQLiteDatabaseLockedException: database is locked

由此可見DBHelper只能有一個例項物件存在,建議使用單例維護

2,關於在SQLite上資料庫連線池的問題

學過java web都知道,資料庫連線池可以提高程式效能,網站併發訪問需要使用,但是在android只能同時存在一個DB connection,即one helper,one connection at the same time,otherwise one fail