Android SQLite數據庫使用 學習與代碼實踐


Android SQLite數據庫使用 學習與代碼實踐

 

SQLiteOpenHelper 類

  用SQLiteOpenHelper 類中的 getWritableDatabase()getReadableDatabase()方法可以獲得數據庫的引用。

  為了實現對數據庫版本進行管理,SQLiteOpenHelper 類提供了兩個重要的方法,分別是 onCreate()和 onUpgrade(),前者用於初次使用軟件時生成數據庫表,后者用於升級軟件時更新數據庫表結構。

  當調用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法獲取用於操作數據庫的SQLiteDatabase實例的時候,如果數據庫不存在,Android系統會自動生成一個數據庫,接着調用onCreate()方法。

  onCreate()方法在初次生成數據庫時才會被調用,在onCreate()方法里可以生成數據庫表結構及添加一些應用使用到的初始化數據。

  onUpgrade()方法在數據庫的版本發生變化時會被調用,一般在軟件升級時才需改變版本號,而數據庫的版本是由程序員控制的。

  假設數據庫現在的版本是1,由於業務的變更,修改了數據庫表結構,這時候就需要升級軟件,升級軟件時希望更新用戶手機里的數據庫表結構,為了實現這一目的,可以把原來的數據庫版本設置為2,並且在onUpgrade()方法里面實現表結構的更新。

  當軟件的版本升級次數比較多,這時在onUpgrade()方法里面可以根據原版號和目標版本號進行判斷,然后做出相應的表結構及數據更新。

 

SQLiteDatabase類

  Android提供了一個名為 SQLiteDatabase的類(SQLiteOpenHelper 類中的 getWritableDatabase()getReadableDatabase()方法返回這個類的對象)。

  SQLiteDatabase類封裝了一些操作數據庫的API,使用該類可以完成對數據進行添加(Create)、查詢(Retrieve)、更新(Update)和刪除(Delete)操作(這些操作簡稱為CRUD)。

  SQLiteDatabase的學習,應該重點掌握execSQL()和rawQuery()方法。

  execSQL()方法可以執行insert、delete、update和CREATE TABLE之類有更改行為的SQL語句;

  rawQuery()方法用於執行select語句。

 

程序實例

  首先是一個DatabaseHelper類,它繼承了SQLiteOpenHelper,實現了onCreate和onUpgrade方法。

  雖然數據庫的名字和版本都是在該類的構造函數中傳入,但是數據庫實際被創建是在該類的getWritableDatabase()或getReadableDatabase()方法第一次被調用時。

  第一次創建之后接着會調用onCreate方法(我們在這里創建了數據表),之后onCreate就不再被調用。

  DatabaseHelper類:

package com.example.hellodatabases;

import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

//參考:http://blog.csdn.net/liuhe688/article/details/6715983

public class DatabaseHelper extends SQLiteOpenHelper// 繼承SQLiteOpenHelper類
{

    // 數據庫版本號
    private static final int DATABASE_VERSION = 1;
    // 數據庫名
    private static final String DATABASE_NAME = "TestDB.db";

    // 數據表名,一個數據庫中可以有多個表(雖然本例中只建立了一個表)
    public static final String TABLE_NAME = "PersonTable";

    // 構造函數,調用父類SQLiteOpenHelper的構造函數
    public DatabaseHelper(Context context, String name, CursorFactory factory,
            int version, DatabaseErrorHandler errorHandler)
    {
        super(context, name, factory, version, errorHandler);

    }

    public DatabaseHelper(Context context, String name, CursorFactory factory,
            int version)
    {
        super(context, name, factory, version);
        // SQLiteOpenHelper的構造函數參數:
        // context:上下文環境
        // name:數據庫名字
        // factory:游標工廠(可選)
        // version:數據庫模型版本號
    }

    public DatabaseHelper(Context context)
    {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);

