[Android] Database trong Android – P4 Update database

Mục đích chính của loạt bài hướng dẫn Database trong Android chính là bài hôm nay – Update database làm sao để ứng dụng không bị mất, không bị lỗi khi bạn muốn thay đổi database.

Giải sử sau khi thực hiện xong app Ghi chú, bạn lại muốn các ghi chú của mình có thời gian chỉnh sửa lần cuối. Vậy chúng ta lần lượt đi thực hiện thôi.

update database android

Bước 1: Thay đổi cấu trúc Class Note

Mỗi note cần có thêm thời gian cuối cùng sửa đổi, vì vậy chúng ta cần thêm thuộc tính lastModified vào class Note.

package cachhoc.net.tut.demodatabase;

public class Note {
    private long id;
    private String title;
    private String content;
    private String lastModified;

    /**
    * here is getter and setter of id, title, content.
    */

    public String getLastModified() {
        return lastModified;
    }

    public Note setLastModified(String lastModified) {
        this.lastModified = lastModified;
        return this;
    }
}

Chúng ta chỉ cần hiển thị thời gian sửa lần cuối chứ cũng không cần tính toán gì với nó nên dùng kiểu String mà không cần dùng kiểu date (kiểu date sẽ khó xử lý hơn đó).

Bước 2: Cập nhật DatabaseHelper

Đây là bước quan trọng nhất. chúng ta lần lượt thay đổi từng phần trong DatabaseHelper như sau:

Thay đổi lệnh tạo bảng

Trước tiên chúng ta cần thêm một hằng số là tiêu đề của cột mới. Sau đó thêm nó vào lệnh tạo bảng note

/**
 * table note contain id, title, content
 */
public static final String TABLE_NOTE = "tb_note";
public static final String KEY_ID_NOTE = "id";
public static final String KEY_TITLE_NOTE = "title";
public static final String KEY_CONTENT_NOTE = "content";
public static final String KEY_LAST_MODIFIED_NOTE = "last_modified";

/**
 * string for create table note
 */
public static final String CREATE_TABLE_NOTE =
        "CREATE TABLE " + TABLE_NOTE + "(" +
                KEY_ID_NOTE + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" +
                ", " + KEY_TITLE_NOTE + " TEXT NOT NULL" +
                "," + KEY_CONTENT_NOTE + " TEXT NOT NULL" +
                "," + KEY_LAST_MODIFIED_NOTE + " TEXT DEFAULT \'\'" +
                ")";

Điều này đảm bảo các điện thoại cài mới cũng sẽ có cột last_modified trong bảng note.

Cập nhật database version

Các bạn nhớ lại hôm trước chúng ta có 1 biến là DATA_VERSION, nó chính là phiên bản database. Giờ bạn cầng tăng nó lên lớn hơn phiên bản trước (lần trước là 1, giờ cần tăng lên thành 2).

/**
 * value for update database
 */
public static final int DATA_VERSION = 2;

Thực hiện update database

Như hôm trước mình có nhắc phương thức onUpgrade sẽ được gọi khi chúng ta cập nhật database version. Chỉ cần DATA_VERSION thay đổi thì phương thức này sẽ được gọi. ta viết lại phương thức này như sau:

/**
 * call when change DATA_VERSION
 * help we update database
 */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // update database for database version < 2
    if (oldVersion < 2) {
        db.execSQL("ALTER TABLE " + TABLE_NOTE + " ADD COLUMN " + KEY_LAST_MODIFIED_NOTE + " TEXT DEFAULT \'\'");
    }
}

Các bạn chú ý các điểm như sau:

  • Khi update app, hệ thống sẽ tự động lưu lại database version của phiên bản cũ vào biến oldVersion bên trên. newVersion chính là DATA_VERSION mới.

  • Một số bạn thường xóa hết các bảng và gọi lại hàm onCreate trong này, tuy nhiên như thế sẽ làm mất toàn bộ data cũ. Điều này bất đắc dĩ phải làm thôi, nếu tránh được cứ tránh.

  • Mình dùng điều kiện oldVersion < 2 để thực hiện sửa bảng. Điều này giúp các bạn sửa database theo từng giai đoạn. Ví dụ lần sau DATA_VERSION = 3 thì bạn cho thêm lệnh if(oldVersion < 3) và thực hiện. Nó giúp bạn nhớ được các giai đoạn update database và cũng giúp cho những người update app cách quãng (ví dụ từ 1.0 không lên 2.0 mà lên 3.0 ngay) không bị update sót.

  • Trường này last_modified mình để default là chuỗi rỗng, giúp các bản ghi đã có không bị null, tránh lỗi khi truy xuất.

Update các phương thức truy xuất liên quan

Trong phần này, chúng ta cần sửa lại 2 phương thức đưa note vào ContentValues và lấy note ra từ database để có thể đưa vào và lấy ra đủ thông tin.

/**
 * convert note to contentvalues
 * don't put id of note because
 * when insert id will auto create
 * when update we don't update id
 */
