บันทึกข้อมูลโดยใช้ SQLite

การบันทึกข้อมูลลงในฐานข้อมูลเหมาะสำหรับการทำซ้ำหรือข้อมูลที่มีโครงสร้าง เช่น ข้อมูลติดต่อ หน้านี้จะถือว่าคุณ ความคุ้นเคยกับฐานข้อมูล 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