        // 數據庫實際被創建是在getWritableDatabase()或getReadableDatabase()方法調用時
        Log.d(AppConstants.LOG_TAG, "DatabaseHelper Constructor");
        // CursorFactory設置為null,使用系統默認的工廠類
    }

    // 繼承SQLiteOpenHelper類,必須要覆寫的三個方法:onCreate(),onUpgrade(),onOpen()
    @Override
    public void onCreate(SQLiteDatabase db)
    {
        // 調用時間:數據庫第一次創建時onCreate()方法會被調用

        // onCreate方法有一個 SQLiteDatabase對象作為參數,根據需要對這個對象填充表和初始化數據
        // 這個方法中主要完成創建數據庫后對數據庫的操作

        Log.d(AppConstants.LOG_TAG, "DatabaseHelper onCreate");

        // 構建創建表的SQL語句(可以從SQLite Expert工具的DDL粘貼過來加進StringBuffer中)
        StringBuffer sBuffer = new StringBuffer();

        sBuffer.append("CREATE TABLE [" + TABLE_NAME + "] (");
        sBuffer.append("[_id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ");
        sBuffer.append("[name] TEXT,");
        sBuffer.append("[age] INTEGER,");
        sBuffer.append("[info] TEXT)");

        // 執行創建表的SQL語句
        db.execSQL(sBuffer.toString());

        // 即便程序修改重新運行,只要數據庫已經創建過,就不會再進入這個onCreate方法

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
    {
        // 調用時間:如果DATABASE_VERSION值被改為別的數,系統發現現有數據庫版本不同,即會調用onUpgrade

        // onUpgrade方法的三個參數,一個 SQLiteDatabase對象,一個舊的版本號和一個新的版本號
        // 這樣就可以把一個數據庫從舊的模型轉變到新的模型
        // 這個方法中主要完成更改數據庫版本的操作

        Log.d(AppConstants.LOG_TAG, "DatabaseHelper onUpgrade");

        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
        // 上述做法簡單來說就是,通過檢查常量值來決定如何,升級時刪除舊表,然后調用onCreate來創建新表
        // 一般在實際項目中是不能這么做的,正確的做法是在更新數據表結構時,還要考慮用戶存放於數據庫中的數據不丟失

    }

    @Override
    public void onOpen(SQLiteDatabase db)
    {
        super.onOpen(db);
        // 每次打開數據庫之后首先被執行

        Log.d(AppConstants.LOG_TAG, "DatabaseHelper onOpen");
    }

}

  定義一個Person類作為測試數據:

package com.example.hellodatabases;

public class Person
{
    public int _id;
    public String name;
    public int age;
    public String info;

    public Person()
    {
    }

    public Person(String name, int age, String info)
    {
        this.name = name;
        this.age = age;
        this.info = info;
    }

}

  之后是一個管理類,持有數據庫對象,對各種操作進行了封裝:

package com.example.hellodatabases;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

//參考:http://blog.csdn.net/liuhe688/article/details/6715983
public class DBManager
{
    private DatabaseHelper helper;
    private SQLiteDatabase db;

    public DBManager(Context context)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> Constructor");
        helper = new DatabaseHelper(context);
        // 因為getWritableDatabase內部調用了mContext.openOrCreateDatabase(mName, 0,
        // mFactory);
        // 所以要確保context已初始化,我們可以把實例化DBManager的步驟放在Activity的onCreate里
        db = helper.getWritableDatabase();
    }

    /**
     * add persons
     * 
     * @param persons
     */
    public void add(List<Person> persons)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> add");
        // 采用事務處理,確保數據完整性
        db.beginTransaction(); // 開始事務
        try
        {
            for (Person person : persons)
            {
                db.execSQL("INSERT INTO " + DatabaseHelper.TABLE_NAME
                        + " VALUES(null, ?, ?, ?)", new Object[] { person.name,
                        person.age, person.info });
                // 帶兩個參數的execSQL()方法,采用占位符參數?,把參數值放在后面,順序對應
                // 一個參數的execSQL()方法中,用戶輸入特殊字符時需要轉義
                // 使用占位符有效區分了這種情況
            }
            db.setTransactionSuccessful(); // 設置事務成功完成
        }
        finally
        {
            db.endTransaction(); // 結束事務
        }
    }

    /**
     * update person's age
     * 
     * @param person
     */
    public void updateAge(Person person)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> updateAge");
        ContentValues cv = new ContentValues();
        cv.put("age", person.age);
        db.update(DatabaseHelper.TABLE_NAME, cv, "name = ?",
                new String[] { person.name });
    }

    /**
     * delete old person
     * 
     * @param person
     */
    public void deleteOldPerson(Person person)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> deleteOldPerson");
        db.delete(DatabaseHelper.TABLE_NAME, "age >= ?",
                new String[] { String.valueOf(person.age) });
    }

    /**
     * query all persons, return list
     * 
     * @return List<Person>
     */
    public List<Person> query()
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> query");
        ArrayList<Person> persons = new ArrayList<Person>();
        Cursor c = queryTheCursor();
        while (c.moveToNext())
        {
            Person person = new Person();
            person._id = c.getInt(c.getColumnIndex("_id"));
            person.name = c.getString(c.getColumnIndex("name"));
            person.age = c.getInt(c.getColumnIndex("age"));
            person.info = c.getString(c.getColumnIndex("info"));
            persons.add(person);
        }
        c.close();
        return persons;
    }

    /**
     * query all persons, return cursor
     * 
     * @return Cursor
     */
    public Cursor queryTheCursor()
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> queryTheCursor");
        Cursor c = db.rawQuery("SELECT * FROM " + DatabaseHelper.TABLE_NAME,
                null);
        return c;
    }

    /**
     * close database
     */
    public void closeDB()
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> closeDB");
        // 釋放數據庫資源
        db.close();
    }

}

  主要的Activity:

