Tự động lưu thời gian (timestamp) trong SQLite
SQLite hỗ trợ DEFAULT CURRENT_TIMESTAMP khi tạo bảng, tự động ghi thời điểm insert/update mà không cần code Java xử lý.
Bước 1: Tạo dự án
Android Studio → File → New Project.
Bước 2: Giao diện (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:padding="16dp">
<EditText android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Nhập tên" />
<EditText android:id="@+id/salary" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Nhập lương" android:inputType="numberDecimal" />
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="8dp">
<Button android:id="@+id/save" android:text="Lưu" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<Button android:id="@+id/refresh" android:text="Làm mới" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<Button android:id="@+id/update" android:text="Cập nhật" android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>
<ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" />
</LinearLayout>
Bước 3: MainActivity.java
package com.example.sqlitetimestamp;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
Button btnSave, btnRefresh, btnUpdate;
EditText edtName, edtSalary;
ListView listView;
DatabaseHelper dbHelper;
ArrayList dataList;
ArrayAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new DatabaseHelper(this);
dataList = new ArrayList<>(dbHelper.getAllContacts());
edtName = findViewById(R.id.name);
edtSalary = findViewById(R.id.salary);
listView = findViewById(R.id.listView);
btnSave = findViewById(R.id.save);
btnRefresh = findViewById(R.id.refresh);
btnUpdate = findViewById(R.id.update);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList);
listView.setAdapter(adapter);
btnRefresh.setOnClickListener(v -> refreshList());
btnSave.setOnClickListener(v -> {
String name = edtName.getText().toString().trim();
String salary = edtSalary.getText().toString().trim();
if (name.isEmpty() || salary.isEmpty()) {
showError(name, salary);
return;
}
if (dbHelper.insert(name, salary)) {
Toast.makeText(this, "Đã thêm với timestamp tự động", Toast.LENGTH_SHORT).show();
refreshList();
} else {
Toast.makeText(this, "Thêm thất bại", Toast.LENGTH_SHORT).show();
}
});
btnUpdate.setOnClickListener(v -> {
String name = edtName.getText().toString().trim();
String salary = edtSalary.getText().toString().trim();
if (name.isEmpty() || salary.isEmpty()) {
showError(name, salary);
return;
}
if (dbHelper.update(name, salary)) {
Toast.makeText(this, "Đã cập nhật", Toast.LENGTH_SHORT).show();
refreshList();
} else {
Toast.makeText(this, "Cập nhật thất bại", Toast.LENGTH_SHORT).show();
}
});
}
private void refreshList() {
dataList.clear();
dataList.addAll(dbHelper.getAllContacts());
adapter.notifyDataSetChanged();
}
private void showError(String name, String salary) {
if (name.isEmpty()) edtName.setError("Nhập tên");
if (salary.isEmpty()) edtSalary.setError("Nhập lương");
}
}
Bước 4: DatabaseHelper.java - Quan trọng: DEFAULT CURRENT_TIMESTAMP
package com.example.sqlitetimestamp;
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;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "salaryDatabase";
private static final String TABLE_NAME = "SalaryDetails";
private static final int DB_VERSION = 1;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// CỘT datetime TỰ ĐỘNG GÁN CURRENT_TIMESTAMP
db.execSQL("CREATE TABLE " + TABLE_NAME +
"(id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT, " +
"salary TEXT, " +
"datetime TEXT DEFAULT CURRENT_TIMESTAMP)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldV, int newV) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public boolean insert(String name, String salary) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("name", name);
cv.put("salary", salary);
// KHÔNG CẦN put datetime - SQLite tự điền CURRENT_TIMESTAMP
return db.insert(TABLE_NAME, null, cv) != -1;
}
public ArrayList getAllContacts() {
ArrayList list = new ArrayList<>();
SQLiteDatabase db = getReadableDatabase();
// Lấy cột datetime thay vì name
Cursor cursor = db.rawQuery("SELECT datetime FROM " + TABLE_NAME, null);
if (cursor.moveToFirst()) {
do {
list.add(cursor.getString(cursor.getColumnIndex("datetime")));
} while (cursor.moveToNext());
}
cursor.close();
return list;
}
public boolean update(String name, String salary) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("name", name);
cv.put("salary", salary);
// datetime KHÔNG tự cập nhật khi UPDATE trừ khi trigger
return db.update(TABLE_NAME, cv, "name = ?", new String[]{name}) > 0;
}
public boolean deleteAll() {
return getWritableDatabase().delete(TABLE_NAME, null, null) > 0;
}
}
Giải thích DEFAULT CURRENT_TIMESTAMP
-- Tạo bảng với timestamp mặc định
CREATE TABLE events (
id INTEGER PRIMARY KEY,
name TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
-- Khi INSERT không chỉ định created_at:
INSERT INTO events (name) VALUES ('Test');
-- created_at tự động = '2024-01-15 10:30:45' (UTC)
-- Format timestamp SQLite: YYYY-MM-DD HH:MM:SS (UTC)
Lấy timestamp hiện tại trong Java (nếu cần)
// Cách 1: System.currentTimeMillis()
long now = System.currentTimeMillis();
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(now);
// Cách 2: Java 8+ (API 26+)
Instant.now().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// Cách 3: SQLite function (trong query)
Cursor c = db.rawQuery("SELECT datetime('now')", null); -- Local time
Cursor c = db.rawQuery("SELECT datetime('now', 'localtime')", null);
Cập nhật timestamp khi UPDATE (cần Trigger)
// Tạo trigger tự động cập nhật updated_at
db.execSQL("CREATE TRIGGER update_timestamp " +
"AFTER UPDATE ON " + TABLE_NAME + " " +
"BEGIN " +
" UPDATE " + TABLE_NAME + " SET datetime = CURRENT_TIMESTAMP WHERE id = NEW.id; " +
"END;");
Định dạng hiển thị đẹp hơn
// Trong adapter, parse và format lại
String raw = cursor.getString(cursor.getColumnIndex("datetime"));
// raw = "2024-01-15 10:30:45"
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
Date date = sdf.parse(raw);
SimpleDateFormat display = new SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault());
list.add(display.format(date));
} catch (ParseException e) {
list.add(raw); // Fallback
}