android sqlite數據庫封裝 實現crud


android常用的數據保存方式有文件、sharepreferences、數據庫、網絡、contentprovider集中方式。

       文件存儲方式,經常使用在緩存整個頁面數據,比如電子書內容、html數據等。

       sharepreferrences存儲方式,實質也就是xml文件存儲的封裝,常用於存儲配置參數數據。當然也可以用文件存儲+Properties來存儲參數數據。

       網絡,就是將數據存儲在網絡空間上。

        contentprovider主要作用是統一數據存儲方式,實現數據共享,以後有機會仔細分析下。

        數據庫的方式,常用在存儲一系列的結構複雜的數據,輕量級的數據庫SQlit使用起來還是比較簡單,但是總想能像hibernate似的框架可以進行下封裝,實現orm並且可以實現簡單的rcud。這裏就介紹下一個分裝過程,源碼下載在後面。

 一、封裝數據庫的類結構

 

結構比較簡單:BaseBean.java--實體類的基類

                       DBConfig.java---數據庫的參數,包括數據庫名、要創建的表列表、數據庫版本等

                       IssContentProvider.java--繼承了ContentProvider,定義了數據庫的初始化和操作

                       DBFactory--數據庫工廠類

                       Table.java----定義Table的annotation

                       TableColumn.java--定義表的列的annotation和屬性

                       TableUtil.java--書庫操作工具類,主要是獲得表和根據標籤拼裝sql語句

二、封裝數據庫的使用過程

    由於封裝後的數據庫使用比較簡單,就跟配置好hibernate之後使用似的,所以咱們先看下咱們使用,不理解的地方,等分析了整個實現過程後就行清晰了。

     1、要實現orm,肯定要定義帶標籤的實體類,當然是繼承BaseBean類。

     2、要將數據庫參數傳遞給DBConfig,並初始化。

     3、數據庫操作類,通過contentprovideder實現crud。

用例子看下
1,定義實體類

public class SmartDownloadBean extends BaseBean<SmartDownloadBean> {

    @TableColumn(type = TableColumn.Types.TEXT, isIndex = true, isNotNull = true)
    public String downpath;

    @TableColumn(type = TableColumn.Types.INTEGER)
    public int threadid;
    
    @TableColumn(type = TableColumn.Types.INTEGER)
    public int downlength;
	
	@Override
	public SmartDownloadBean parseJSON(JSONObject jsonObj) {
		return null;
	}

	@Override
	public JSONObject toJSON() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public SmartDownloadBean cursorToBean(Cursor cursor) {
		this.downpath = cursor.getString(cursor.getColumnIndex("downpath"));
        this.threadid = cursor.getInt(cursor.getColumnIndex("threadid"));
        this.downlength = cursor.getInt(cursor.getColumnIndex("downlength"));
		return this;
	}

	@Override
	public ContentValues beanToValues() {
		ContentValues values = new ContentValues();
        if (!TextUtils.isEmpty(downpath)) {
            values.put("downpath", downpath);
        }
        if (!TextUtils.isEmpty(threadid+"")) {
            values.put("threadid", threadid);
        }
        if (!TextUtils.isEmpty(downlength+"")) {
            values.put("downlength", downlength);
        }
        return values;
	}

}

實體類通過標籤,定義了對應表的列名、及列的屬性

2、定義要創建的表、數據名等參數

/**
 * 數據庫配置
 **/
public class SssProvider extends IssContentProvider {

	@Override
	public void init() {

		// 數據庫相關參數設置
		DBConfig config = new DBConfig.Builder()
				.addTatble(SmartDownloadBean.class)
				.setName("sss.db").setVersion(2)
				.setAuthority("com.sss").build();

		IssDBFactory.init(getContext(), config);
	}

    
	

}

要定義都個表的話,再addTatble(Bean.class)即可。

3、調用數據庫的工具類

/**
 * 操作數據庫的utils
 * 
 * @author dllik 2013-11-23
 */
public class DBUtils {

    public static Uri URI_SMARTDOWNLOAD = IssContentProvider.buildUri(SmartDownloadBean.class);