HelloDBActivity
package com.example.hellodatabases;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleCursorAdapter;

//參考:http://blog.csdn.net/liuhe688/article/details/6715983

public class HelloDBActivity extends Activity
{
    private DBManager dbManager;
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_db);

        listView = (ListView) findViewById(R.id.listView);
        // 初始化DBManager
        dbManager = new DBManager(this);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.hello_db, menu);
        return true;
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        dbManager.closeDB();// 釋放數據庫資源
    }

    public void add(View view)
    {
        ArrayList<Person> persons = new ArrayList<Person>();

        Person person1 = new Person("Ella", 22, "lively girl");
        Person person2 = new Person("Jenny", 22, "beautiful girl");
        Person person3 = new Person("Jessica", 23, "sexy girl");
        Person person4 = new Person("Kelly", 23, "hot baby");
        Person person5 = new Person("Jane", 25, "a pretty woman");

        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person4);
        persons.add(person5);

        dbManager.add(persons);
    }

    public void update(View view)
    {
        // 把Jane的年齡改為30(注意更改的是數據庫中的值,要查詢才能刷新ListView中顯示的結果)
        Person person = new Person();
        person.name = "Jane";
        person.age = 30;
        dbManager.updateAge(person);
    }

    public void delete(View view)
    {
        // 刪除所有三十歲以上的人(此操作在update之后進行,Jane會被刪除(因為她的年齡被改為30))
        // 同樣是查詢才能查看更改結果
        Person person = new Person();
        person.age = 30;
        dbManager.deleteOldPerson(person);
    }

    public void query(View view)
    {
        List<Person> persons = dbManager.query();
        ArrayList<Map<String, String>> list = new ArrayList<Map<String, String>>();
        for (Person person : persons)
        {
            HashMap<String, String> map = new HashMap<String, String>();
            map.put("name", person.name);
            map.put("info", person.age + " years old, " + person.info);
            list.add(map);
        }
        SimpleAdapter adapter = new SimpleAdapter(this, list,
                android.R.layout.simple_list_item_2, new String[] { "name",
                        "info" }, new int[] { android.R.id.text1,
                        android.R.id.text2 });
        listView.setAdapter(adapter);
    }

    @SuppressWarnings("deprecation")
    public void queryTheCursor(View view)
    {
        Cursor c = dbManager.queryTheCursor();
        startManagingCursor(c); // 托付給activity根據自己的生命周期去管理Cursor的生命周期
        CursorWrapper cursorWrapper = new CursorWrapper(c)
        {
            @Override
            public String getString(int columnIndex)
            {
                // 將簡介前加上年齡
                if (getColumnName(columnIndex).equals("info"))
                {
                    int age = getInt(getColumnIndex("age"));
                    return age + " years old, " + super.getString(columnIndex);
                }
                return super.getString(columnIndex);
            }
        };
        // 確保查詢結果中有"_id"列
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_2, cursorWrapper,
                new String[] { "name", "info" }, new int[] {
                        android.R.id.text1, android.R.id.text2 });
        ListView listView = (ListView) findViewById(R.id.listView);
        listView.setAdapter(adapter);
    }

}

 

  另,附上布局和其他:

布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="add"
        android:text="add" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="update"
        android:text="update" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="delete"
        android:text="delete" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="query"
        android:text="query" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="queryTheCursor"
        android:text="queryTheCursor" />

    <ListView
        android:id="@+id/listView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
常量
package com.example.hellodatabases;

public class AppConstants
{
    public static final String LOG_TAG="Hello DB";

}


 

參考資料

  官網Training: Saving Data in SQL Databases

  http://developer.android.com/training/basics/data-storage/databases.html

  Android中SQLite應用詳解:

  http://blog.csdn.net/liuhe688/article/details/6715983

  關於Cursor類的介紹:

  http://www.cnblogs.com/TerryBlog/archive/2010/07/05/1771459.html

  Android 小項目之--SQLite 使用法門 (附源碼):

  http://www.cnblogs.com/TerryBlog/archive/2010/06/12/1757166.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM