Tại sao android dùng db sqlite

Các khóa học qua video:Lập trình C Java C# SQL Server PHP HTML5-CSS3-JavaScriptLưu dữ liệu vào cơ sở dữ liệu là lý tưởng để lặp lại hoặc cấu trúc dữ liệu, chẳng hạn như thông tin liên hệ. Bài viết này giả định rằng bạn đã quen thuộc với cơ sở dữ liệu SQL nói chung và giúp bạn bắt đầu với cơ sở dữ liệu SQLite trên Android. Các API bạn sẽ cần sử dụng cơ sở dữ liệu trên Android có sẵn trong gói android.database.sqlite.

Thận trọng: Mặc dù các API này rất mạnh, nhưng chúng ở mức độ khá thấp và đòi hỏi nhiều thời gian và công sức để sử dụng:

  • Không có xác minh thời gian biên dịch các truy vấn SQL thô. Khi biểu đồ dữ liệu của bạn thay đổi, bạn cần cập nhật các truy vấn SQL bị ảnh hưởng theo cách thủ công. Quá trình này có thể tốn thời gian và dễ bị lỗi.
  • Bạn cần sử dụng nhiều mã soạn sẵn để chuyển đổi giữa các truy vấn SQL và các đối tượng dữ liệu.

Vì những lý do này, chúng tôi khuyên bạn nên sử dụng Room Persistence Library làm lớp trừu tượng để truy cập thông tin trong cơ sở dữ liệu SQLite của ứng dụng.

Xác định một lược đồ và hợp đồng

Một trong những nguyên tắc chính của cơ sở dữ liệu SQL là lược đồ: một khai báo chính thức về cách tổ chức cơ sở dữ liệu. Lược đồ được phản ánh trong các câu lệnh SQL mà bạn sử dụng để tạo cơ sở dữ liệu của mình. Bạn có thể thấy hữu ích khi tạo một lớp đồng hành, được gọi là lớp hợp đồng (contract), trong đó chỉ định rõ ràng bố cục của lược đồ của bạn theo cách có hệ thống và tự viết tài liệu.

Một lớp contract là một bộ chứa các hằng số xác định tên cho các URI, bảng và cột. Lớp hợp đồng cho phép bạn sử dụng cùng các hằng số trên tất cả các lớp khác trong cùng một gói. Điều này cho phép bạn thay đổi tên cột ở một nơi và để nó lan truyền trong toàn bộ mã của bạn.

Một cách tốt để tổ chức một lớp contract là đặt các định nghĩa toàn cầu cho toàn bộ cơ sở dữ liệu của bạn ở cấp độ gốc của lớp. Sau đó tạo một lớp bên trong cho mỗi bảng. Mỗi lớp bên trong liệt kê các cột của bảng tương ứng.

Lưu ý: Bằng cách triển khai interface BaseColumns, lớp bên trong của bạn có thể kế thừa trường khóa chính được gọi là _ID trong đó một số lớp Android như CursorAdapter mong muốn điều đó. Không bắt buộc, nhưng điều này có thể giúp cơ sở dữ liệu của bạn hoạt động hài hòa với Android framework.

Ví dụ: contract sau đây xác định tên bảng và tên cột cho một bảng duy nhất đại diện cho nguồn cấp RSS:

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";
    }
}

Tạo cơ sở dữ liệu bằng trình trợ giúp SQL

Khi bạn đã xác định giao diện cơ sở dữ liệu của mình, bạn nên triển khai các phương thức tạo và duy trì cơ sở dữ liệu và bảng. Dưới đây là một số câu lệnh điển hình tạo và xóa bảng:

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;

Giống như các tệp bạn lưu trên bộ nhớ trong của thiết bị, Android lưu trữ cơ sở dữ liệu của bạn trong thư mục riêng của ứng dụng. Dữ liệu của bạn được bảo mật, vì theo mặc định, khu vực này không thể truy cập được vào các ứng dụng khác hoặc người dùng.

Lớp SQLiteOpenHelper chứa một tập hữu ích của các API để quản lý cơ sở dữ liệu của bạn. Khi bạn sử dụng lớp này để có được các tham chiếu đến cơ sở dữ liệu của mình, hệ thống sẽ thực hiện các hoạt động có khả năng lâu dài là tạo và cập nhật cơ sở dữ liệu chỉ khi cần chứ không phải trong quá trình khởi động ứng dụng. Tất cả bạn cần làm là gọi getWritableDatabase() hoặc getReadableDatabase().

Lưu ý: Vì chúng có thể chạy trong thời gian dài, hãy chắc chắn rằng bạn gọi getWritableDatabase() hoặc getReadableDatabase()trong một chuỗi nền. 

Để sử dụng SQLiteOpenHelper, hãy tạo một lớp con ghi đè các phương thức callback là onCreate() và onUpgrade(). Bạn cũng có thể muốn thực hiện các phương thức onDowngrade() hoặc onOpen(), nhưng chúng là không bắt buộc.

Ví dụ: đây là một triển khai SQLiteOpenHelper sử dụng một số lệnh được hiển thị ở trên:

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);
    }
}

Để truy cập cơ sở dữ liệu của bạn, khởi tạo lớp con của SQLiteOpenHelper:

FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());

Đưa thông tin vào cơ sở dữ liệu

Chèn dữ liệu vào cơ sở dữ liệu bằng cách truyền một đối tượng ContentValues cho phương thức insert():