    /**
     * 獲取每條線程已經下載的文件長度
     * 
     * @param context
     * @param downpath
     * @return
     */
    public static Map<Integer, Integer> querySmartDownData(Context context, String downpath) {
        ContentResolver mResolver = context.getContentResolver();
        Cursor cursor = mResolver.query(URI_SMARTDOWNLOAD, null, "downpath=?", new String[] {
            downpath
        }, null);
        Map<Integer, Integer> data = new HashMap<Integer, Integer>();
        while (cursor.moveToNext()) {
            SmartDownloadBean bean = new SmartDownloadBean();
            bean.cursorToBean(cursor);
            data.put(bean.threadid, bean.downlength);
        }
        cursor.close();
        return data;
    }

    /**
     * 保存每條線程已經下載的文件長度
     * 
     * @param context
     * @param path
     * @param map
     */
    public static void insertSmartDown(Context context, String path, Map<Integer, Integer> map) {
        ContentResolver mResolver = context.getContentResolver();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            SmartDownloadBean bean = new SmartDownloadBean();
            bean.downpath = path;
            bean.downlength = entry.getValue();
            bean.threadid = entry.getKey();
            mResolver.insert(URI_SMARTDOWNLOAD, bean.beanToValues());
        }
    }

    /**
     * 實時更新每條線程已經下載的文件長度
     * 
     * @param context
     * @param path
     * @param map
     */
    public static void updateSmartDown(Context context, String path, Map<Integer, Integer> map) {
        ContentResolver mResolver = context.getContentResolver();
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            SmartDownloadBean bean = new SmartDownloadBean();
            bean.downpath = path;
            bean.downlength = entry.getValue();
            bean.threadid = entry.getKey();
            mResolver.update(URI_SMARTDOWNLOAD, bean.beanToValues(), "downpath=? and threadid=?",
                    new String[] {
                            bean.downpath, bean.threadid + ""
                    });
        }
    }

    /**
     * 當文件下載完成後,刪除對應的下載記錄
     * 
     * @param context
     * @param path
     */
    public static void deleteSmartDown(Context context, String path) {
        ContentResolver mResolver = context.getContentResolver();
        mResolver.delete(URI_SMARTDOWNLOAD, "downpath=?", new String[] {
            path
        });
    }

   

}

三、數據庫的封裝過程

    看下實體類的基類

public abstract class BaseBean<T> implements Serializable {
    private static final long serialVersionUID = -804757173578073135L;

    @TableColumn(type = TableColumn.Types.INTEGER, isPrimary = true)
    public static final String _ID = "_id";

    /**
     * 將json對象轉化爲Bean實例
     *
     * @param jsonObj
     * @return
     */
    public abstract T parseJSON(JSONObject jsonObj);

    /**
     * 將Bean實例轉化爲json對象
     *
     * @return
     */
    public abstract JSONObject toJSON();

    /**
     * 將數據庫的cursor轉化爲Bean實例(如果對象涉及在數據庫存取,需實現此方法)
     *
     * @param cursor
     * @return
     */
    public abstract T cursorToBean(Cursor cursor);

    /**
     * 將Bean實例轉化爲一個ContentValues實例,供存入數據庫使用(如果對象涉及在數據庫存取,需實現此方法)
     *
     * @return
     */
    public abstract ContentValues beanToValues();

    @SuppressWarnings("unchecked")
    public T parseJSON(Gson gson, String json) {
        return (T) gson.fromJson(json, this.getClass());
    }