private ContentValues noteToValues(Note note) {
    ContentValues values = new ContentValues();
    values.put(KEY_TITLE_NOTE, note.getTitle());
    values.put(KEY_CONTENT_NOTE, note.getContent());
    values.put(KEY_LAST_MODIFIED_NOTE, note.getLastModified());
    return values;
}

/**
 * convert cursor to note
 */
private Note cursorToNote(Cursor cursor) {
    Note note = new Note();
    note.setId(cursor.getInt(cursor.getColumnIndex(KEY_ID_NOTE)))
            .setTitle(cursor.getString(cursor.getColumnIndex(KEY_TITLE_NOTE)))
            .setContent(cursor.getString(cursor.getColumnIndex(KEY_CONTENT_NOTE)))
            .setLastModified(cursor.getString(cursor.getColumnIndex(KEY_LAST_MODIFIED_NOTE)));
    return note;
}

Bước 3: Cập nhật nơi tạo ra dữ liệu mới

Nghe vẻ hơi khó hiểu nhưng nó rất đơn giản, chính là sửa lại phương thức save trong class NoteAcitivty để lưu lại thời gian cập nhật note.

/**
 * get title and content and update last modified time of note, if they empty then finish
 * if they not empty then check note is null?
 * if note is null (create new note), we will create note and insert into database
 * if note not null then we update note
 * after save we finish activity
 */
private void save() {

    String title = editTitle.getText().toString().trim();
    String content = editContent.getText().toString().trim();

    String notify = null;

    if (TextUtils.isEmpty(title) && TextUtils.isEmpty(content)) {
        notify = "note empty, don't save!";
    } else {

        // get curren time for last modified
        SimpleDateFormat formatTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar cal = Calendar.getInstance();
        String currenTime = formatTime.format(cal.getTime());

        // new note
        if (note == null) {
            Note note = new Note();
            note.setTitle(title).setContent(content).setLastModified(currenTime);
            if (db.insertNote(note) > 0) {
                notify = "add success!";
            } else {
                notify = "add fail!";
            }
        } else { // update note
            note.setTitle(title).setContent(content).setLastModified(currenTime);
            if (db.updateNote(note)) {
                notify = "update success!";
            } else {
                notify = "update fail!";
            }
        }
    }

    Toast.makeText(context, notify, Toast.LENGTH_SHORT).show();
    finish();
}

Bước 4: Cập nhật giao diện

Sau khi thực hiện chỉnh sửa trong file database, chúng ta quay lại sửa giao diện cho phù hợp.

Mỗi item bây giờ sẽ hiển thị thêm 1 trường là last_modified ở góc dưới bên trái.

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:layout_margin="3dp"
    card_view:cardCornerRadius="1dp">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/item_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/backgroud_item_note"
        android:gravity="center_vertical"
        android:orientation="vertical"
        android:padding="5dp">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:gravity="center_vertical"
            android:lines="1"
            android:text="@string/title_note"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:gravity="center_vertical"
            android:lines="1"
            android:text="@string/content"
            android:textSize="13sp" />

        <TextView
            android:id="@+id/tv_last_modified"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:gravity="end"
            android:textSize="13sp" />
    </LinearLayout>

</android.support.v7.widget.CardView>

Cuối cùng là sửa lại Adapter cho phù hợp thôi. Chúng ta chỉ cần sửa 2 chỗ là class RecyclerViewHolder nằm trong ItemNoteAdapter và phương thức onBindViewHolder

/**
 * ViewHolder for item view of list
 */

public class RecyclerViewHolder extends RecyclerView.ViewHolder implements
        OnClickListener {

    public LinearLayout container;
    public TextView tvTitle;
    public TextView tvContent;
    public TextView tvLastModified; //add

    public RecyclerViewHolder(View itemView) {
        super(itemView);

        container = (LinearLayout) itemView.findViewById(R.id.item_container);
        tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
        tvContent = (TextView) itemView.findViewById(R.id.tv_content);
        tvLastModified = (TextView) itemView.findViewById(R.id.tv_last_modified);

        container.setOnClickListener(this);
    }

    // click item then display note
    @Override
    public void onClick(View v) {
        MainActivity.showNote(context, list.get(getPosition()).getId());
    }
}
/**
 * set data for item
 */
@Override
public void onBindViewHolder(RecyclerViewHolder viewHolder, int position) {
    Note note = list.get(position);
    viewHolder.tvTitle.setText(note.getTitle());
    viewHolder.tvContent.setText(note.getContent());
    viewHolder.tvLastModified.setText(note.getLastModified());
}

Chốt lại là bài quan trọng nhất cũng đã xong. Phần tiếp theo mình sẽ hướng dẫn các bạn cách sao lưu dữ liệu sang thẻ nhớ.

Bài viết được thực hiện trong loạt bài hướng dẫn Database trong Android bởi nguyenvanquan7826