Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 如何將Android數據庫操作通用化(一)

如何將Android數據庫操作通用化(一)

編輯:關於Android編程

概述 小小說明 一別之後二地相懸 都說是三四月誰又知五六年 七弦琴無心彈八行書不可傳 九連環從中折斷十裡長亭望眼欲穿 披荊斬棘

概述

在開始考慮Android的數據庫操作之前,我們先回想一下Web方面的數據庫操作。如果我們只是停留在JDBC的簡單使用和封裝上(比如純JDCB,或者DBUtils),即使我們對數據庫的增刪改查操作進行了接口的抽取,代碼依舊會和業務有很強的耦合性。

經過我們分析,解除耦合性的關鍵在於如何解決自動映射“實體類 與 數據庫表”之間的對應關系。如果能夠做到這一步,那麼我們就能夠更好的解耦了,也能降低我們的代碼重復率。

如果我們再跨前一步,使用更為優秀的框架(比如:Hibernate),這一切都會變得簡單和方便。Hibernate為我們提供了兩種建立“實體類(bean) - 數據庫表”之間關系的方式。

一種是使用配置文件,在配置文件中對表名、字段、實體間的關系進行配置,Hibernate就能夠為我們做好一切,下面提供一段配置文件示例:

 
        
    

另一種,則是使用注解的方式。Hibernate為我們提供了@Entity@Table@Id@Column@GeneratedValue諸如此類很多的注解。我們只需要在合適的地方加上這些注解,為我們的類、字段配置好關系,Hibernate也能夠為我們做好一切,下面提供一段配置文件示例:

@Entity(name=brand) /* 實體bean */
@Table(name=brand_t) /* 指定生成表的名字*/
public class Brand implements Serializable {

    /** @Fields code : ID使用UUID生成 **/
    private String code;
    /** @Fields name : 品牌名稱 **/
    private String name;
    /** @Fields visible : 是否可見 **/
    private Boolean visible = true;
    /** @Fields logopath : logo路徑 **/
    private String logopath;

    public Brand(String name, String logopath) {
        this.name = name;
        this.logopath = logopath;
    }
    @Id
    @Column(name=code, length=36) /* UUID是36 */
    @GeneratedValue(generator = system-uuid)   
    @GenericGenerator(name = system-uuid, strategy = uuid) 
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    @Column(length=40, nullable=false)
    public String getName() {
        return name;
    }
    @Column(nullable=false)
    public Boolean getVisible() {
        return visible;
    }
    /*/images/brand/2012/12/8/9/sss.gif */
    @Column(length=80,nullable=true)
    public String getLogopath() {
        return logopath;
    }

}

番外話說完,回到我們熱愛的Android中,我們應該如何在Android寫出一個通用的數據庫增刪改查操作,可能也需要借鑒Hibernate的優秀思想。

不過,世間萬物都是一步一步進行演化的,我們的代碼同樣是如此,從不通用到通用,我會通過幾篇博客一一向大家展示,但是由於本人經驗和學識有限,無法構建出一個完整的開源項目,望大家海涵。有興趣的可以聯系我大家一起嘗試做一下(每當你在感歎,如果有這樣一個東西就好了的時候,請注意,其實這是你的機會)。

小小說明

整個例子會以一條新聞實體作為基本元素,並貫穿整個例子。新聞實體很簡單,我們給它起個名字叫做News,字段由idtitlesummary構成。以下是示例代碼:


public class News {

    private int id;

    private String title;

    private String summary;

    // 此處省略get、set方法
}

另外,在SQLite數據庫中,存東西絕大多數是String類型的,只有涉及到主鍵、自增的才會使用int。

最後,想章節名太痛苦,就在網上找了一些有趣的詩詞作為章節了,大家按順序看哦。

一別之後,二地相懸。

實體對象我們已經有了,那麼現在就差數據庫表了。現在回想一下Android中,數據庫表的操作步驟,繼承SQLiteOpenHelper,修改一些構造函數,再在onCreate中使用db執行一下建表語句,好像就完成了(數據庫版本升級後續再講)。請看示例代碼:

public class DBHelper extends SQLiteOpenHelper {