// 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);

Đối số đầu tiên insert() chỉ đơn giản là tên bảng.

Đối số thứ hai cho biết framework phải làm gì trong trường hợp ContentValues trống (nghĩa là bạn không put bất kỳ giá trị nào). Nếu bạn chỉ định tên của một cột, framework sẽ chèn một hàng và đặt giá trị của cột đó thành null. Nếu bạn chỉ định null, như trong ví dụ trên, thì famework không chèn một hàng khi không có giá trị.

Các phương thức insert() trả về ID cho hàng vừa tạo hoặc nó sẽ trả về -1 nếu có lỗi khi chèn dữ liệu. Điều này có thể xảy ra nếu bạn có xung đột với dữ liệu có sẵn trong cơ sở dữ liệu.

Đọc thông tin từ cơ sở dữ liệu

Để đọc từ cơ sở dữ liệu, hãy sử dụng phương thức query(), truyền nó theo tiêu chí lựa chọn của bạn và các cột mong muốn. Phương thức sẽ kết hợp các thành phần của insert() và update(), ngoại trừ danh sách cột xác định dữ liệu bạn muốn tìm nạp ("phép chiếu"), thay vì dữ liệu cần chèn. Kết quả của truy vấn là đối tượng Cursor được trả về.

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
    );

Đối số thứ ba và thứ tư ( selection và selectionArgs) được kết hợp để tạo mệnh đề WHERE. Bởi vì các đối số được cung cấp riêng biệt với truy vấn lựa chọn, chúng được thoát trước khi được kết hợp. Điều này làm cho các lệnh select của bạn miễn dịch với SQL injection.

Để xem một hàng bằng con trỏ, hãy sử dụng một trong các phương thức dịch chuyển Cursor, trong đó bạn phải luôn gọi trước khi bắt đầu đọc các giá trị. Vì con trỏ bắt đầu ở vị trí -1, nên việc gọi moveToNext() sẽ đặt "vị trí đọc-read position" trên mục nhập đầu tiên trong kết quả và trả về việc con trỏ có vượt quá mục nhập cuối cùng trong tập kết quả hay không. Đối với mỗi hàng, bạn có thể đọc giá trị của một cột bằng cách gọi một trong các phương thức get Cursor, chẳng hạn như getString()  hoặc getLong(). Đối với mỗi phương thức get, bạn phải vượt qua vị trí chỉ mục của cột mà bạn mong muốn, bạn có thể nhận được bằng cách gọi getColumnIndex() hoặc getColumnIndexOrThrow(). Khi hoàn thành việc lặp qua các kết quả, hãy gọi con trỏ close() để giải phóng tài nguyên của nó. Ví dụ: phần sau đây cho biết cách lấy tất cả ID của các item được lưu trữ trong một con trỏ và thêm chúng vào danh sách:

List itemIds = new ArrayList<>();
while(cursor.moveToNext()) {
  long itemId = cursor.getLong(
      cursor.getColumnIndexOrThrow(FeedEntry._ID));
  itemIds.add(itemId);
}
cursor.close();

Xóa thông tin khỏi cơ sở dữ liệu

Để xóa các hàng khỏi một bảng, bạn cần cung cấp các tiêu chí lựa chọn xác định các hàng cho phương thức delete(). Cơ chế hoạt động giống như các đối số lựa chọn cho phương thức query(). Nó phân chia đặc tả lựa chọn thành một mệnh đề lựa chọn và đối số lựa chọn. Mệnh đề xác định các cột cần xem xét và cũng cho phép bạn kết hợp các kiểm tra cột. Các đối số là các giá trị để kiểm tra đối với điều đó được ràng buộc vào mệnh đề. Vì kết quả không được xử lý giống như một câu lệnh SQL thông thường, nên nó miễn nhiễm với SQL injection.

// 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);

Giá trị trả về của phương thức delete() cho biết số lượng hàng đã bị xóa khỏi cơ sở dữ liệu.

Cập nhật cơ sở dữ liệu

Phương thức update() dùng để sửa đổi một tập hợp con các giá trị cơ sở dữ liệu.

Cập nhật bảng kết hợp mệnh đề ContentValues của insert() với mệnh đề WHERE của delete().

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);

Giá trị trả về của phương thức update() là số lượng hàng bị ảnh hưởng trong cơ sở dữ liệu.

Kết nối cơ sở dữ liệu bền bỉ

Vì việc gọi getWritableDatabase() và getReadableDatabase() mất nhiều thời gian khi cơ sở dữ liệu bị đóng, vậy nên bạn nên để kết nối cơ sở dữ liệu của mình mở. Thường thì tối ưu nhất là đóng cơ sở dữ liệu bằng onDestroy() thông qua lời gọi Activity.

@Override
protected void onDestroy() {
    dbHelper.close();
    super.onDestroy();
}

Gỡ lỗi cơ sở dữ liệu của bạn

SDK Android bao gồm một tool shell sqlite3 cho phép bạn duyệt nội dung bảng, chạy các lệnh SQL và thực hiện các chức năng hữu ích khác trên cơ sở dữ liệu SQLite. Để biết thêm thông tin.

Tại sao android dùng db sqlite

Các khóa học qua video:
Lập trình C Java C# SQL Server PHP HTML5-CSS3-JavaScript
« Prev: Android: Intent Standard Category trong Android
» Next: Android: Hiệu ứng Animation Alpha