    public ContentValues toValues() {
        ContentValues values = new ContentValues();
        try {
            Class<?> c = getClass();
            Field[] fields = c.getFields();
            for (Field f : fields) {
                f.setAccessible(true);
                final TableColumn tableColumnAnnotation = f.getAnnotation(TableColumn.class);
                if (tableColumnAnnotation != null) {
                    if (tableColumnAnnotation.type() == TableColumn.Types.INTEGER) {
                        values.put(f.getName(), f.getInt(this));
                    } else if (tableColumnAnnotation.type() == TableColumn.Types.BLOB) {
                        values.put(f.getName(), (byte[]) f.get(this));
                    } else if (tableColumnAnnotation.type() == TableColumn.Types.TEXT) {
                        values.put(f.getName(), f.get(this).toString());
                    } else {
                        values.put(f.getName(), f.get(this).toString());
                    }
                }
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return values;
    }


}

說明幾點:1、用到了泛型,因爲定義的數據實體類有多個

                 2、實現序列化,實體類數據通過Intent進行傳遞

                 3、定義了一個主鍵id

數據庫參數類

public class DBConfig {
    final ArrayList<Class<? extends BaseBean<?>>> tableList;

    final String dbName;

    final int dbVersion;
    
    final String authority;
    
    final ArrayList<String> tableNameList;

    private DBConfig(final Builder builder) {
        tableList = builder.tableList;
        dbName = builder.dbName;
        dbVersion = builder.dbVersion;
        authority = builder.authority;
        
        tableNameList =  new ArrayList<String>();
        for(Class<? extends BaseBean<?>> c:tableList){
            String name = TableUtil.getTableName(c);
            tableNameList.add(name);
        }
    }

    public static class Builder {
        private ArrayList<Class<? extends BaseBean<?>>> tableList;

        private String dbName;

        private int dbVersion;
        
        private String authority = "com.iss.mobile";

        public Builder() {
            tableList = new ArrayList<Class<? extends BaseBean<?>>>();
        }

        public Builder setName(String name) {
            dbName = name;
            return this;
        }

        public Builder setVersion(int version) {
            dbVersion = version;
            return this;
        }

        public Builder addTatble(Class<? extends BaseBean<?>> table) {
            tableList.add(table);
            return this;
        }
        
        public Builder setAuthority(String authority){
            this.authority = authority;
            return this;
        }
        
        public DBConfig build(){
            return new DBConfig(this);
        }
    }

}

通過該類,來設置數據庫的參數,在初始化數據庫的時候用到。

內容提供者類,初始化和操作數據庫

public abstract class IssContentProvider extends ContentProvider {
    public static String CONTENT_TYPE = "vnd.android.cursor.dir/iss.db";

    protected SQLiteDatabase mDB;

    public static String AUTHORITY = "com.iss.mobile";

    @Override
    public boolean onCreate() {
         init();
        IssDBFactory issDBFactory = IssDBFactory.getInstance();
        DBConfig config = IssDBFactory.getInstance().getDBConfig();
        if (config == null) {
            throw new RuntimeException("db factory not init");
        }
        AUTHORITY = config.authority;
        CONTENT_TYPE = "vnd.android.cursor.dir/" + config.dbName;
        mDB = issDBFactory.open();
        return true;
    }

    public abstract void init();

    public static final String SCHEME = "content";

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        String tableName = getTableName(uri);
        long result = mDB.insert(tableName, null, values);
        if (result != -1) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return buildResultUri(tableName, result);
    }
    
    @Override
    public int bulkInsert(Uri uri, ContentValues[] values) {
        mDB.beginTransaction();
        String tableName = getTableName(uri);
        for(ContentValues value:values){
            mDB.insert(tableName, null, value);
        }
        mDB.setTransactionSuccessful();
        mDB.endTransaction();
        return values.length;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
            String sortOrder) {
        String tableName = getTableName(uri);
        return mDB.query(tableName, projection, selection, selectionArgs, null, null, sortOrder);
    }

    @Override
    public String getType(Uri uri) {
        return CONTENT_TYPE;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        String tableName = getTableName(uri);
        int result = mDB.delete(tableName, selection, selectionArgs);
        if (result != 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return result;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        String tableName = getTableName(uri);
        int result = mDB.update(tableName, values, selection, selectionArgs);
        if (result != 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return result;
    }

    private Uri buildResultUri(String tableName, long result) {
        final Uri.Builder builder = new Uri.Builder();
        DBConfig config = IssDBFactory.getInstance().getDBConfig();
        if (config == null) {
            throw new RuntimeException("db factory not init");
        }
        builder.scheme(SCHEME);
        builder.authority(config.authority);
        builder.path(tableName);
        builder.appendPath(String.valueOf(result));
        return builder.build();
    }

    private String getTableName(Uri uri) {
        DBConfig config = IssDBFactory.getInstance().getDBConfig();
        if (config == null) {
            throw new RuntimeException("db factory not init");
        }
        String path = uri.getLastPathSegment();
        if (!config.tableNameList.contains(path)) {
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
        return path;
    }

    public static Uri buildUri(String path, String id) {
        final Uri.Builder builder = new Uri.Builder();
        DBConfig config = IssDBFactory.getInstance().getDBConfig();
        if (config == null) {
            throw new RuntimeException("db factory not init");
        }
        builder.scheme(SCHEME);
        builder.authority(config.authority);
        builder.path(path);
        builder.appendPath(id);
        return builder.build();
    }

    public static Uri buildUri(String path) {
        final Uri.Builder builder = new Uri.Builder();
        DBConfig config = IssDBFactory.getInstance().getDBConfig();
        if (config == null) {
            throw new RuntimeException("db factory not init");
        }
        builder.scheme(SCHEME);
        builder.authority(config.authority);
        builder.path(path);
        return builder.build();
    }

    public static Uri buildUri(Class<? extends BaseBean<?>> c) {
        final String tableName = TableUtil.getTableName(c);
        return buildUri(tableName);
    }

}

該內容提供者在創建的時候,先執行init()(將要數據庫的參數設置好,再將參數傳遞給工廠類),工廠類根據參數創建數據庫mDB = issDBFactory.open();
數據庫工廠類:

public class IssDBFactory {
	private static final String TAG = IssDBFactory.class.getSimpleName();

	private DBConfig mConfig;
	private SQLiteDatabase mSQLiteDB;

	private IssDBOpenHelper mDBOpenHelper;

	private final Context mContext;
	
	private static IssDBFactory instance ;

	private IssDBFactory(Context context) {
		mContext = context;
	}
	
	public static void init(Context context,DBConfig dbConfig){
	    if(instance==null){
	        instance = new IssDBFactory(context.getApplicationContext());
	        instance.setDBConfig(dbConfig);
	    }
	}
	
	public static IssDBFactory getInstance(){
	    return instance;
	}
	
	
	public void setDBConfig(DBConfig dbConfig){
	    mConfig = dbConfig;
	}
	
	public DBConfig getDBConfig(){
	    return mConfig;
	}

	public SQLiteDatabase open() {
	    if(mSQLiteDB==null){
	        mDBOpenHelper = new IssDBOpenHelper(mContext, mConfig.dbName, null, mConfig.dbVersion);
	        mSQLiteDB = mDBOpenHelper.getWritableDatabase();
	    }
		return mSQLiteDB;
	}

	public void close() {
	    if(mDBOpenHelper!=null){
	        mDBOpenHelper.close();
	    }
	}

	public void beginTransaction() {
	    if(mSQLiteDB==null){
	        mSQLiteDB.beginTransaction();
	    }
	}

	public void endTransaction() {
		if (mSQLiteDB==null&&mSQLiteDB.inTransaction()) {
			mSQLiteDB.endTransaction();
		}
	}

	public void setTransactionSuccessful() {
	    if (mSQLiteDB==null){
	        mSQLiteDB.setTransactionSuccessful();
	    }
	}

	private final class IssDBOpenHelper extends SQLiteOpenHelper {

		public IssDBOpenHelper(Context context, String name, CursorFactory factory, int version) {
			super(context, name, factory, version);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			for (Class<? extends BaseBean<?>> table : mConfig.tableList) {
				try {
					for (String statment : TableUtil.getCreateStatments(table)) {
						Log.d(TAG, statment);
						db.execSQL(statment);
					}
				} catch (Throwable e) {
					Log.e(TAG, "Can't create table " + table.getSimpleName());
				}
			}

			/**
			 * 初始化數據
			 */
			// initData();

		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			Log.d(TAG, "onUpgrade: " + oldVersion + " >> " + newVersion);
			for (Class<? extends BaseBean<?>> table : mConfig.tableList) {
				try {
					db.execSQL("DROP TABLE IF EXISTS " + TableUtil.getTableName(table));
				} catch (Throwable e) {
					Log.e(TAG, "Can't create table " + table.getSimpleName());
				}
			}
			onCreate(db);
		}

	}

	public void cleanTable(String tableName, int maxSize, int batchSize) {
		Cursor cursor = mSQLiteDB.rawQuery("select count(_id) from " + tableName, null);
		if (cursor.getCount() != 0 && cursor.moveToFirst() && !cursor.isAfterLast()) {
			if (cursor.getInt(0) >= maxSize) {
				int deleteSize = maxSize - batchSize;
				mSQLiteDB.execSQL("delete from " + tableName + " where _id in (" + "select _id from " + tableName
						+ " order by _id " + "  limit " + deleteSize + " )");
			}
		}
		cursor.close();
	}

 

看到這用過數據庫的就比較清晰了,用到了SQLiteOpenHelper是以內部類的形式實現的,在oncreat裏創建了表,在onupgrade裏實現了更新表。

其中用到TableUtil.getCreateStatments(table),

數據庫工具類:

 

public class TableUtil {

    public static String getTableName(Class<? extends BaseBean<?>> c) {
        String name = null;
        Table tableNameAnnotation = c.getAnnotation(Table.class);
        if (tableNameAnnotation != null) {
            name = tableNameAnnotation.name();
        }
        if (TextUtils.isEmpty(name)) {
            name = c.getSimpleName();
        }
        return name;
    }

    /**
     * 拼裝sql用的建表語句以及索引語句
     * 
     * @param c
     * @return
     */
    public final static List<String> getCreateStatments(Class<? extends BaseBean<?>> c) {
        final List<String> createStatments = new ArrayList<String>();
        final List<String> indexStatments = new ArrayList<String>();

        final StringBuilder builder = new StringBuilder();
        final String tableName = getTableName(c);
        builder.append("CREATE TABLE ");
        builder.append(tableName);
        builder.append(" (");
        int columnNum = 0;
        for (final Field f : c.getFields()) {
            f.setAccessible(true);
            final TableColumn tableColumnAnnotation = f.getAnnotation(TableColumn.class);
            if (tableColumnAnnotation != null) {
                columnNum++;
                String columnName = f.getName();
                builder.append(columnName);
                builder.append(" ");
                if (tableColumnAnnotation.type() == TableColumn.Types.INTEGER) {
                    builder.append(" INTEGER");
                } else if (tableColumnAnnotation.type() == TableColumn.Types.BLOB) {
                    builder.append(" BLOB");
                } else if (tableColumnAnnotation.type() == TableColumn.Types.TEXT) {
                    builder.append(" TEXT");
                } else {
                    builder.append(" DATETIME");
                }
                if (tableColumnAnnotation.isPrimary()) {
                    builder.append(" PRIMARY KEY");
                } else {
                    if (tableColumnAnnotation.isNotNull()) {
                        builder.append(" NOT NULL");
                    }
                    if (tableColumnAnnotation.isUnique()) {
                        builder.append(" UNIQUE");
                    }
                }
                if (tableColumnAnnotation.isIndex()) {
                    indexStatments.add("CREATE INDEX idx_" + columnName + "_" + tableName + " ON "
                            + tableName + "(" + columnName + ");");
                }
                builder.append(", ");
            }
        }
        builder.setLength(builder.length() - 2); // remove last ','
        builder.append(");");
        if (columnNum > 0) {
            createStatments.add(builder.toString());
            createStatments.addAll(indexStatments);
        }
        return createStatments;
    }

}

就兩個方法,獲取表名,在更新表的時候用到,拼裝sql在創建表時候用到。
最後兩個標籤類:

@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
    String name();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface TableColumn {
    public enum Types {
        INTEGER, TEXT, BLOB, DATETIME
    }

    Types type() default Types.TEXT;

    boolean isPrimary() default false;

    boolean isIndex() default false;

    boolean isNotNull() default false;

       boolean isUnique() default false;
    
}

思路比較簡單,就是注意先標籤、過濾器、內容提供者的使用就行了。

最後是封裝包代碼,使用過程沒有加,自己加入吧:http://download.csdn.net/detail/xiangxue336/7001299

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章