    private static final int VERSION = 1;
    private static final String NAME = bzh.db;

    public DBHelper(Context context) {
        super(context, NAME, null, VERSION);
    }

    // 新聞表:主鍵+標題+摘要
    public static final String TABLE_ID = _id;
    public static final String TABLE_NEWS_NAME = news;
    public static final String TABLE_NEWS_TITLE = title;
    public static final String TABLE_NEWS_SUMMARY = summary;

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE TABLE  + TABLE_NEWS_NAME + (  + //
                TABLE_ID +  integer primary key autoincrement ,  + //
                TABLE_NEWS_TITLE +  varchar(50) , + //
                TABLE_NEWS_SUMMARY +  varhcar(200))//
        );

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }

}

仔細看一下代碼,其中我們把各個字段和表名都抽取成為了常量字段,這樣可以更方便我們寫數據庫增刪改查操作的時候使用。

都說是三四月,誰又知五六年。

現在,實體和表都有了,是時候寫增刪改查的操作了。既然是增刪改查,那麼肯定有對應的insertdeleteupdatefindAll等操作咯。
而我們作為有經驗的開發人員(偷笑),肯定是要面向接口編程咯,那就先寫一個NewsDao吧,整體上很簡單,就不寫注釋了。代碼如下:

public interface NewsDao {
    long insert(News news);
    int delete(int id);
    int update(News news);
    List findAll();
}

看起來一切都很順利,下面是時候寫Dao的實現類了,我們就叫做NewsDaoImpl吧,並實現NewsDao接口。代碼如下:

public class NewsDaoImpl implements NewsDao {

    private Context context;
    private DBHelper helper;
    private SQLiteDatabase db;

    public NewsDaoImpl(Context context) {
        super();
        this.context = context;
        this.helper = new DBHelper(context);
        this.db = helper.getWritableDatabase();
    }

    @Override
    public long insert(News news) {
        return 0;
    }

    @Override
    public int delete(int id) {
        return 0;
    }

    @Override
    public int update(News news) {
        return 0;
    }

    @Override
    public List findAll() {
        return null;
    }
}

貌似架子就是這個樣子了,為了執行增刪改查等語句,我們需要一個之前寫的DBHelper實例,而DBHelper又要求傳入Context對象。然後DBHelper又給了我們一個操作數據庫SQLiteDatabase實例。

一切很順利,不過增刪改查方法都是空著的,看來接下來只需要填空就好了。

七弦琴無心彈,八行書不可傳。

既然是填空,那麼我們就只能一個一個來了,太麻煩了(怪不得程序員喜歡偷懶,喵喵!)。

就先從insert開始吧。

@Override
public long insert(News news) {
    ContentValues values = new ContentValues();
    values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
    // ... 此處省略N行代碼
    return db.insert(DBHelper.TABLE_NEWS_NAME, null, values);
}

當然,我這些只是示例性的代碼,你看省略的好多呢,整個insert的內容一點也不復雜。首先,把字段名和實體字段的對應關系存一個一個入ContentValues中,然後再執行一下db.insert,並填入要插入的表名和數據,搜一的一下,就成功了。

接下來是delete,這麼貌似更容易了。在delete中指定表的名稱、過濾條件、以及過濾條件對應的參數,就OK了。

@Override
public int delete(int id) {
    return db.delete(DBHelper.TABLE_NEWS_NAME, DBHelper.TABLE_ID + =?, new String[] { id +  });
}

下面是update方法。為更新准備一下數據,在update填入對應的表名、更新值、過濾條件、以及過濾條件對應的參數,這就完成了。當然,為了偷懶,還是省略了一下代碼。我保證下個肯定寫完整。

@Override
public int update(News news) {
    ContentValues values = new ContentValues();
    values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
    // ... 此處省略N行代碼
    return db.update(DBHelper.TABLE_NEWS_NAME, values, DBHelper.TABLE_ID + =?, new String[] { news.getId() +  });
}

輪到findAll方法了,這個比較重要,大家仔細聽。我們通過db.query()查詢數據,並獲取到游標(不要忘記關閉哦),然後就開始移動游標,拿出數據並封裝到News實體中,然後再一個一個加入到List中。好吧,我承認,這裡又偷懶了,嘿嘿。

