Android Sqlite Basics
Android Sqlite Basics
Android: www.3bugs.com
Android ก SQLite
ฐานขอมูล (database) คือวิธีหนึ่งในการจัดเก็บขอมูลในระบบแอนดรอยด เหมาะสําหรับ
ขอมูลที่มีโครงสรางแบบเดียวกันหลายๆชุด ยกตัวอย)างเช)นขอมูลพนักงาน ซึ่งขอมูลแต)ละชุด (พนักงาน
แต)ละคน) ประกอบดวยรหัสพนักงาน ชื่อ นามสกุล และวันเกิด เป/นตน
รหัสพนักงาน ชื อ นามสกุล วันเกิด
6473 สมชาย ไม้อ่อน 07/11/1985
6783 สมศักดิ รักงาน 12/07/1979
6777 ยุวดี ศรีสะอาด 03/01/1976
6789 รุง่ อรุณ กุลบุตร 01/09/1984
Android: www.3bugs.com
( # ) (column) 3 4 56# ) (field) 4? ( # )
3 4 ?4 56# )
EmpID Firstname Lastname Birthdate
6473 07/11/1985
6783 ก ก 12/07/1979 ;< (row) 3 4
& ( ) (record)
6777 ! 03/01/1976
6789 " ก#$% 01/09/1984
@ A#
&'&$#
เมื่อจะใชงานฐานขอมูลในการเก็บขอมูล อันดับแรกตองออกแบบโครงสร$างของฐานขอมูลก)อน
ว)าจะใหมีเทเบิลอะไรบาง (ในฐานขอมูลหนึ่งสามารถมีไดมากกว)า 1 เทเบิล) แต)ละเทเบิลมีคอลัมนอะไร
และเก็บขอมูลชนิดใด ซึ่งโครงสรางของฐานขอมูลนี้เรียกว)า schema
เมื่อพัฒนาแอพไปเรื่อยๆ เราอาจมีความจําเป/นตองปรับเปลี่ยน schema ของฐานขอมูล เช)น
เพิ่มเทเบิลหรือคอลัมนเพื่อรองรับฟWเจอรใหม)ของแอพ หรือเราอาจจัดระเบียบขอมูลใหม)โดยลบเทเบิล
หนึ่ง และยายขอมูลจากเทเบิลนั้นไปเก็บไวในอีกเทเบิลหนึ่งแทน เป/นตน
จากที่กล)าวมาจะเห็นว)า บางครั้งการพัฒนาแอพเวอรชั่นใหม) ไม)เพียงแต)โคดจาวาในแอพจะ
เปลี่ยนไปเท)านั้น แต) "ความคาดหวัง" ของโคดจาวาที่มีต)อ schema ของฐานขอมูลยังเปลี่ยนไปดวย
โดยเวอรชั่น 1 ของแอพอาจทํางานกับ schema ที่เราออกแบบไวเริ่มแรก แต)พอถึงเวลาที่เราปรับปรุง
แอพเป/นเวอรชั่น 5 เราอาจตองการ schema ที่เปลี่ยนไปก็ได
แอนดรอยดมีวิธีใหเราอัพเกรดฐานขอมูลไดอย)างสะดวก โดยผ)านทาง helper class ซึ่งก็คือ
คลาส SQLiteOpenHelper
Android: www.3bugs.com
+ กก , % SQLiteOpenHelper
การใชงาน SQLite จะเริ่มจากการสรางคลาสใหม)ที่เป/นซับคลาสของ (สืบทอดจาก) คลาส
SQLiteOpenHelper ในแพคเกจ android.database.sqlite และทําการ override เมธอดเพื่อตอบ
คําถามแอนดรอยด 2 ขอต)อไปนี้
จะเกิดอะไรขึ้นเมื่อแอพของเราถูกติดตั้งและรันขึ้นมาครั้งแรกสุด ซึ่งตอนนั้นแอพของเรายัง
ไม)มีฐานขอมูล? เราตองจัดการสรางเทเบิลต)างๆขึ้นมาตาม schema ที่ออกแบบไว รวมถึง
อาจมีการเพิ่มขอมูลลงในฐานขอมูลเพื่อเตรียมขอมูลตั้งตนดวย
จะเกิดอะไรขึ้นเมื่อแอพเวอรชั่นใหม)ของเราถูกติดตั้งและรันขึ้นมาบนเครื่องที่ติดตั้งแอพ
เวอรชั่นเดิมของเราไวแลว (มีฐานขอมูลแลว) ซึ่งแอพเวอรชั่นใหม)คาดหวัง schema ที่
เปลี่ยนไปจากเดิม? เราตองแกไข schema ของฐานขอมูลที่มีอยู)แลวในตอนนั้น ให
สอดคลองกับการทํางานของแอพเวอรชั่นใหม) รวมถึงอาจตองโยกยายขอมูลดวย
การตอบคําถาม 2 ขอนี้จะทําไดโดย override เมธอด onCreate และเมธอด onUpgrade
ตามลําดับ
Hands On: ก% %) Helper Class
สมมติตองการเตรียมฐานขอมูลในแอพไวสําหรับเก็บชื่อและเบอรโทรติดต)อของบุคคลต)างๆ
เราจะสราง helper class ซึ่งเป/นซับคลาสของ SQLiteOpenHelper ดังนี้ (ใน Android Studio ให
สราง Java Class ใหม)ในแพคเกจเดียวกันกับแอคทิวิตี, ตั้งชื่อคลาสว)า DatabaseHelper)
package com.example.sqlitebasics;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
Android: www.3bugs.com
private static final String DATABASE_NAME = "contacts.db";
private static final int DATABASE_VERSION = 1;
//
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
//
Log.d(TAG, "Creating table");
String SQL_CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "("
+ COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COL_NAME + " TEXT, "
+ COL_PHONE_NUMBER + " TEXT" + ")";
db.execSQL(SQL_CREATE_TABLE);
//
Log.d(TAG, "Inserting initial data");
ContentValues cv = new ContentValues();
Android: www.3bugs.com
cv.put(COL_NAME, " ");
cv.put(COL_PHONE_NUMBER, "222-222-2222");
db.insert(TABLE_NAME, null, cv);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String msg = "Upgrading database from version " + oldVersion + " to " +
newVersion;
Log.d(TAG, msg);
// ก
}
}
( &'BกC)'?& !$
% $&DB(@ (# E
Android: www.3bugs.com
ในที่นี้คอนเท็กซที่ส)งให SQLiteOpenHelper เอามาจากพารามิเตอรของ DatabaseHelper
อีกที ซึ่งเราจะส)งค)าใหพารามิเตอรนี้ตอนที่สรางออบเจ็คของคลาส DatabaseHelper
ขึ้นมา
เมธอด onCreate แอนดรอยดจะมาเรียกเมธอดนี้เมื่อยังไม)มีฐานขอมูล และจําเป/นตอง
สรางขึ้นมา (เช)นตอนที่เราพยายามอ)านหรือเขียนขอมูล) โดยแอนดรอยดจะส)งออบเจ็ค
SQLiteDatabase มาให ซึ่งเป/นตัวแทนของฐานขอมูลว)างที่เพิ่งถูกสรางขึ้น หนาที่ของเราก็
คือทําการสรางเทเบิลและคอลัมนต)างๆในฐานขอมูล รวมถึงเตรียมขอมูลตั้งตนตามตองการ
โดยเรียกใชเมธอดบนออบเจ็ค SQLiteDatabase ที่ส)งมานี้
ก onCreate
ก F @ #A K'?
! " # $% ; )&L? @ME
call
Android: www.3bugs.com
ในที่นี้สรางเทเบิล 1 เทเบิลชื่อ contacts โดยใชเมธอด execSQL บนออบเจ็ค
SQLiteDatabase และระบุอารกิวเมนตเป/นคําสั่งในภาษา SQL ที่ใชสรางเทเบิล ซึ่งคําสั่ง
Android: www.3bugs.com
ก onUpgrade
ก ,
" ! , -./ ก
call F @ A#& 1 3
F @ #A &
& )? 1
// ! ก" # 1 2 ก% '
if (oldVersion <= 2) {
db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN email TEXT");
}
Android: www.3bugs.com
จากโคด ถาหากฐานขอมูลเดิมเป/นเวอรชั่น 1 จะทํา if ทั้งสองบล็อค คือเพิ่มทั้งคอลัมน
วันเวลาและอีเมลแอดเดรส แต)หากฐานขอมูลเดิมเป/นเวอรชั่น 2 จะทําเฉพาะ if อันหลัง
คือเพิ่มคอลัมนอีเมลแอดเดรสเท)านั้น (ส)วนคอลัมนวันเวลาควรจะตองถูกเพิ่มไวแลวโดย
onUpgrade ของแอพเวอรชั่น 2)
Android: www.3bugs.com
(#
SQLiteOpenHelper
extend
(inherit)
(# instantiate $&DB(
DatabaseHelper DatabaseHelper
getReadableDatabase getWritableDatabase
Helper Class
7/ # $%
(89: # return
SQLiteOpenHelper) rawQuery $&DB( ", 7
execSQL SQLiteDatabase ? :@A B, :?
", กC A
query delete
insert update
เขาใจแบบนั้น)
อย)างไรก็ตาม เมื่อเกิดปhญหา เช)น หน)วยความจํา (storage) เต็มหรือใกลเต็ม จะมีความ
แตกต)างระหว)างสองเมธอดนี้ โดยเมธอด getReadableDatabase จะส)งคืนฐานขอมูลที่อ)านได
อย)างเดียว (read-only) ในขณะที่เมธอด getWritableDatabase จะเกิดขอผิดพลาด ดังนั้นหากเรา
ตองการแค)เพียงอ)านขอมูลจากฐานขอมูล โดยไม)มีการเขียน (เพิ่ม/แกไข/ลบ) แลวล)ะก็ การเขาถึง
ฐานขอมูลดวยเมธอด getReadableDatabase จึงเป/นวิธีที่ปลอดภัยกว)า
การเรียกเมธอด getReadableDatabase หรือ getWritableDatabase จะทําใหฐานขอมูลถูก
สรางขึ้นกรณีที่ยังไม)มีฐานขอมูล (ซึ่งแอนดรอยดจะเรียกมายังเมธอด onCreate) หรือหากมีฐานขอมูล
แลวแต)เป/นเวอรชั่นเก)า ก็จะเรียกมายังเมธอด onUpgrade ดังที่อธิบายแลว พูดอีกอย)างคือ เมธอด
Android: www.3bugs.com
onCreate และ onUpgrade จะทํางานตอนที่เราเรียกเมธอด getReadableDatabase หรือ
getWritableDatabase นั่นเอง
Hands On: ก% %) 6, ) Helper Class
สําหรับแอพที่มีเพียงแอคทิวิตี (หนาจอ) เดียว เรามักจะสรางออบเจ็คของ helper class (ขอ
เรียกออบเจ็คนี้ว)า helper) ตอนที่แอคทิวิตีถูกสรางขึ้นมา นั่นก็คือในเมธอด onCreate ของแอคทิวิตี
แลวเก็บ helper ไวในตัวแปรระดับคลาสของแอคทิวิตี เพื่อใหทุกเมธอดในแอคทิวิตีมาเรียกใชได โดย
ไม)ตองสราง helper ใหม)ทุกครั้งที่ตองการทํางานกับฐานขอมูล
นอกจากนี้ตอนที่แอคทิวิตีกําลังจะถูกทําลาย เราก็จะเรียกเมธอด close ของ helper เพื่อปeด
ฐานขอมูลที่อาจเปeดอยู) อย)างไรก็ตาม จริงๆแลวการเรียกเมธอด close นี้ไม)ใช)เรื่องจําเป/น เพราะ
แอนดรอยดจะปeดฐานขอมูลใหเองอยู)แลวตอนที่มัน kill โปรเซสของแอพ
package com.example.sqlitebasics;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@Override
protected void onDestroy() {
super.onDestroy();
mHelper.close();
}
}
Android: www.3bugs.com
ใหสังเกตว)าเราสราง helper โดยระบุ this เป/นอารกิวเมนต ซึ่งก็คือการส)งแอคทิวิตีปhจจุบัน
(อินสแทนซของ MainActivity) ไปใหคอนสตรัคเตอรของ DatabaseHelper เนื่องจากเราออกแบบให
DatabaseHelper รับคอนเท็กซเขามา (เพื่อส)งต)อให SQLiteOpenHelper อีกที ดังที่อธิบายแลว) ซึ่ง
http://developer.android.com/reference/android/support/v7/app/AppCompatActivity.html
ก% % + %ก*% +
เมื่อพูดถึงการทํางานกับฐานขอมูล สิ่งที่เรามักจะทําบ)อยที่สุดก็คือการอ)านหรือดึงขอมูล
ออกมาใชงาน ซึ่งบ)อยครั้งจะมีการระบุเงื่อนไขเพื่อคัดเลือกเฉพาะขอมูลที่ตรงตามเงื่อนไขนั้น เช)น
พนักงานที่มีอายุงานมากกว)า 10 ปW หรือรายการสั่งซื้อที่มียอดรวมมากกว)า 100,000 บาท เป/นตน
การอ)านขอมูลจากฐานขอมูลจะทําไดโดยใชเมธอด query หรือเมธอด rawQuery
7 & query
วิธีหนึ่งในการอ)านขอมูลจากฐานขอมูล SQLite ก็คือการใชเมธอด query บนออบเจ็ค
SQLiteDatabase เมธอดนี้จะสรางประโยคคําสั่ง SELECT ในภาษา SQL (เรียกว)า คิวรี) ขึ้นจากค)าที่เรา
เพื่อใหดูง)าย)
Android: www.3bugs.com
Cursor cursor = db.query(
DatabaseHelper.TABLE_NAME, ❶
allColumns, ❷
null, ❸
null, ❹
null, ❺
null, ❻
null ❼
);
Android: www.3bugs.com
❻ ค)าสตริง ใชร)วมกับ ❺ เพื่อระบุเงื่อนไขของกลุ)มขอมูลที่จะเลือกมา เทียบเท)า
HAVING ในภาษา SQL
Android: www.3bugs.com
จากนั้นเมื่อนํามาระบุในเมธอด rawQuery ก็จะทําใหเครื่องหมาย ? ถูกแทนดวยค)าสตริง
" " จากอารเรย args และเราจะไดคิวรีที่สมบูรณ
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] allColumns = {
DatabaseHelper.COL_ID,
DatabaseHelper.COL_NAME,
DatabaseHelper.COL_PHONE_NUMBER
};
Cursor cursor = mDb.query(DatabaseHelper.TABLE_NAME, allColumns, null,
null, null, null, null);
}
...
}
Android: www.3bugs.com
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ กก , < = <
ทั้งเมธอด query และ rawQuery จะส)งคืนเคอรเซอร (ออบเจ็ค Cursor) กลับมาใหเรา
เคอรเซอรนั้นถาอธิบายง)ายๆก็คือออบเจ็คที่เก็บผลลัพธ (result set) ของคิวรีไว ซึ่งก็คือแถวและ
คอลัมนทั้งหมดที่ไดจากฐานขอมูลตามคําสั่งของคิวรี
1 L &# 111-111-1111
2 "F[\ ) 222-222-2222
&( )&C )
// ( )ก * #
cursor.close(); // +, *
Android: www.3bugs.com
เมื่อตองการอ)านขอมูลจากเคอรเซอรมาใชงาน (เช)นแสดงผลบนจอ) เราอาจใชวิธีเขาถึงแถว
ขอมูลแต)ละแถวในเคอรเซอรเองโดยตรง หรืออาจใช CursorAdapter มาช)วยก็ได
ก% % + %ก , < = <>& )
ขอย้ําอีกครั้งว)า ขอมูลที่อยู)ในเคอรเซอร คือ result set ซึ่งมีลักษณะเป/นแถวและคอลัมน
เหมือนกับเทเบิลในฐานขอมูล แต)ขอมูลนี้จะเป/น subset ของขอมูลในเทเบิล (ไม)พูดถึงการอ)านขอมูล
จากหลายเทเบิลกรณีฐานขอมูลมีมากกว)า 1 เทเบิล) เราสามารถหาจํานวนแถวใน result set ไดโดย
เรียกเมธอด getCount บนเคอรเซอร
int rowCount = cursor.getCount();
1 L &# 111-111-1111
2 "F[\ ) 222-222-2222
&( )&C )
Android: www.3bugs.com
1 L &# 111-111-1111
2 "F[\ ) 222-222-2222
&( )&C )
&( )&C )
// ( ( - '!" & -
}
Android: www.3bugs.com
ลูปคําสั่ง while ขางตนจะทํางานไดอย)างลงตัว โดยตอนเริ่มตนจะขยับตัวชี้จากตําแหน)ง
"ก)อน" แถวขอมูลแรกไปยังแถวขอมูลแรก แลวค)อยทําโคดภายในลูปเพื่ออ)านขอมูลแต)ละคอลัมน และ
เมื่อวนซ้ําไปเรื่อยๆจนตัวชี้ถูกขยับไปอยู)ที่ตําแหน)ง "หลัง" แถวสุดทายแลว เมธอด moveToNext จะคืน
ค)า false ทําใหเงื่อนไขของ while เป/น false และออกจากลูปทันที โคดภายในลูปจึงไม)ถูกทําอีก
สําหรับการอ)านขอมูลแต)ละคอลัมนจะใชเมธอด getString, getInt, getShort, getLong,
getFloat, getDouble, getBlob ขึ้นอยู)กับว)า เราจะอ)านขอมูลในคอลัมนนั้นมาเป/นชนิดขอมูลใดใน
Android: www.3bugs.com
1 ก)อนอื่นกําหนด layout ของหนาจอ (ไฟล activity_main.xml) ใหมี TextView 1 อันชื่อ
textView
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World"
android:textSize="16sp"/>
</RelativeLayout>
String[] allColumns = {
DatabaseHelper.COL_ID,
DatabaseHelper.COL_NAME,
DatabaseHelper.COL_PHONE_NUMBER
};
Cursor cursor = mDb.query(DatabaseHelper.TABLE_NAME, allColumns, null,
null, null, null, null);
Android: www.3bugs.com
String text = "";
while (cursor.moveToNext()) {
// ( '(. ก% "'+ ( -
int id = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COL_ID));
String name = cursor.getString(cursor.getColumnIndex(DatabaseHelper
.COL_NAME));
String phoneNumber = cursor.getString(cursor.getColumnIndex
(DatabaseHelper.COL_PHONE_NUMBER));
// ) +' ' ( .'!" ' " ก% /ก'!" " 0" "'+ "
text += String.format("%d, %s, %s\n", id, name, phoneNumber);
}
// ' TextView
((TextView) findViewById(R.id.text_view)).setText(text);
// # * %)' " +, * /ก
cursor.close();
}
3 สั่งรันแอพ จะไดดังรูป
Android: www.3bugs.com
และจะมีขอความใน logcat ดังรูป (ในที่นี้กําหนด filter ใหแสดงเฉพาะ log message ที่มี tag
เป/นคําว)า DatabaseHelper)
SimpleCursorAdapter จะอ)านแถวขอมูลหนึ่งๆจากเคอรเซอรมาแสดงเป/นไอเท็มหนึ่งๆใน
ListView โดยเราตองเตรียม layout file ที่กําหนด layout ใหกับไอเท็มเหล)านี้ (ไฟลเดียวใชกับทุกๆ
Android: www.3bugs.com
text1
text2
layout
1 L &# 111-111-1111
2 "F[\ ) 222-222-2222
ListView text1
text2 &( )&C )
layout
android.R.layout.simple_list_item_2
Android: www.3bugs.com
ความหมายของพารามิเตอรต)างๆ
❶ คอนเท็กซ เช)น แอคทิวิตี
❷ layout file ที่กําหนด layout ของแต)ละไอเท็มใน ListView (แต)ละแถวขอมูล)
ในที่นี้เลือกใช layout ที่แอนดรอยดเตรียมมาให (predefined) คือ
android.R.layout.simple_list_item_2 ซึ่งเป/น layout ที่มี TextView 2 อัน
String[] showColumns = {
DatabaseHelper.COL_NAME,
DatabaseHelper.COL_PHONE_NUMBER
};
((ListView) findViewById(R.id.listView)).setAdapter(mAdapter);
Android: www.3bugs.com
Hands On: ก% &) + %ก , < = < ก %4 ListView >& 45
SimpleCursorAdapter
เราจะแกไขโคดจาก Hands On ที่แลวที่เป/นการเขาถึงขอมูลในเคอรเซอรโดยตรง มาเป/นการ
ใช SimpleCursorAdapter
1 แกไข layout โดยลบ TextView แลวเพิ่ม ListView เขามาแทน
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<ListView
android:id="@+id/list_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Android: www.3bugs.com
mHelper = new DatabaseHelper(this);
mDb = mHelper.getWritableDatabase();
String[] allColumns = {
DatabaseHelper.COL_ID,
DatabaseHelper.COL_NAME,
DatabaseHelper.COL_PHONE_NUMBER
};
Cursor cursor = mDb.query(DatabaseHelper.TABLE_NAME, allColumns, null,
null, null, null, null);
// adapter
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2, cursor, showColumns, views, 0);
// ก adapter ก ListView
((ListView) findViewById(R.id.list_view)).setAdapter(mAdapter);
}
@Override
protected void onDestroy() {
super.onDestroy();
// +, *
mAdapter.getCursor().close();
mHelper.close();
}
}
Android: www.3bugs.com
สังเกตว)าเราไม)ไดปeดเคอรเซอรในเมธอด onCreate เหมือนอย)างโคดใน Hands On ที่แลว
เพราะเคอรเซอรจะถูกใชงานโดย adapter ตลอดเวลาที่ขอมูลถูกแสดงออกมาใน ListView
ดังนั้นเราจึงปeดเคอรเซอรตอนที่แอคทิวิตีกําลังจะถูกทําลาย คือในเมธอด onDestroy แทน
3 สั่งรันแอพ จะไดดังรูป
ก% ( กB C + 4 *% +
การดําเนินการกับขอมูลในฐานขอมูลนั้นมีอยู) 4 เรื่อง ไดแก) การสรางหรือเพิ่มขอมูลใหม), การ
อ)านขอมูลมาใชงาน, การแกไข/ปรับปรุงขอมูล และการลบขอมูล เราเรียกการดําเนินการทั้งสี่นี้รวมๆว)า
CRUD (create-read-update-delete)
ที่ผ)านมา เราไดเรียนรูวิธีการอ)านขอมูลจากฐานขอมูลแลว ดังนั้นในหัวขอนี้จะอธิบายการ
ดําเนินการอีก 3 เรื่องที่เหลือ คือ การเพิ่ม การแกไข และการลบ
Android: www.3bugs.com
7 & insert, update C delete
เราทราบแลวว)าการอ)านขอมูลจะใชเมธอด query บนออบเจ็ค SQLiteDatabase สําหรับการ
เพิ่ม แกไข และลบขอมูลนั้นจะใชเมธอด insert, เมธอด update และเมธอด delete ตามลําดับ ซึ่ง
เป/นเมธอดบนออบเจ็ค SQLiteDatabase เช)นเดียวกัน จะขออธิบายรายละเอียดไปทีละเมธอด
เมธอด insert
ใชเพิ่มแถวขอมูลใหม)ลงในเทเบิล ความจริงเราเคยใชเมธอดนี้มาแลวตอนที่เตรียมขอมูลตั้งตน
ใหกับฐานขอมูล (ภายในคลาส DatabaseHelper ซึ่งเป/น helper class ของเรา) แต)ครั้งนี้จะลอง
เรียกใชจากภายในแอคทิวิตีบาง เช)นถาตองการเพิ่มขอมูลผูติดต)อใหม)ชื่อ "มนัสนันท" ที่มีเบอรโทรคือ
333-333-3333 จะเขียนโคดไดดังนี้
ContentValues cv = new ContentValues();
cv.put(DatabaseHelper.COL_NAME, " ");
cv.put(DatabaseHelper.COL_PHONE_NUMBER, "333-333-3333");
mDb.insert( ContentValues
DatabaseHelper.TABLE_NAME, ❶
null, ❷ key value
cv ❸
name "มนัสนันท์"
);
phone_number "333-333-3333"
เมธอด insert มีพารามิเตอร 3 ตัว
❶ ค)าสตริงระบุชื่อเทเบิลที่จะเพิ่มแถวขอมูลลงไป
❷ (ในเอกสารของแอนดรอยดตั้งชื่อพารามิเตอรตัวนี้ว)า nullColumnHack) จะใชกรณี
เพิ่มแถวขอมูลว)าง (empty row) ลงในเทเบิล ซึ่งตองอธิบายเพิ่มเติมว)า SQLite ไม)
อนุญาตใหเพิ่มแถวขอมูลว)างได แต)จะตองมีการกําหนดค)าอย)างนอย 1 คอลัมน
เมธอด insert จึงใหเราระบุชื่อคอลัมนที่เป/น null ไดมาคอลัมนหนึ่ง และหากเรา
ระบุ ContentValues ว)างๆเป/นพารามิเตอรตัวที่ 3 เมธอด insert ก็จะเพิ่มแถว
ขอมูลใหม)โดยกําหนดค)า null ใหกับคอลัมนนั้น
❸ ออบเจ็ค ContentValues ซึ่งระบุชื่อและค)าที่เราตองการกําหนดใหกับคอลัมน
ต)างๆในแถวขอมูลใหม) ในที่นี้กําหนดค)าเฉพาะคอลัมน name และ
Android: www.3bugs.com
phone_number ส)วนคอลัมน _id นั้น SQLite จะกําหนดค)าใหอัตโนมัติ
อันเนื่องมาจากคุณสมบัติ AUTOINCREMENT ที่เราระบุตอนสรางเทเบิล
name "มนัสนันท์"
phone_number "333-333-3333"
mDb.update(
DatabaseHelper.TABLE_NAME, ❶
cv, ❷
DatabaseHelper.COL_NAME + "=?", ❸
new String[] { " " } ❹
);
Android: www.3bugs.com
❶ ค)าสตริงระบุชื่อเทเบิลที่จะแกไขขอมูล
❷ ออบเจ็ค ContenValues ซึ่งระบุชื่อและค)าใหม)ที่เราตองการกําหนดใหคอลัมน
ต)างๆ (ค)าเดิมในคอลัมนจะถูกแทนที่ดวยค)าจาก ContentValues นี้)
❸ ค)าสตริงระบุเงื่อนไขของแถวที่จะถูกแกไขขอมูล เทียบเท)าส)วน WHERE ในภาษา SQL
❹ อารเรยของสตริง ซึ่งค)าสตริงแต)ละค)าจะถูกนําไปแทนที่เครื่องหมาย ? (ถามี) ใน
❸ ตามลําดับ
เมธอด delete
ใชลบแถวขอมูลในเทเบิล โดยสามารถระบุเงื่อนไขของแถวที่ตองการลบได เช)น โคดต)อไปนี้จะ
ลบแถวที่มีเบอรโทรขึ้นตนดวย 333
mDb.delete(
DatabaseHelper.TABLE_NAME, ❶
DatabaseHelper.COL_PHONE_NUMBER + " LIKE ?", ❷
new String[] { "333%" } ❸
);
Android: www.3bugs.com
7 & execSQL
อีกวิธีหนึ่งที่สามารถเพิ่ม แกไข และลบแถวขอมูลได นอกเหนือจากการใชเมธอด insert,
update และ delete ที่อธิบายแลว ก็คือการระบุประโยคคําสั่งในภาษา SQL โดยตรงใหกับเมธอด
ยากแต)อย)างใด)
Android: www.3bugs.com
จากโคด เมธอด String.format จะนําชื่อเทเบิล ชื่อคอลัมน และค)าที่เราตองการกําหนดให
คอลัมนเหล)านั้น แทนลงใน %s ในตัวแปร sql แลวนําสตริงผลลัพธ (คําสั่ง SQL ที่สมบูรณ) เก็บลง
ตัวแปร sql อีกครั้ง ก)อนนําไประบุใหเมธอด execSQL ในบรรทัดสุดทาย
คําสั่ง SQL ที่ส)งใหเมธอด execSQL เป/นเพียงค)าสตริงธรรมดา ดังนั้นเราอาจใชการเชื่อมต)อ
สตริงเพื่อสรางคําสั่ง SQL ที่สมบูรณแทนการใชเมธอด String.format ก็ได แต)จะทําใหโคดดูเขาใจ
ยากกว)า
ตัวอย่ างการแก้ ไขข้ อมูล
ถาตองการเปลี่ยนเบอรโทรของ "พรอมเลิศ" เป/น 555-555-5555 จะเขียน SQL ไดดังนี้ (จริงๆ
เขียนต)อเนื่องกันไปในบรรทัดเดียวก็ได)
UPDATE contacts
SET phone_number='555-555-5555'
WHERE name=' '
Android: www.3bugs.com
ตัวอย่ างการลบข้ อมูล
ถาตองการลบแถวที่มีเบอรโทรขึ้นตนดวย 333 จะเขียน SQL ไดดังนี้ (จริงๆเขียนต)อเนื่องกันไป
ในบรรทัดเดียวก็ได)
DELETE FROM contacts
WHERE phone_number LIKE '333%'
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
Android: www.3bugs.com
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<ListView
android:id="@+id/list_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/insert_button"/>
<Button
android:id="@+id/insert_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text=" "/>
<Button
android:id="@+id/update_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/insert_button"
android:layout_toEndOf="@+id/insert_button"
android:layout_toRightOf="@+id/insert_button"
android:text=" ก "/>
<Button
android:id="@+id/delete_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/insert_button"
android:layout_toEndOf="@+id/update_button"
android:layout_toRightOf="@+id/update_button"
android:text=" "/>
</RelativeLayout>
Android: www.3bugs.com
2 เพิ่มคําสั่ง implements View.OnClickListener ที่บรรทัดการประกาศคลาส MainActivity
เพื่อใชแอคทิวิตีเป/นตัวจัดการ onClick ของวิว (ใชแอคทิวิตีเป/น onClick listener)
public class MainActivity extends ActionBarActivity
implements View.OnClickListener {
newCursor = readAllData(); ❷
mAdapter.changeCursor(newCursor); ❸
break;
Android: www.3bugs.com
case R.id.update_button:
cv = new ContentValues();
cv.put(DatabaseHelper.COL_PHONE_NUMBER, "555-555-5555");
mDb.update( ❶
DatabaseHelper.TABLE_NAME,
cv,
DatabaseHelper.COL_NAME + "=?",
new String[] { " " }
);
newCursor = readAllData(); ❷
mAdapter.changeCursor(newCursor); ❸
break;
case R.id.delete_button:
mDb.delete( ❶
DatabaseHelper.TABLE_NAME,
DatabaseHelper.COL_PHONE_NUMBER + " LIKE ?",
new String[] { "333%" }
);
newCursor = readAllData(); ❷
mAdapter.changeCursor(newCursor); ❸
break;
}
}
จากโคด เราเพิ่ม แกไข และลบขอมูล โดยใชเมธอด insert, update และ delete บนออบเจ็ค
SQLiteDatabase ❶ แต)หากคุณตองการใชเมธอด execSQL ก็สามารถทําไดเช)นกัน
Android: www.3bugs.com
ถ้าคุณเคยใช้ ArrayAdapter (adapter ทีน+ ําข้อมูลจากอาร์เรย์มาแสดงผลใน ListView) มา
ก่อน อาจทราบว่า ArrayAdapter มีเมธอด notifyDataSetChanged ซึง+ เมือ+ ข้อมูลในอาร์เรย์
เปลีย+ นไป เราจะเรียกเมธอดนี0เพือ+ อัพเดท ListView
SimpleCursorAdapter ก็มเี มธอด notifyDataSetChanged เช่นเดียวกัน แต่ในตัวอย่างนี0
เราไม่สามารถอัพเดท ListView ด้วยเมธอดดังกล่าวได้ เพราะเมธอดดังกล่าวจะแจ้ง
ListView ว่าแหล่งข้อมูลทีผ + กู อยูก่ บั ListView ซึง+ กรณีน0ีคอื เคอร์เซอร์ เปลีย+ นแปลงไป แต่
สําหรับตัวอย่างนี0 ข้อมูลในเคอร์เซอร์ไม่ได้เปลีย+ น หากแต่เป็ นข้อมูลในฐานข้อมูลทีเ+ ปลีย+ น ดังนัน0
เราจําเป็ นต้องคิวรีฐานข้อมูลใหม่
ฐานข้ อมูล
Android: www.3bugs.com
mHelper = new DatabaseHelper(this);
mDb = mHelper.getWritableDatabase();
String[] allColumns = {
DatabaseHelper.COL_ID,
DatabaseHelper.COL_NAME,
DatabaseHelper.COL_PHONE_NUMBER
};
Cursor cursor = mDb.query(DatabaseHelper.TABLE_NAME, allColumns, null,
null, null, null, null);
...
Android: www.3bugs.com
โค้ ดทั.งหมดในแอคทิวติ ี
ตอนนี้แอคทิวิตีจะมีโคดทั้งหมดดังนี้
package com.example.sqlitebasics;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Android: www.3bugs.com
// "" layout ).) (ก result set
int[] views = {
android.R.id.text1,
android.R.id.text2
};
// adapter
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2, cursor, showColumns, views, 0);
// ก adapter ก ListView
((ListView) findViewById(R.id.listView)).setAdapter(mAdapter);
@Override
protected void onDestroy() {
super.onDestroy();
// +, *
mAdapter.getCursor().close();
mHelper.close();
}
Android: www.3bugs.com
@Override
public void onClick(View v) {
ContentValues cv;
Cursor newCursor;
newCursor = readAllData();
mAdapter.changeCursor(newCursor);
break;
case R.id.update_button:
cv = new ContentValues();
cv.put(DatabaseHelper.COL_PHONE_NUMBER, "555-555-5555");
mDb.update(
DatabaseHelper.TABLE_NAME,
cv,
DatabaseHelper.COL_NAME + "=?",
new String[] { " " }
);
newCursor = readAllData();
mAdapter.changeCursor(newCursor);
break;
case R.id.delete_button:
mDb.delete(
DatabaseHelper.TABLE_NAME,
DatabaseHelper.COL_PHONE_NUMBER + " LIKE ?",
new String[] { "333%" }
);
Android: www.3bugs.com
newCursor = readAllData();
mAdapter.changeCursor(newCursor);
break;
}
}
}
ทดสอบการเพิมข้ อมูล
Android: www.3bugs.com
ทดสอบการแก้ ไขข้ อมูล
(#ก
Android: www.3bugs.com
ทดสอบการลบข้ อมูล
(#ก
Android: www.3bugs.com