การบันทึกข้อมูลลงในฐานข้อมูลเหมาะสำหรับการทำซ้ำหรือข้อมูลที่มีโครงสร้าง
เช่น ข้อมูลติดต่อ หน้านี้จะถือว่าคุณ
ความคุ้นเคยกับฐานข้อมูล SQL โดยทั่วไป และช่วยให้คุณเริ่มต้นใช้งาน
ฐานข้อมูล SQLite บน Android API ที่คุณจะต้องใช้ฐานข้อมูล
บน Android มีให้บริการในแพ็กเกจ android.database.sqlite
ข้อควรระวัง: แม้ว่า API เหล่านี้จะมีประสิทธิภาพ แต่ก็อยู่ในระดับที่ค่อนข้างต่ำ ซึ่งต้องใช้เวลาและความพยายามอย่างมากในการใช้:
- ไม่มีการยืนยันเวลาคอมไพล์ของการค้นหา SQL แบบข้อมูลดิบ ในรูปแบบข้อมูลของคุณ เปลี่ยนแปลง คุณต้องอัปเดตการค้นหา SQL ที่ได้รับผลกระทบด้วยตนเอง ช่วงเวลานี้ อาจใช้เวลามากและมีโอกาสเกิดข้อผิดพลาด
- คุณต้องใช้โค้ดต้นแบบจำนวนมากเพื่อแปลงการค้นหา SQL ต่างๆ และออบเจ็กต์ข้อมูล
ด้วยเหตุนี้ เราจึงขอแนะนำอย่างยิ่งให้ใช้ คลังภาพ Room Persistence เป็นเลเยอร์นามธรรมสำหรับเข้าถึงข้อมูลใน SQLite ของแอป ฐานข้อมูล
กำหนดสคีมาและสัญญา
หนึ่งในหลักการสำคัญของฐานข้อมูล SQL คือสคีมา: การประกาศการจัดระเบียบฐานข้อมูล สคีมาจะแสดงใน SQL ที่คุณใช้สร้างฐานข้อมูล คุณอาจพบว่ามีประโยชน์กับ สร้างคลาสที่แสดงร่วม ซึ่งเรียกว่าคลาส contract ซึ่งระบุ การจัดวางสคีมาของคุณให้เป็นระบบและมีการจัดทำเอกสารประกอบด้วยตนเอง
คลาสสัญญาคือคอนเทนเนอร์สำหรับค่าคงที่ที่กำหนดชื่อสำหรับ URI ตาราง และคอลัมน์ คลาสสัญญาอนุญาตให้คุณใช้ค่าคงที่เดียวกันได้ คลาสอื่นๆ ทั้งหมดในแพ็กเกจเดียวกัน การดำเนินการนี้ช่วยให้คุณเปลี่ยนคอลัมน์ ไว้ในที่เดียวเพื่อให้เผยแพร่ทั่วโค้ดของคุณ
วิธีที่ดีในการจัดระเบียบคลาสสัญญาคือการใช้คำจำกัดความที่ ทั้งหมดไปยังฐานข้อมูลทั้งหมดในระดับรากของชั้นเรียน จากนั้นสร้างอินเนอร์ สำหรับแต่ละตาราง คลาสภายในแต่ละคลาสจะแจกแจงคอลัมน์ของตารางที่เกี่ยวข้อง
หมายเหตุ: การใช้ BaseColumns
ทำให้ชั้นเรียนภายในของคุณ สามารถสืบทอดระดับ
ช่องคีย์ _ID
ที่คลาส Android บางคลาส เช่น CursorAdapter
คาดว่าจะมี ไม่จำเป็น แต่การดำเนินการนี้จะช่วยฐานข้อมูลของคุณได้
ทำงานร่วมกับเฟรมเวิร์ก Android ได้อย่างกลมกลืน
ตัวอย่างเช่น สัญญาต่อไปนี้จะกำหนดชื่อตารางและชื่อคอลัมน์สำหรับ ตารางเดียวที่แสดงฟีด RSS:
Kotlin
object FeedReaderContract { // Table contents are grouped together in an anonymous object. object FeedEntry : BaseColumns { const val TABLE_NAME = "entry" const val COLUMN_NAME_TITLE = "title" const val COLUMN_NAME_SUBTITLE = "subtitle" } }
Java
public final class FeedReaderContract { // To prevent someone from accidentally instantiating the contract class, // make the constructor private. private FeedReaderContract() {} /* Inner class that defines the table contents */ public static class FeedEntry implements BaseColumns { public static final String TABLE_NAME = "entry"; public static final String COLUMN_NAME_TITLE = "title"; public static final String COLUMN_NAME_SUBTITLE = "subtitle"; } }
สร้างฐานข้อมูลโดยใช้ตัวช่วยเหลือ SQL
เมื่อคุณกำหนดลักษณะของฐานข้อมูลแล้ว คุณควรนำเมธอด ที่สร้างและดูแลรักษาฐานข้อมูลและตาราง ตัวอย่าง URL ทั่วไป ที่สร้างและลบตาราง:
Kotlin
private const val SQL_CREATE_ENTRIES = "CREATE TABLE ${FeedEntry.TABLE_NAME} (" + "${BaseColumns._ID} INTEGER PRIMARY KEY," + "${FeedEntry.COLUMN_NAME_TITLE} TEXT," + "${FeedEntry.COLUMN_NAME_SUBTITLE} TEXT)" private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${FeedEntry.TABLE_NAME}"
Java
private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + FeedEntry._ID + " INTEGER PRIMARY KEY," + FeedEntry.COLUMN_NAME_TITLE + " TEXT," + FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)"; private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
เช่นเดียวกับไฟล์ที่คุณบันทึกไว้ในภายในของอุปกรณ์ พื้นที่เก็บข้อมูล Android จะจัดเก็บฐานข้อมูลไว้ในโฟลเดอร์ส่วนตัวของแอป ข้อมูลของคุณ เนื่องจากโดยค่าเริ่มต้น พื้นที่นี้จะ ที่แอปอื่นๆ หรือผู้ใช้เข้าถึงได้
คลาส SQLiteOpenHelper
มีฟังก์ชัน
API ชุดหนึ่งสำหรับจัดการฐานข้อมูล
เมื่อคุณใช้คลาสนี้เพื่อรับการอ้างอิงไปยังฐานข้อมูล ระบบจะ
จะแสดงความเป็นไปได้
การดำเนินการสร้างและอัปเดตฐานข้อมูลที่ใช้เวลานานเฉพาะเมื่อ
และไม่ใช้ในระหว่างการเริ่มต้นแอป ทั้งหมดที่คุณต้องทำคือการโทร
getWritableDatabase()
หรือ
getReadableDatabase()
หมายเหตุ: เนื่องจากอาจใช้เวลานาน
โปรดตรวจสอบว่าคุณเรียกใช้ getWritableDatabase()
หรือ getReadableDatabase()
ในชุดข้อความเบื้องหลัง
ดูข้อมูลเพิ่มเติมได้ที่ชุดข้อความใน Android
หากต้องการใช้ SQLiteOpenHelper
ให้สร้างคลาสย่อยที่
จะลบล้าง onCreate()
และ
onUpgrade()
วิธีในการติดต่อกลับ คุณสามารถ
และต้องการใช้
onDowngrade()
หรือ
onOpen()
วิธีการ
แต่ไม่จำเป็น
ลองดูตัวอย่างการใช้งาน SQLiteOpenHelper
ที่
ใช้คำสั่งบางรายการที่แสดงด้านบน
Kotlin
class FeedReaderDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(SQL_CREATE_ENTRIES) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES) onCreate(db) } override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { onUpgrade(db, oldVersion, newVersion) } companion object { // If you change the database schema, you must increment the database version. const val DATABASE_VERSION = 1 const val DATABASE_NAME = "FeedReader.db" } }
Java
public class FeedReaderDbHelper extends SQLiteOpenHelper { // If you change the database schema, you must increment the database version. public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "FeedReader.db"; public FeedReaderDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); } }
หากต้องการเข้าถึงฐานข้อมูล ให้สร้างอินสแตนซ์คลาสย่อยของ
SQLiteOpenHelper
:
Kotlin
val dbHelper = FeedReaderDbHelper(context)
Java
FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());
ใส่ข้อมูลลงในฐานข้อมูล
แทรกข้อมูลลงในฐานข้อมูลโดยการส่ง ContentValues
ของเมธอด insert()
:
Kotlin
// Gets the data repository in write mode val db = dbHelper.writableDatabase // Create a new map of values, where column names are the keys val values = ContentValues().apply { put(FeedEntry.COLUMN_NAME_TITLE, title) put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle) } // Insert the new row, returning the primary key value of the new row val newRowId = db?.insert(FeedEntry.TABLE_NAME, null, values)
Java
// Gets the data repository in write mode SQLiteDatabase db = dbHelper.getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, title); values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle); // Insert the new row, returning the primary key value of the new row long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
อาร์กิวเมนต์แรกสำหรับ insert()
ก็คือชื่อตาราง
อาร์กิวเมนต์ที่สองบอกเฟรมเวิร์กถึงสิ่งที่ต้องทำในกรณีที่
ContentValues
ว่างเปล่า (เช่น คุณไม่ได้
put
ค่าใดก็ได้)
หากคุณระบุชื่อคอลัมน์ เฟรมเวิร์กจะแทรกแถวและชุดข้อมูล
ค่าของคอลัมน์นั้นเป็นค่าว่าง หากคุณระบุ null
เช่นในข้อนี้
ตัวอย่างโค้ดจะไม่แทรกแถวเมื่อไม่มีค่าใดๆ
เมธอด insert()
จะแสดงรหัสสำหรับ
แถวที่สร้างใหม่ ไม่เช่นนั้นจะแสดง -1 หากเกิดข้อผิดพลาดในการแทรกข้อมูล กรณีนี้เกิดขึ้นได้
หากมีข้อขัดแย้งกับข้อมูลที่มีอยู่แล้วในฐานข้อมูล
อ่านข้อมูลจากฐานข้อมูล
หากต้องการอ่านจากฐานข้อมูล ให้ใช้เมธอด query()
เพื่อส่งเกณฑ์การเลือกและคอลัมน์ที่ต้องการไปยังฐานข้อมูล
เมธอดรวมองค์ประกอบของ insert()
และ update()
ยกเว้นรายการคอลัมน์
ระบุข้อมูลที่คุณต้องการดึงข้อมูล ("เส้นโครง") ไม่ใช่ข้อมูลที่จะแทรก ผลลัพธ์
ของการค้นหาจะส่งกลับมาให้คุณในออบเจ็กต์ Cursor
Kotlin
val db = dbHelper.readableDatabase // Define a projection that specifies which columns from the database // you will actually use after this query. val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE) // Filter results WHERE "title" = 'My Title' val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?" val selectionArgs = arrayOf("My Title") // How you want the results sorted in the resulting Cursor val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC" val cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order )
Java
SQLiteDatabase db = dbHelper.getReadableDatabase(); // Define a projection that specifies which columns from the database // you will actually use after this query. String[] projection = { BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE }; // Filter results WHERE "title" = 'My Title' String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?"; String[] selectionArgs = { "My Title" }; // How you want the results sorted in the resulting Cursor String sortOrder = FeedEntry.COLUMN_NAME_SUBTITLE + " DESC"; Cursor cursor = db.query( FeedEntry.TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs, // The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order );
อาร์กิวเมนต์ที่ 3 และ 4 (selection
และ selectionArgs
) คือ
รวมกันเพื่อสร้างอนุประโยค WHERE เนื่องจากอาร์กิวเมนต์มีให้แยกต่างหากจากสิ่งที่เลือก
คำค้นหา ให้กำหนดเป็นอักขระหลีกก่อนที่จะรวมเข้าด้วยกัน การดำเนินการนี้จะทำให้คำสั่งการเลือกของคุณไม่มีภูมิคุ้มกันต่อ SQL
แบบฉีดได้ โปรดดูรายละเอียดเพิ่มเติมเกี่ยวกับอาร์กิวเมนต์ทั้งหมดที่หัวข้อ
ข้อมูลอ้างอิง query()
รายการ
หากต้องการดูแถวในเคอร์เซอร์ ให้ใช้การเลื่อน Cursor
อย่างใดอย่างหนึ่ง
ซึ่งคุณต้องเรียกใช้ทุกครั้งก่อนเริ่มอ่านค่า เนื่องจากเคอร์เซอร์เริ่มต้นที่
ตำแหน่ง -1 การเรียก moveToNext()
จะเป็นการแสดง "ตำแหน่งการอ่าน" ใน
รายการแรกในผลลัพธ์ และแสดงผลว่าเคอร์เซอร์ผ่านรายการสุดท้ายไปแล้วหรือไม่
ชุดผลลัพธ์ สำหรับแต่ละแถว คุณจะอ่านค่าของคอลัมน์ได้โดยเรียกค่าใดค่าหนึ่งของ
Cursor
รับเมธอด เช่น getString()
หรือ getLong()
สำหรับแต่ละเมธอด Get แต่ละเมธอด
คุณต้องผ่านตำแหน่งดัชนีของคอลัมน์ที่ต้องการ ซึ่งคุณจะได้รับจากการเรียก
getColumnIndex()
หรือ
getColumnIndexOrThrow()
เมื่อเสร็จสิ้น
กำลังทำซ้ำผ่านผลลัพธ์ ให้เรียก close()
บนเคอร์เซอร์
ในการปล่อยทรัพยากร
ตัวอย่างต่อไปนี้แสดงวิธีรับรหัสสินค้าทั้งหมดที่จัดเก็บไว้ในเคอร์เซอร์
และเพิ่มลงในรายการ เช่น
Kotlin
val itemIds = mutableListOf<Long>() with(cursor) { while (moveToNext()) { val itemId = getLong(getColumnIndexOrThrow(BaseColumns._ID)) itemIds.add(itemId) } } cursor.close()
Java
List itemIds = new ArrayList<>(); while(cursor.moveToNext()) { long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID)); itemIds.add(itemId); } cursor.close();
ลบข้อมูลออกจากฐานข้อมูล
หากต้องการลบแถวออกจากตาราง คุณต้องระบุเกณฑ์การเลือกที่
ระบุแถวที่จะใช้เมธอด delete()
จะทำงานเหมือนกับอาร์กิวเมนต์การเลือก
query()
วิธี จะแบ่ง
เป็นข้อกำหนดการเลือกและอาร์กิวเมนต์การเลือก
จะกำหนดคอลัมน์ที่จะดู และคุณยังรวมคอลัมน์
การทดสอบ อาร์กิวเมนต์คือค่าที่จะทดสอบเทียบกับค่าที่อยู่ในอนุประโยค
เนื่องจากผลลัพธ์ไม่ได้รับการจัดการเหมือนกับคำสั่ง SQL ปกติ
มีภูมิคุ้มกันต่อการแทรก SQL
Kotlin
// Define 'where' part of query. val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?" // Specify arguments in placeholder order. val selectionArgs = arrayOf("MyTitle") // Issue SQL statement. val deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs)
Java
// Define 'where' part of query. String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?"; // Specify arguments in placeholder order. String[] selectionArgs = { "MyTitle" }; // Issue SQL statement. int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
ผลลัพธ์สำหรับเมธอด delete()
ระบุจำนวนแถวที่ถูกลบจากฐานข้อมูล
อัปเดตฐานข้อมูล
เมื่อคุณต้องแก้ไขชุดย่อยของค่าฐานข้อมูล ให้ใช้เมธอด
update()
วิธี
การอัปเดตตารางจะรวมไวยากรณ์ ContentValues
ของ
insert()
ที่มีไวยากรณ์ WHERE
จาก delete()
Kotlin
val db = dbHelper.writableDatabase // New value for one column val title = "MyNewTitle" val values = ContentValues().apply { put(FeedEntry.COLUMN_NAME_TITLE, title) } // Which row to update, based on the title val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?" val selectionArgs = arrayOf("MyOldTitle") val count = db.update( FeedEntry.TABLE_NAME, values, selection, selectionArgs)
Java
SQLiteDatabase db = dbHelper.getWritableDatabase(); // New value for one column String title = "MyNewTitle"; ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, title); // Which row to update, based on the title String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?"; String[] selectionArgs = { "MyOldTitle" }; int count = db.update( FeedReaderDbHelper.FeedEntry.TABLE_NAME, values, selection, selectionArgs);
ค่าที่ส่งกลับของเมธอด update()
คือ
จำนวนแถวที่ได้รับผลกระทบในฐานข้อมูล
ยังคงเชื่อมต่อฐานข้อมูลอยู่
ตั้งแต่วันที่ getWritableDatabase()
และ getReadableDatabase()
มีค่าใช้จ่ายสูงในการเรียกใช้เมื่อฐานข้อมูลปิด คุณควรออกจากการเชื่อมต่อฐานข้อมูล
ให้นานตราบเท่าที่คุณจำเป็นต้องเข้าถึง โดยปกติแล้ว การปิดฐานข้อมูล
ใน onDestroy()
ของกิจกรรมการโทร
Kotlin
override fun onDestroy() { dbHelper.close() super.onDestroy() }
Java
@Override protected void onDestroy() { dbHelper.close(); super.onDestroy(); }
แก้ไขข้อบกพร่องของฐานข้อมูล
Android SDK มีเครื่องมือ Shell sqlite3
ที่ช่วยให้คุณเรียกดู
เนื้อหาตาราง เรียกใช้คำสั่ง SQL และใช้ฟังก์ชันอื่นๆ ที่เป็นประโยชน์ใน SQLite
ฐานข้อมูล ดูข้อมูลเพิ่มเติมได้ที่วิธีออกคำสั่ง Shell