@Override
public List findAll() {
    List result = null; // List
    Cursor cursor = db.query(DBHelper.TABLE_NEWS_NAME, null, null, null, null, null, null);
    if (cursor != null) {
        result = new ArrayList();
        while (cursor.moveToNext()) {
            News news = new News(); // M m  = new M();
            int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_NAME);
            String title = cursor.getString(columnIndex);
            news.setTitle(title);
            // ... 此處省略N行代碼

            result.add(news);
        }
    }

    // 關閉游標
    cursor.close();
    return result;
}

一步一步下來,貌似一個有關News實體的增刪改查的Dao就出爐了,但是,作為一名程序員,寫一個這樣子的還是可以承受的,那麼萬一有更多的UserAAABBBCCC 等等出現怎麼辦?

難道還要寫出來一大堆UserDaoUserDaoImplAAADaoAAADaoImpl等等的一大堆類和方法? 老天,你還是殺了我吧。

那麼,問題來了,上述的代碼需要解決什麼問題,才能變成通用的?

九連環從中折斷,十裡長亭望眼欲穿!

接下來我們從insertdeleteupdatefindAll這些方法中依次分析,看看是哪些問題阻擋了我們通用化的腳步。

insert中,

ContentValues values = new ContentValues();
values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
    // ... 此處省略N行代碼

如果這些代碼能夠,以一種“自動”的方式,那麼我們的雙手就算解放出來一步了,我們需要面對的問題是:如何將實體中的數據,按照對應關系導入到對應的表之中。

return db.insert(DBHelper.TABLE_NEWS_NAME, null, values);

再看看這句,好像在Java中,寫死就等於耦合、等於不方便。而DBHelper.TABLE_NEWS_NAME好像也固定在語句之中了,看來這句代碼也阻擋了我們解放雙手的步伐,嘿嘿,又出來一個。我們需要面對的問題是:如何獲取表名。

這個方法是沒有了,接下來看看delete

return db.delete(DBHelper.TABLE_NEWS_NAME, DBHelper.TABLE_ID + =?, new String[] { id +  });

其中的DBHelper.TABLE_NEWS_NAME我們已經記錄下來了,那麼,後面的DBHelper.TABLE_ID又是怎麼回事? 哦,原來是主鍵,也是固定寫死的。看來這也是我們面對的問題:明確實體中的主鍵是誰,並獲取到主鍵中封裝的值。

再看看update方法,仔細瞅瞅~ 好像沒有耶!那麼就繼續下一個findAll方法。

News news = new News(); // M m  = new M();
int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_NAME);
String title = cursor.getString(columnIndex);
news.setTitle(title);
// ... 此處省略N行代碼www.2cto.com

這裡是將游標中的數據取出來,並放入到對應的實體屬性中去,這裡最容易出現錯誤、並且也是寫死的。看來這裡也是問題之一:將數據庫表中列的數據,按照對應關系導入到實體之中。

分析完後,我們已經可以總結出一二三四五六七八條了。哈哈。(貌似沒有這麼多。)

披荊斬棘

需要解決什麼問題,才能變成通用的? ① 表名的獲取 ② 如何將實體中的數據,按照對應關系導入到對應的表之中。 ③ 明確實體中的主鍵是誰,並獲取到主鍵中封裝的值。 ④ 搞定實體對象的創建 ⑤ 將數據庫表中列的數據,按照對應關系導入到實體之中。

以上就是全部問題所在了,如果能夠搞定這些,在Android中的數據庫通用操作也就應運而生了。

這裡寫圖片描述

時間是怎樣一種東西,它能改變一切、帶走一切、更可留下一切。昨天仿佛還在眼前,今天卻悄悄過去,我們的生命總是那麼有限,那些屬於你我的年輕亦是如此缥缈,擁有時並無察覺,待往事已成過眼雲煙,方才了解自己荒廢了那大好的時光,歲月仍在,流星劃過的剎那,卻忘記在心中默默許下那卑微的心願。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved