關於Android數據庫的使用,網上和很多書籍上都有相應的介紹,簡單的使用,難度不大,但是作爲一個持久存儲數據的方式,有必要做個總結,以下內容基本參照《第一行代碼》中的有關介紹。
概述
Android爲了讓我們能夠更加方便地管理數據庫,專門提供了一個SQLiteOpenHelper幫助類,藉助這個類就可以非常簡單地對數據庫進行創建和升級。既然有好東西可以直接使用,那我們自然要嘗試一下了,下面我就將對SQLiteOpenHelper的基本用法進行介紹。
首先你要知道SQLiteOpenHelper是一個抽象類,這意味着如果我們想要使用它的話,就需要創建一個自己的幫助類去繼承它。SQLiteOpenHelper中有兩個抽象方法,分別是onCreate()和onUpgrade(),我們必須在自己的幫助類裏面重寫這兩個方法,然後分別在這兩個方法中去實現創建、升級數據庫的邏輯。
SQLiteOpenHelper中還有兩個非常重要的實例方法,getReadableDatabase()和getWritableDatabase()。這兩個方法都可以創建或打開一個現有的數據庫(如果數據庫已存在則直接打開,否則創建一個新的數據庫),並返回一個可對數據庫進行讀寫操作的對象。不同的是,當數據庫不可寫入的時候(如磁盤空間已滿)getReadableDatabase()方法返回的對象將以只讀的方式去打開數據庫,而getWritableDatabase()方法則將出現異常。
SQLiteOpenHelper中有兩個構造方法可供重寫,一般使用參數少一點的那個構造方法即可。這個構造方法中接收四個參數,第一個參數是Context,這個沒什麼好說的,必須要有它才能對數據庫進行操作。第二個參數是數據庫名,創建數據庫時使用的就是這裏指定的名稱。第三個參數允許我們在查詢數據的時候返回一個自定義的Cursor,一般都是傳入null。第四個參數表示當前數據庫的版本號,可用於對數據庫進行升級操作。構建出SQLiteOpenHelper的實例之後,再調用它的getReadableDatabase()或getWritableDatabase()方法就能夠創建數據庫了,數據庫文件會存放在/data/data//databases/目錄下。
代碼
實現Book.java簡單數據類,MySQLHelper.java繼承自SQLiteOpenHelper,MainActivity.java界面操作
- Layout(只寫一個button的,其他button類似):
<Button
android:text="update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/insert"
android:layout_alignParentStart="true"
android:layout_marginTop="17dp"
android:id="@+id/update"
android:onClick="updateDataClick" />
- Book.java
public class Book {
private String name;
private float price;
private int pages;
public Book(float price, String name, int pages) {
this.price = price;
this.name = name;
this.pages = pages;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
}
- MySQLHelper.java
public class MySQLHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK_TABLE= "create table Book (" +
"id integer primary key autoincrement," +
"name text, " +
"price real," +
"pages integer," +
"category_id integer)";
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
private static final String TAG = "MySQLHelper";
private Context mContext;
public MySQLHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
Log.d(TAG,"onCreate");
sqLiteDatabase.execSQL(CREATE_BOOK_TABLE);
sqLiteDatabase.execSQL(CREATE_CATEGORY);
}
//數據庫升級的最佳方式
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
Log.d(TAG,"onUpgrade");
//刪除數據庫中的表
//sqLiteDatabase.execSQL("drop table if exists Book");
Log.d(TAG,"i = "+ i+" i1 = "+i1);
//機智的沒有使用break
switch (i){
case 1:
sqLiteDatabase.execSQL(CREATE_CATEGORY);
case 2:
sqLiteDatabase.execSQL("alter table Book add column category_id integer");
default:
}
}
}
- MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG ="ZYW";
private Button create;
private Button insert;
private Button update;
private Button delete;
private Button select;
private MySQLHelper helper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
setContentView(R.layout.activity_main);
init();
//修改version,onUpgrade()方法會被執行到
helper = new MySQLHelper(this,"DataBase.db",null,2);
}
private void init(){
create = (Button) findViewById(R.id.create);
insert = (Button) findViewById(R.id.insert);
update = (Button) findViewById(R.id.update);
delete = (Button) findViewById(R.id.delete);
select = (Button) findViewById(R.id.select);
}
public void createTableClick(View view) {
Log.d(TAG,"createTableClick");
SQLiteDatabase database = helper.getReadableDatabase();
}
public void insertDataClick(View view) {
Log.d(TAG,"insertDataClick");
SQLiteDatabase database = helper.getReadableDatabase();
//法1:Android 接口
/*ContentValues values = new ContentValues();
Book book = new Book((float) 114.5,"<sangeren>",250);
Book book1 = new Book((float) 66.5,"<wanglishiwunian>",350);
Book book2 = new Book((float) 245.5,"<diyihangdaima>",150);
values.put("name",book.getName());
values.put("price",book.getPrice());
values.put("pages",book.getPages());
database.insert("Book",null,values);
values.clear();
values.put("name",book1.getName());
values.put("price",book1.getPrice());
values.put("pages",book1.getPages());
database.insert("Book",null,values);
values.clear();
values.put("name",book2.getName());
values.put("price",book2.getPrice());
values.put("pages",book2.getPages());
database.insert("Book",null,values);*/
//法2:SQLite語句
//同上,id是自增長的,所以也不需要賦值
//(name,pages,price): 發現這個順序沒必要和創建表時的順序一致,但是要保證後續賦值的一一對應。
//('<xuesanfeihu>',555,120.5): 如果使用雙引號的話會報錯,需要使用英文單引號
// 不加單引號,運行時會報錯:Caused by: android.database.sqlite.SQLiteException: near "<": syntax error (code 1):
//拓展: 轉義字符
//String str1 = "\"name\"";//字符串兩邊含有雙引號
//String str2 = "name \"is\" wgb";//字符串中間含有雙引號
//String str3 = "\\name";//使用轉義字符還可以使字符串包含其他字符
//database.execSQL("insert into Book (name,pages,price) values ('<feihu>',432,32.5)");
//法3:折中方法
database.execSQL("insert into Book (name,pages,price) values (?,?,?)",new String[]{"<ciqiongle>","421","73.2"});
}
public void updateDataClick(View view) {
Log.d(TAG,"updateDataClick");
SQLiteDatabase database = helper.getReadableDatabase();
//法1:Android 接口
/*ContentValues values = new ContentValues();
Book book = new Book((float) 333.5,"<HHH>",222);
values.put("name",book.getName());
values.put("price",book.getPrice());
values.put("pages",book.getPages());
database.insert("Book",null,values);
database.update("Book",values,"price = ?",new String[]{"245.5"});*/
//法2:SQLite語句
//database.execSQL("update Book set price = 321.1 where name = '<ciqiongle>'");
//法3:折中方法
database.execSQL("update Book set price = ? where name = ?",new String[]{"123.4","<ciqiongle>"});
}
public void deleteDataClick(View view) {
Log.d(TAG,"deleteDataClick");
SQLiteDatabase database = helper.getReadableDatabase();
//法1:Android 接口
/*database.delete("Book","name = ?",new String[]{"<sangeren>"});*/
//法2:SQLite語句
//database.execSQL("delete from Book where name = '<HHH>'");
//法3:折中方法
database.execSQL("delete from Book Where name = ?",new String[]{"<feihu>"});
}
public void selectDataClick(View view) {
Log.d(TAG,"selectDataClick");
SQLiteDatabase database = helper.getReadableDatabase();
//"id desc": 表示查詢得到的數據,按id降序排列; 升序爲"id asc"
//"pages between ? and ?": 測試發現查詢得到的數據包含邊界
//Cursor cursor = database.query("Book", new String[]{"id","name", "price"}, "pages between ? and ?", new String[]{"150","300"}, null, null, "id desc", null);
//法2:SQLite語句
Cursor cursor = database.rawQuery("select * from Book where pages between ? and ?", new String[]{"150", "300"});
if (cursor.moveToFirst()) {
do {
// new String[]{"name", "price"}代表最終讀取數據庫中哪幾列的數據,
// 如果cursor中沒有包含,下面非要去獲取,將報錯Caused by: java.lang.IllegalStateException:
// Couldn't read row 0, col -1 from CursorWindow.
// 查詢得到的數據中沒有,你去讀,理當報異常,注意點
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
float price = cursor.getFloat(cursor.getColumnIndex("price"));
Log.d(TAG,"id = "+id+ " name = "+name+" ; price = "+price);
}while (cursor.moveToNext());
}
//不要忘了
cursor.close();
}
//事務的使用:SQLite數據庫是支持事務的,事務的特性可以保證讓某一系列的操作要麼全部完成,要麼一個都不會完成。
public void replaceDataClick(View view) {
Log.d(TAG,"replaceDataClick");
SQLiteDatabase readableDatabase = helper.getReadableDatabase();
//事務開始
readableDatabase.beginTransaction();
try{
readableDatabase.delete("Book",null,null);
if (false) {
//手動拋出一個異常,讓事務失敗
throw new NullPointerException();
}
readableDatabase.execSQL("insert into Book (name,pages,price) values (?,?,?)",new String[]{"<ciqiongle>","421","73.2"});
//事務執行成功
readableDatabase.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
//結束事務
readableDatabase.endTransaction();
}
}
}
關於數據庫常用語句介紹:
1、查找一個表返回其中幾條記錄
select * from table where name= ? limit 0,10;
其中limit 0,10中,0表示從第0條記錄開始,10表示向下10條記錄。
2、根據條件查找一個表,按某字段進行排序
select * from table where name= ? order by id desc或者 asc 或者不填 ;
desc降序排列,asc升序排列
其中id是表中的字段。
3、查詢一個表中,某字段名爲X或者Y的所有數據
select * from LocalModelTable where prodCategory = ? or prodCategory = ?;
其中prodCategory爲字段,名爲X或者Y,返回的是名字爲X、Y下的所有數據。
4、更新一條數據到表中
update LocalSchemeTable set Area = ?,BudgetList = ? ,TimeStamp = ? where SchemeID = ?;
5、插入一條記錄到表中
insert into LocalSchemeTable (Area, BudgetList,Budget) values (?,?,?);
6、給表添加、刪除、修改一列(字段)
alert table table_name add column column_name datatype default 0
舉例: alert table student add column age interger default 0
給student表增加一個字段age ,設置默認值爲0
7、手動添加一個表
create table if not exists LocalProductInfoTable (Num integer primary key autoincrement,marque text);
其中Num爲自增長的編號。text爲文本類型。
8、查看數據庫中是否有某個表
select count(*) as ‘count’ from sqlite_master where type =’table’ and name = ?;
?爲表名。
9、約束
比如有的值不能爲空,有的必須給默認值,有的字段的值必須唯一
建表時可以給特定的字段設置一些約束條件,常見的約束有
not null :規定字段的值不能爲null
unique :規定字段的值必須唯一
default :指定字段的默認值
(建議:儘量給字段設定嚴格的約束,以保證數據的規範性)
舉例
create table student (id integer, name text not null unique, age integer not null default 1) ;
name字段不能爲null,並且唯一
age字段不能爲null,並且默認爲1
10、添加主鍵 自增長
主鍵(Primary Key,簡稱PK)用來唯一地標識某一條記錄,一個表可以有多個主鍵。只要聲明爲primary key,就說明是一個主鍵字段,主鍵字段默認就包含了not null 和 unique 兩個約束。
create table student (id integer primary key, name text, age integer) ;
創建一個student表,設置id爲主鍵。
create table student (id integer primary key autoincrement, name text, age integer) ;
創建一個student表,設置id爲主鍵,並且是自增長類型,需要注意的是,自增長類型主鍵必須!!!是interger類型的。
11、外鍵
外鍵是指本表的某個字段是另外一張表的主鍵。
拓展
Android開發之SQLite詳解
介紹SQLite的內容更詳細,豐富。