Android Java
Xây dựng kho dữ liệu SQLite cho ứng dụng Android
Thực hiện thêm, đọc, sửa, xóa danh lam thắng cảnh. Dùng tham số truy vấn và giao dịch khi ghi nhiều bản ghi.
Thích và đánh giá Bình luận và hỏi đáp1. Thiết kế bảng cho danh lam thắng cảnh
Ứng dụng cần lưu tên địa điểm và khu vực để người dùng có thể tìm, sửa hoặc xóa khi đang ngoại tuyến. Một bảng place đủ cho ví dụ. Mỗi dòng có _id kiểu số nguyên làm khóa chính, còn name và area là văn bản bắt buộc. Hai địa điểm có thể trùng tên, vì vậy thao tác sửa và xóa dùng ID thay vì tên hiển thị.
Giao diện gọi một lớp kho dữ liệu với các hàm rõ mục đích. Lớp kho chuẩn hóa chuỗi, tạo câu lệnh và chuyển kết quả truy vấn thành đối tượng Java. Ràng buộc CHECK trong bảng là một lớp kiểm tra nữa nếu dữ liệu được ghi qua đường khác.
2. Triển khai kho dữ liệu bằng SQLiteOpenHelper
Lưu PlaceStore.java vào dự án Android Java. Constructor dùng ngữ cảnh ứng dụng. SQLiteOpenHelper sẽ gọi onCreate khi cơ sở dữ liệu được tạo lần đầu. Bản mẫu khai báo lược đồ phiên bản 1. Khi phát triển phiên bản tiếp theo, hãy bổ sung đường nâng cấp trong onUpgrade và thử trên bản sao cơ sở dữ liệu cũ.
Môi trường thực hành. Mẫu Java dùng trong dự án Android với AndroidX và các tài nguyên giao diện được mô tả trong bài. Tệp này chưa phải một ứng dụng Android hoàn chỉnh.
package vn.utt.sotay;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
public final class PlaceStore extends SQLiteOpenHelper {
private static final String TABLE = "place";
public PlaceStore(Context context) {
super(context.getApplicationContext(), "places.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE place ("
+ "_id INTEGER PRIMARY KEY,"
+ "name TEXT NOT NULL CHECK(length(trim(name)) > 0),"
+ "area TEXT NOT NULL CHECK(length(trim(area)) > 0))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
throw new IllegalStateException("Thiếu migration " + oldVersion + " -> " + newVersion);
}
public long add(String name, String area) {
return getWritableDatabase().insertOrThrow(TABLE, null, values(name, area));
}
public List<Place> findByArea(String area) {
List<Place> result = new ArrayList<>();
String[] columns = {"_id", "name", "area"};
try (Cursor cursor = getReadableDatabase().query(TABLE, columns,
"area = ?", new String[]{required(area)}, null, null, "name, _id")) {
while (cursor.moveToNext()) {
result.add(new Place(cursor.getLong(0), cursor.getString(1), cursor.getString(2)));
}
}
return result;
}
public boolean update(long id, String name, String area) {
return getWritableDatabase().update(TABLE, values(name, area),
"_id = ?", new String[]{Long.toString(id)}) == 1;
}
public boolean remove(long id) {
return getWritableDatabase().delete(TABLE,
"_id = ?", new String[]{Long.toString(id)}) == 1;
}
public void addAll(List<Place> places) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
for (Place place : places) {
db.insertOrThrow(TABLE, null, values(place.name, place.area));
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private static ContentValues values(String name, String area) {
ContentValues values = new ContentValues();
values.put("name", required(name));
values.put("area", required(area));
return values;
}
private static String required(String value) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException("Tên và khu vực không được để trống");
}
return value.trim();
}
public static final class Place {
public final long id;
public final String name;
public final String area;
public Place(long id, String name, String area) {
this.id = id;
this.name = name;
this.area = area;
}
}
}ContentValues biểu diễn cặp cột và giá trị khi thêm hoặc sửa. Truy vấn dùng dấu ? và mảng đối số cho giá trị lọc. Nhờ đó tên khu vực có dấu nháy đơn vẫn được truyền như dữ liệu. try với tài nguyên đóng Cursor ngay cả khi đọc một bản ghi gặp lỗi. Danh sách trả về có sẵn dữ liệu để giao diện hiển thị.
3. Ghi một lô bản ghi bằng giao dịch
Giả sử người dùng nhập danh sách hai địa điểm từ tệp. Hàng thứ nhất hợp lệ còn hàng thứ hai bỏ trống tên. Nếu mỗi dòng được ghi độc lập, người dùng có thể nhìn thấy một nửa danh sách dù thao tác nhập thất bại. addAll gom các lần ghi vào một giao dịch.
Hàm gọi setTransactionSuccessful sau vòng lặp. Nếu bất kỳ lần thêm nào ném lỗi, mã chạy đến finally mà giao dịch chưa được đánh dấu thành công, nên endTransaction hoàn tác các lần ghi trong giao dịch đó. Đặt endTransaction trong finally để giao dịch được đóng cả ở nhánh thành công lẫn nhánh lỗi.
4. Chạy thử trên thiết bị hoặc trình giả lập
Tạo một đối tượng PlaceStore rồi thực hiện chuỗi gọi dưới đây trong luồng nền của ứng dụng. Khi hiển thị kết quả trong Activity, chuyển cập nhật giao diện về luồng chính. Trên ứng dụng có màn hình thực, giao việc quản lý vòng đời của kho cho thành phần phù hợp với dự án.
long id = store.add("Hồ Hoàn Kiếm", "Hà Nội");
List<PlaceStore.Place> places = store.findByArea("Hà Nội");
boolean saved = store.update(id, "Hồ Gươm", "Hà Nội");
boolean deleted = store.remove(id);
- Kiểm tra danh sách sau khi thêm có chứa ID vừa trả về. Sau khi sửa, đọc lại và đối chiếu tên mới trên cùng ID.
- Thử khu vực
Phố O'Connellvà tìm theo đúng chuỗi. Sau đó xóa hai lần theo cùng ID. Lần đầu thành công, lần hai báo không có dòng nào được xóa. - Gọi
addAllvới một dòng hợp lệ và một dòng trống tên. So sánh số bản ghi trước và sau khi lỗi để xác nhận giao dịch đã hoàn tác.
Đọc thêm tại Android Developers · SQLiteDatabase.