Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Cách dùng ràng buộc UNIQUE trong SQLite Android

Giới thiệu về SQLite trong Android

SQLite là một cơ sở dữ liệu SQL mã nguồn mở, lưu trữ dữ liệu vào tệp văn bản trên thiết bị. Android tích hợp sẵn SQLite, hỗ trợ đầy đủ các tính năng của cơ sở dữ liệu quan hệ. Để truy cập cơ sở dữ liệu này, bạn không cần thiết lập kết nối như JDBC hay ODBC.

Ví dụ: Sử dụng ràng buộc UNIQUE để đảm bảo dữ liệu không trùng lặp

Hướng dẫn này minh họa cách định nghĩa ràng buộc UNIQUE trên cột khi tạo bảng, ngăn chặn việc chèn dữ liệu trùng lặp.

Bước 1: Tạo dự án mới

Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.

Bước 2: Thiết kế giao diện (res/layout/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">

    <EditText
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:hint="Nhập tên"
        android:layout_height="wrap_content" />

    <EditText
        android:id="@+id/salary"
        android:layout_width="match_parent"
        android:inputType="numberDecimal"
        android:hint="Nhập lương"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/save"
        android:text="Lưu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

Bước 3: Code MainActivity.java

package com.example.andy.myapplication;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    Button save;
    EditText name, salary;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final DatabaseHelper helper = new DatabaseHelper(this);
        name = findViewById(R.id.name);
        salary = findViewById(R.id.salary);

        findViewById(R.id.save).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String nameStr = name.getText().toString().trim();
                String salaryStr = salary.getText().toString().trim();

                if (!nameStr.isEmpty() && !salaryStr.isEmpty()) {
                    if (helper.insert(nameStr, salaryStr)) {
                        Toast.makeText(MainActivity.this, "Đã thêm", Toast.LENGTH_LONG).show();
                        name.setText("");
                        salary.setText("");
                    } else {
                        Toast.makeText(MainActivity.this, "Không thêm được (có thể trùng UNIQUE)", Toast.LENGTH_LONG).show();
                    }
                } else {
                    name.setError("Nhập TÊN");
                    salary.setError("Nhập LƯƠNG");
                }
            }
        });
    }
}

Bước 4: Code DatabaseHelper.java (có UNIQUE constraint)

package com.example.andy.myapplication;

import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

class DatabaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "MyDBName";
    public static final String CONTACTS_TABLE_NAME = "SalaryDetails";

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // Quan trọng: UNIQUE trên cột id và name
        db.execSQL("create table " + CONTACTS_TABLE_NAME + 
            "(id integer primary key NOT NULL UNIQUE, " +
            " name text NOT NULL UNIQUE, " +
            " salary text NOT NULL)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + CONTACTS_TABLE_NAME);
        onCreate(db);
    }

    public boolean insert(String name, String salary) {
        SQLiteDatabase db = this.getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put("name", name);
        contentValues.put("salary", salary);
        
        try {
            long result = db.insertOrThrow(CONTACTS_TABLE_NAME, null, contentValues);
            return result != -1;
        } catch (android.database.sqlite.SQLiteConstraintException e) {
            // Vi phạm UNIQUE constraint
            e.printStackTrace();
            return false;
        }
    }
}

Chạy ứng dụng

Kết nối thiết bị Android thật với máy tính. Trong Android Studio, mở file activity và nhấn biểu tượng Run. Chọn thiết bị di động và kiểm tra màn hình hiển thị.

Cách dùng ràng buộc UNIQUE trong SQLite Android

Nhập giá trị và nhấn Lưu:

Cách dùng ràng buộc UNIQUE trong SQLite Android

Kiểm tra dữ liệu trong Database Inspector:

Cách dùng ràng buộc UNIQUE trong SQLite Android

Giải thích ràng buộc UNIQUE

  • UNIQUE: Đảm bảo tất cả giá trị trong cột (hoặc nhóm cột) là duy nhất, không trùng lặp
  • PRIMARY KEY ngầm hiểu là NOT NULL UNIQUE
  • Có thể áp dụng cho một cột hoặc nhóm cột (composite unique)
  • Vi phạm UNIQUE → ném SQLiteConstraintException (subclass của SQLException)

Các cách định nghĩa UNIQUE

-- 1. Cột đơn lẻ (inline)
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    email TEXT NOT NULL UNIQUE,
    username TEXT UNIQUE
);

-- 2. Nhóm cột (composite unique) - ở cuối bảng
CREATE TABLE enrollments (
    student_id INTEGER,
    course_id INTEGER,
    semester TEXT,
    UNIQUE(student_id, course_id, semester) -- 1 sinh viên chỉ đăng ký 1 môn/học kỳ
);

-- 3. Đặt tên constraint (dễ quản lý, drop sau này)
CREATE TABLE products (
    id INTEGER PRIMARY KEY,
    sku TEXT,
    barcode TEXT,
    CONSTRAINT uq_product_sku UNIQUE (sku),
    CONSTRAINT uq_product_barcode UNIQUE (barcode)
);

Xử lý vi phạm UNIQUE trong Android

public boolean insertUser(String email, String name) {
    SQLiteDatabase db = getWritableDatabase();
    ContentValues cv = new ContentValues();
    cv.put("email", email);
    cv.put("name", name);
    
    try {
        // insertOrThrow ném exception nếu vi phạm constraint
        long id = db.insertOrThrow("users", null, cv);
        return id != -1;
    } catch (SQLiteConstraintException e) {
        // Xử lý trùng lặp: log, trả về false, hoặc update thay vì insert
        Log.w("DB", "Email đã tồn tại: " + email);
        return false;
    }
}

// Hoặc dùng INSERT OR IGNORE / INSERT OR REPLACE
// INSERT OR IGNORE: bỏ qua nếu trùng, không báo lỗi
// INSERT OR REPLACE: xóa bản ghi cũ, chèn mới (cần PRIMARY KEY hoặc UNIQUE)
db.execSQL("INSERT OR IGNORE INTO users (email, name) VALUES (?, ?)", new Object[]{email, name});

UNIQUE vs INDEX

Đặc điểmUNIQUEINDEX (CREATE INDEX)
Đảm bảo duy nhấtCóKhông
Tăng tốc truy vấnCó (tự tạo index)Có
Cho phép NULLCó (nhiều NULL được phép)*Có
Dùng choEmail, username, SKU...Cột thường WHERE/JOIN

* SQLite cho phép nhiều NULL trong cột UNIQUE (theo chuẩn SQL). Muốn chỉ 1 NULL: thêm NOT NULL.

Best Practice

  • Luôn bắt exception SQLiteConstraintException khi insert/update có UNIQUE
  • Dùng INSERT OR REPLACE cho upsert (update nếu tồn tại, insert nếu không)
  • Đặt tên constraint rõ ràng: CONSTRAINT uq_table_column UNIQUE (column)
  • Kết hợp NOT NULL UNIQUE cho các trường bắt buộc duy nhất (email, phone)