建立資料庫表格使用SQL的「CREATE TABLE」指令,這個指令需要指定表格的名稱,還有這個表格用來儲存每一筆資料的欄位(Column)。這些需要的表格欄位可以對應到主要類別中的欄位變數,不過SQLite資料庫的資料型態只有下面這幾種,使用它們來決定表格欄位可以儲存的資料型態:
- INTEGER – 整數,對應Java 的byte、short、int 和long。
- REAL – 小數,對應Java 的float 和double。
- TEXT – 字串,對應Java 的String。
在設計表格欄位的時候,需要設定欄位名稱和型態,表格欄位的名稱建議就使用主要類別中的欄位變數名稱。表格欄位的型態依照欄位變數的型態,把它們轉換為SQLite提供的資料型態。通常在表格欄位中還會加入「NOT NULL」的指令,表示這個表格欄位不允許空值,可以避免資料發生問題
表格的名稱可以使用主要類別的類別名稱,一個SQLite表格建議一定要包含一個可以自動為資料編號的欄位,欄位名稱固定為「_id」,型態為「INTEGER」,後面加上「PRIMARY KEY AUTOINCREMENT」的設定,就可以讓SQLite自動為每一筆資料編號以後儲存在這個欄位。
建立SQLiteOpenHelper類別
Android 提供許多方便與簡單的資料庫API,可以簡化應用程式處理資料庫的工作。這些API都在「android.database.sqlite」套件,它們可以用來執行資料庫的管理和查詢的工作。在這個套件中的「SQLiteOpenHelper」類別,可以在應用程式中執行建立資料庫與表格的工作,應用程式第一次在裝置執行的時候,由它負責建立應用程式需要的資料庫與表格,後續執行的時候開啟已經建立好的資料庫讓應用程式使用。還有應用程式在運作一段時間以後,如果增加或修改功能,資料庫的表格也增加或修改了,它也可以為應用程式執行資料庫的修改工作,讓新的應用程式可以正常的運作。
接下來設計建立資料庫與表格的類別,在「net.macdidi.myandroidtutorial」套件按滑鼠右鍵,選擇「New -> Java CLass」,在Name輸入「MyDBHelper」後選擇「OK」。參考下列的內容先完成部份的程式碼:
接下來設計建立資料庫與表格的類別,在「net.macdidi.myandroidtutorial」套件按滑鼠右鍵,選擇「New -> Java CLass」,在Name輸入「MyDBHelper」後選擇「OK」。參考下列的內容先完成部份的程式碼:
package
net.macdidi.myandroidtutorial;
import
android.content.Context;
import
android.database.sqlite.SQLiteDatabase;
import
android.database.sqlite.SQLiteDatabase.CursorFactory;
import
android.database.sqlite.SQLiteOpenHelper;
public
class
MyDBHelper
extends
SQLiteOpenHelper {
// 資料庫名稱
public
static
final
String DATABASE_NAME =
"mydata.db"
;
// 資料庫版本,資料結構改變的時候要更改這個數字,通常是加一
public
static
final
int
VERSION =
1
;
// 資料庫物件,固定的欄位變數
private
static
SQLiteDatabase database;
// 建構子,在一般的應用都不需要修改
public
MyDBHelper(Context context, String name, CursorFactory factory,
int
version) {
super
(context, name, factory, version);
}
// 需要資料庫的元件呼叫這個方法,這個方法在一般的應用都不需要修改
public
static
SQLiteDatabase getDatabase(Context context) {
if
(database ==
null
|| !database.isOpen()) {
database =
new
MyDBHelper(context, DATABASE_NAME,
null
, VERSION).getWritableDatabase();
}
return
database;
}
@Override
public
void
onCreate(SQLiteDatabase db) {
// 建立應用程式需要的表格
// 待會再回來完成它
}
@Override
public
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion) {
// 刪除原有的表格
// 待會再回來完成它
// 呼叫onCreate建立新版的表格
onCreate(db);
}
}
資料庫功能類別
在Android應用程式中使用資料庫功能通常會有一種狀況,就是Activity或其它元件的程式碼,會因為加入處理資料庫的工作,程式碼變得又多、又複雜。一般程式設計的概念,一個元件中的程式碼如果很多的話,在撰寫或修改的時候,都會比較容易出錯。所以這裡說明的作法,會採用在一般應用程式中執行資料庫工作的設計方式,把執行資料庫工作的部份寫在一個獨立的Java類別中。
接下來設計應用程式需要的資料庫功能類別,提供應用程式與資料庫相關功能。在「net.macdidi.myandroidtutorial」套件按滑鼠右鍵,選擇「New -> Java CLass」,在Name輸入「ItemDAO」後選擇「OK」。參考下列的內容先完成部份的程式碼:
package
net.macdidi.myandroidtutorial;
import
java.util.ArrayList;
import
java.util.Date;
import
java.util.List;
import
android.content.ContentValues;
import
android.content.Context;
import
android.database.Cursor;
import
android.database.sqlite.SQLiteDatabase;
// 資料功能類別
public
class
ItemDAO {
// 表格名稱
public
static
final
String TABLE_NAME =
"item"
;
// 編號表格欄位名稱,固定不變
public
static
final
String KEY_ID =
"_id"
;
// 其它表格欄位名稱
public
static
final
String DATETIME_COLUMN =
"datetime"
;
public
static
final
String COLOR_COLUMN =
"color"
;
public
static
final
String TITLE_COLUMN =
"title"
;
public
static
final
String CONTENT_COLUMN =
"content"
;
public
static
final
String FILENAME_COLUMN =
"filename"
;
public
static
final
String LATITUDE_COLUMN =
"latitude"
;
public
static
final
String LONGITUDE_COLUMN =
"longitude"
;
public
static
final
String LASTMODIFY_COLUMN =
"lastmodify"
;
// 使用上面宣告的變數建立表格的SQL指令
public
static
final
String CREATE_TABLE =
"CREATE TABLE "
+ TABLE_NAME +
" ("
+
KEY_ID +
" INTEGER PRIMARY KEY AUTOINCREMENT, "
+
DATETIME_COLUMN +
" INTEGER NOT NULL, "
+
COLOR_COLUMN +
" INTEGER NOT NULL, "
+
TITLE_COLUMN +
" TEXT NOT NULL, "
+
CONTENT_COLUMN +
" TEXT NOT NULL, "
+
FILENAME_COLUMN +
" TEXT, "
+
LATITUDE_COLUMN +
" REAL, "
+
LONGITUDE_COLUMN +
" REAL, "
+
LASTMODIFY_COLUMN +
" INTEGER)"
;
// 資料庫物件
private
SQLiteDatabase db;
// 建構子,一般的應用都不需要修改
public
ItemDAO(Context context) {
db = MyDBHelper.getDatabase(context);
}
// 關閉資料庫,一般的應用都不需要修改
public
void
close() {
db.close();
}
// 新增參數指定的物件
public
Item insert(Item item) {
// 建立準備新增資料的ContentValues物件
ContentValues cv =
new
ContentValues();
// 加入ContentValues物件包裝的新增資料
// 第一個參數是欄位名稱, 第二個參數是欄位的資料
cv.put(DATETIME_COLUMN, item.getDatetime());
cv.put(COLOR_COLUMN, item.getColor().parseColor());
cv.put(TITLE_COLUMN, item.getTitle());
cv.put(CONTENT_COLUMN, item.getContent());
cv.put(FILENAME_COLUMN, item.getFileName());
cv.put(LATITUDE_COLUMN, item.getLatitude());
cv.put(LONGITUDE_COLUMN, item.getLongitude());
cv.put(LASTMODIFY_COLUMN, item.getLastModify());
// 新增一筆資料並取得編號
// 第一個參數是表格名稱
// 第二個參數是沒有指定欄位值的預設值
// 第三個參數是包裝新增資料的ContentValues物件
long
id = db.insert(TABLE_NAME,
null
, cv);
// 設定編號
item.setId(id);
// 回傳結果
return
item;
}
// 修改參數指定的物件
public
boolean
update(Item item) {
// 建立準備修改資料的ContentValues物件
ContentValues cv =
new
ContentValues();
// 加入ContentValues物件包裝的修改資料
// 第一個參數是欄位名稱, 第二個參數是欄位的資料
cv.put(DATETIME_COLUMN, item.getDatetime());
cv.put(COLOR_COLUMN, item.getColor().parseColor());
cv.put(TITLE_COLUMN, item.getTitle());
cv.put(CONTENT_COLUMN, item.getContent());
cv.put(FILENAME_COLUMN, item.getFileName());
cv.put(LATITUDE_COLUMN, item.getLatitude());
cv.put(LONGITUDE_COLUMN, item.getLongitude());
cv.put(LASTMODIFY_COLUMN, item.getLastModify());
// 設定修改資料的條件為編號
// 格式為「欄位名稱=資料」
String where = KEY_ID +
"="
+ item.getId();
// 執行修改資料並回傳修改的資料數量是否成功
return
db.update(TABLE_NAME, cv, where,
null
) >
0
;
}
// 刪除參數指定編號的資料
public
boolean
delete(
long
id){
// 設定條件為編號,格式為「欄位名稱=資料」
String where = KEY_ID +
"="
+ id;
// 刪除指定編號資料並回傳刪除是否成功
return
db.delete(TABLE_NAME, where ,
null
) >
0
;
}
// 讀取所有記事資料
public
List<Item> getAll() {
List<Item> result =
new
ArrayList<>();
Cursor cursor = db.query(
TABLE_NAME,
null
,
null
,
null
,
null
,
null
,
null
,
null
);
while
(cursor.moveToNext()) {
result.add(getRecord(cursor));
}
cursor.close();
return
result;
}
// 取得指定編號的資料物件
public
Item get(
long
id) {
// 準備回傳結果用的物件
Item item =
null
;
// 使用編號為查詢條件
String where = KEY_ID +
"="
+ id;
// 執行查詢
Cursor result = db.query(
TABLE_NAME,
null
, where,
null
,
null
,
null
,
null
,
null
);
// 如果有查詢結果
if
(result.moveToFirst()) {
// 讀取包裝一筆資料的物件
item = getRecord(result);
}
// 關閉Cursor物件
result.close();
// 回傳結果
return
item;
}
// 把Cursor目前的資料包裝為物件
public
Item getRecord(Cursor cursor) {
// 準備回傳結果用的物件
Item result =
new
Item();
result.setId(cursor.getLong(
0
));
result.setDatetime(cursor.getLong(
1
));
result.setColor(ItemActivity.getColors(cursor.getInt(
2
)));
result.setTitle(cursor.getString(
3
));
result.setContent(cursor.getString(
4
));
result.setFileName(cursor.getString(
5
));
result.setLatitude(cursor.getDouble(
6
));
result.setLongitude(cursor.getDouble(
7
));
result.setLastModify(cursor.getLong(
8
));
// 回傳結果
return
result;
}
// 取得資料數量
public
int
getCount() {
int
result =
0
;
Cursor cursor = db.rawQuery(
"SELECT COUNT(*) FROM "
+ TABLE_NAME,
null
);
if
(cursor.moveToNext()) {
result = cursor.getInt(
0
);
}
return
result;
}
// 建立範例資料
public
void
sample() {
Item item =
new
Item(
0
,
new
Date().getTime(), Colors.RED,
"關於Android Tutorial的事情."
,
"Hello content"
,
""
,
0
,
0
,
0
);
Item item2 =
new
Item(
0
,
new
Date().getTime(), Colors.BLUE,
"一隻非常可愛的小狗狗!"
,
"她的名字叫「大熱狗」,又叫\n作「奶嘴」,是一隻非常可愛\n的小狗。"
,
""
,
25.04719
,
121.516981
,
0
);
Item item3 =
new
Item(
0
,
new
Date().getTime(), Colors.GREEN,
"一首非常好聽的音樂!"
,
"Hello content"
,
""
,
0
,
0
,
0
);
Item item4 =
new
Item(
0
,
new
Date().getTime(), Colors.ORANGE,
"儲存在資料庫的資料"
,
"Hello content"
,
""
,
0
,
0
,
0
);
insert(item);
insert(item2);
insert(item3);
insert(item4);
}
}
完成資料庫功能類別以後,裡面也宣告了一些SQLiteOpenHelper類別會使用到的資料,開啟「MyDBHelper」類別,完成之前還沒有完成的工作:
@Override
public
void
onCreate(SQLiteDatabase db) {
// 建立應用程式需要的表格
db.execSQL(ItemDAO.CREATE_TABLE);
}
@Override
public
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion) {
// 刪除原有的表格
db.execSQL(
"DROP TABLE IF EXISTS "
+ ItemDAO.TABLE_NAME);
// 呼叫onCreate建立新版的表格
onCreate(db);
}
使用資料庫中的記事資料
完成與資料庫相關的類別以後,其它的部份就簡單多了,Activity元件也可以保持比較簡潔的程式架構。開啟在「net.macdidi.myandroidtutorial」套件下的「MainActivity」類別,修改原來自己建立資料的作法,改由資料庫提供記事資料並顯示在畫面。由於所有執行資料庫工作的程式碼都寫在「ItemDAO」類別,所以要宣告一個ItemDAO的欄位變數,「onCreate」方法也要執行相關的修改:
// 宣告資料庫功能類別欄位變數
private ItemDAO itemDAO;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
processViews();
processControllers();
// 建立資料庫物件
itemDAO =
new
ItemDAO(getApplicationContext());
// 如果資料庫是空的,就建立一些範例資料
// 這是為了方便測試用的,完成應用程式以後可以拿掉
if
(itemDAO.getCount() ==
0
) {
itemDAO.sample();
}
// 取得所有記事資料
items = itemDAO.getAll();
itemAdapter =
new
ItemAdapter(
this
, R.layout.single_item, items);
item_list.setAdapter(itemAdapter);
}
完成這個部份的修改以後,執行應用程式,如果畫面上顯示像這樣的畫面,資料庫的部份應該就沒有問題了。
沒有留言:
張貼留言