Room Database di Android

room database android

Room Database adalah bagian dari Jetpack yang mempermudah pengelolaan database SQLite di aplikasi Android. Room menyediakan API untuk menangani database dengan cara yang aman, efisien, dan modern. Artikel ini akan membahas tentang Room Database secara mendalam, lengkap dengan contoh implementasi dalam bahasa Java.


Apa itu Room Database?

Room adalah library ORM (Object-Relational Mapping) dari Jetpack Android yang memetakan tabel di database ke objek Java. Library ini memungkinkan untuk:

  • Mengelola database SQLite tanpa menulis banyak boilerplate code.
  • Menghindari kesalahan umum, seperti lupa menutup cursor.
  • Mendukung migrasi database yang mudah.
  • Menggunakan LiveData atau Flow untuk mendengarkan perubahan data secara real-time.

Komponen Utama Room

Room Database memiliki tiga komponen utama, yaitu:

  1. Entity: Representasi tabel dalam database.
  2. DAO (Data Access Object): Antarmuka untuk melakukan operasi database.
  3. Database: Titik akses utama untuk database Room.

Langkah-Langkah Menggunakan Room Database

1. Tambahkan Dependensi Room ke dalam Proyek

Tambahkan dependensi Room ke file build.gradle.

dependencies {
    implementation "androidx.room:room-runtime:2.5.0"
    annotationProcessor "androidx.room:room-compiler:2.5.0"
    // Untuk mendukung RxJava atau LiveData
    implementation "androidx.room:room-rxjava2:2.5.0"
    implementation "androidx.room:room-livedata:2.5.0"
}

androidx.room:room-runtime:2.5.0
Dependency ini adalah inti dari Room Database. Ia menyediakan API utama untuk berinteraksi dengan database SQLite, seperti membuat entitas, DAO, dan database. Library ini menangani semua logika yang diperlukan untuk membaca dan menulis data ke database secara efisien dan aman.

androidx.room:room-compiler:2.5.0
Dependency ini diperlukan untuk proses kompilasi. Ia digunakan oleh anotasi seperti @Entity, @Dao, dan @Database untuk menghasilkan kode boilerplate secara otomatis. Dependency ini memastikan bahwa struktur database dan metode DAO yang Anda definisikan benar-benar sesuai dengan spesifikasi Room.

androidx.room:room-rxjava2:2.5.0
Library ini memungkinkan integrasi dengan RxJava 2, yang digunakan untuk pemrograman reaktif. Dengan dependency ini, Anda dapat menjalankan operasi database seperti insert atau query dalam alur RxJava, seperti Flowable, Single, atau Maybe, yang cocok untuk aplikasi dengan logika berbasis reaktivitas.

androidx.room:room-livedata:2.5.0
Dependency ini memungkinkan Room untuk mendukung LiveData, yaitu komponen Android Jetpack yang memungkinkan pengamatan data secara reaktif. Dengan menggunakan LiveData bersama Room, perubahan data di database secara otomatis diperbarui di UI tanpa Anda harus mengelola lifecycle komponen secara manual.


2. Buat Entity

Entity adalah kelas yang memetakan tabel dalam database. Gunakan anotasi @Entity untuk mendeklarasikan tabel.

import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
private int id;

private String name;

private String email;

// Getters and Setters
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}
}

@Entity(tableName = "users"):

  • Memberitahu Room bahwa kelas User akan dipetakan ke tabel database bernama users.
  • Jika tableName tidak diberikan, secara default nama tabel akan sama dengan nama kelas (User).

@PrimaryKey(autoGenerate = true):

  • Menentukan bahwa id adalah kolom kunci utama untuk tabel users.
  • Atribut autoGenerate = true memberitahu Room untuk secara otomatis menghasilkan nilai unik untuk id saat data baru disisipkan.

private String name;:

  • Kolom ini akan menyimpan nama pengguna dalam bentuk string.

private String email;:

  • Kolom ini akan menyimpan email pengguna dalam bentuk string.

Catatan: Kolom-kolom ini secara otomatis akan diubah menjadi kolom tabel dalam database, dengan nama yang sama seperti atribut kelas, kecuali jika Anda menentukan anotasi tambahan seperti @ColumnInfo.

Getters dan Setters adalah metode akses untuk membaca dan menulis data ke atribut privat dalam kelas.

Dalam konteks Room:

  • Setter digunakan saat Room menyisipkan data ke dalam tabel. Contohnya, Room akan memanggil setId(int id) untuk mengisi nilai id yang baru dibuat secara otomatis.
  • Getter digunakan saat Room membaca data dari tabel. Contohnya, Room akan memanggil getName() saat Anda mengakses data nama pengguna.

Fungsi Kelas User dalam Room Database

Kelas ini berfungsi sebagai representasi dari tabel users dalam database Room. Misalnya, jika tabel users di database memiliki data seperti ini:

idnameemail
1John Doe[email protected]
2Jane Smith[email protected]

Setiap baris di tabel di atas akan dipetakan ke dalam instance User dalam aplikasi Anda. Sebagai contoh:

Baris pertama (id = 1, name = "John Doe", email = "[email protected]") akan menjadi:

User user = new User(); 
user.setId(1);
user.setName("John Doe");
user.setEmail("[email protected]");

3. Buat DAO (Data Access Object)

DAO adalah antarmuka untuk operasi database, seperti insert, update, delete, dan query.

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import java.util.List;

@Dao
public interface UserDao {

@Insert
void insert(User user);

@Update
void update(User user);

@Delete
void delete(User user);

@Query("SELECT * FROM users")
List<User> getAllUsers();

@Query("SELECT * FROM users WHERE id = :userId")
User getUserById(int userId);
}

@Dao:

  • Menandai interface UserDao sebagai DAO.
  • Room akan menghasilkan implementasi kode dari metode-metode dalam interface ini pada waktu kompilasi.
  • Interface ini adalah tempat di mana semua operasi database (CRUD – Create, Read, Update, Delete) didefinisikan.

@Insert:

  • Memberitahu Room bahwa metode ini digunakan untuk menyisipkan data ke dalam tabel.
  • Parameter User user mewakili satu instance dari entitas User yang akan dimasukkan ke tabel.
  • Room akan secara otomatis menangani pembuatan query SQL seperti:
INSERT INTO users (id, name, email) VALUES (?, ?, ?);

Catatan:

  • Jika Anda ingin menyisipkan banyak data sekaligus, gunakan parameter berupa array atau list (List<User>).

@Update:

  • Memberitahu Room bahwa metode ini digunakan untuk memperbarui data yang sudah ada dalam tabel.
  • Parameter User user adalah data yang sudah dimodifikasi dan akan diperbarui di tabel.
  • Room akan secara otomatis membuat query seperti:
UPDATE users SET name = ?, email = ? WHERE id = ?;

Catatan:

  • Objek yang diperbarui harus memiliki primary key (id) yang sesuai untuk menentukan baris yang akan diubah.

@Delete:

  • Memberitahu Room bahwa metode ini digunakan untuk menghapus baris data dalam tabel.
  • Room akan membuat query seperti:
DELETE FROM users WHERE id = ?;

Catatan:

  • Primary key (id) dari entitas User digunakan untuk menentukan baris yang akan dihapus.
@Query("SELECT * FROM users")
List<User> getAllUsers();
  • @Query:
    • Digunakan untuk menjalankan query SQL secara spesifik.
    • Dalam kasus ini, query SELECT * FROM users akan mengambil semua data dari tabel users.
  • Return Type:
    • List<User>: Mengembalikan daftar semua pengguna yang ditemukan dalam tabel.
@Query("SELECT * FROM users WHERE id = :userId")
User getUserById(int userId);
  • Parameter Query:
    • Tanda :userId adalah cara Room menerima parameter dinamis dalam query.
    • Nilai dari parameter userId akan diisi saat metode dipanggil, dan digunakan dalam query SQL.
  • Return Type:
    • User: Mengembalikan satu baris data dari tabel users berdasarkan ID yang diberikan.

4. Buat Database

Database adalah kelas abstrak yang memperluas RoomDatabase dan berisi referensi DAO.

import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import android.content.Context;

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase instance;

public abstract UserDao userDao();

public static synchronized AppDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "app_database")
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
}

Definisi Database

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
...
}

Penjelasan:

  • @Database:
    • Anotasi ini memberitahu Room bahwa AppDatabase adalah kelas database.
    • Atribut Penting:
      • entities: Menentukan kelas entitas yang akan digunakan Room untuk membuat tabel dalam database.
        • Dalam contoh ini, User.class adalah entitas yang akan dipetakan ke tabel users.
      • version: Versi database.
        • Room menggunakan nomor versi ini untuk melacak perubahan struktur database. Jika struktur diubah (misalnya, menambahkan kolom baru), Anda harus meningkatkan versi ini.
  • AppDatabase extends RoomDatabase:
    • AppDatabase mewarisi dari RoomDatabase, yang menyediakan semua fungsi dasar untuk database Room.

Singleton Instance

private static AppDatabase instance;

Penjelasan:

  • Singleton:
    • AppDatabase menggunakan pola singleton untuk memastikan hanya ada satu instance dari database yang digunakan di seluruh aplikasi.
    • Ini penting karena membuka banyak koneksi database dapat menyebabkan masalah kinerja atau konflik data.

Deklarasi DAO

public abstract UserDao userDao();

Penjelasan:

  • public abstract UserDao userDao();:
    • Metode abstrak ini digunakan untuk mendapatkan instance dari DAO (UserDao).
    • Room akan secara otomatis menghasilkan implementasi dari metode ini pada waktu kompilasi.
    • Anda akan menggunakan userDao() untuk mengakses tabel users melalui metode DAO.

Metode getInstance

public static synchronized AppDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "app_database")
.fallbackToDestructiveMigration()
.build();
}
return instance;
}

Penjelasan:

  • getInstance:
    • Metode ini digunakan untuk mendapatkan instance dari AppDatabase.
    • Jika instance belum dibuat (instance == null), maka Room akan membuatnya.

Poin-poin Penting dalam getInstance:

  1. synchronized:
    • Digunakan untuk mencegah beberapa thread membuat instance database secara bersamaan.
    • Menjamin bahwa hanya satu thread yang bisa mengakses metode ini pada suatu waktu.
  2. Room.databaseBuilder:
    • Membuat instance database Room.
    • Parameter:
      • context.getApplicationContext():
        • Menggunakan ApplicationContext untuk memastikan database tetap ada selama aplikasi berjalan.
      • AppDatabase.class:
        • Menentukan kelas database (kelas ini).
      • "app_database":
        • Nama file database yang akan dibuat di penyimpanan perangkat.
  3. fallbackToDestructiveMigration():
    • Menangani situasi di mana versi database diperbarui tetapi Anda tidak menyediakan strategi migrasi.
    • Jika ini terjadi, Room akan menghapus database lama dan membuat database baru.
    • Peringatan: Semua data akan hilang jika metode ini digunakan.
  4. build():
    • Metode terakhir yang dipanggil untuk membuat instance database.


5. Gunakan Database di Activity atau ViewModel

Gunakan DAO untuk melakukan operasi database di UI.

import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.List;
import java.util.concurrent.Executors;

public class MainActivity extends AppCompatActivity {

private AppDatabase database;

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

database = AppDatabase.getInstance(this);

// Insert data
Executors.newSingleThreadExecutor().execute(() -> {
User user = new User();
user.setName("John Doe");
user.setEmail("[email protected]");
database.userDao().insert(user);

// Fetch all users
List<User> users = database.userDao().getAllUsers();
runOnUiThread(() ->
Toast.makeText(this, "Users count: " + users.size(), Toast.LENGTH_SHORT).show()
);
});
}
}

Catatan Penting

  1. Operasi di Luar Thread Utama:
    • Semua operasi database harus dilakukan di luar thread utama untuk mencegah Application Not Responding (ANR).
  2. Mengelola Data Asynchronous:
    • Dalam aplikasi nyata, disarankan menggunakan LiveData atau Flow untuk mendapatkan hasil pembaruan data secara otomatis.
  3. Migrasi Database:
    • Ketika struktur database berubah (misalnya, menambahkan kolom baru), versi database harus ditingkatkan, dan strategi migrasi perlu diterapkan.

Tips dan Best Practices

  1. Gunakan Executor atau Coroutine: Hindari menjalankan operasi database di thread utama.
  2. Migrasi Database: Gunakan metode addMigrations() untuk mengelola perubahan struktur tabel.
  3. LiveData: Gunakan LiveData untuk mendapatkan data secara real-time.
  4. Uji Coba: Gunakan library seperti Robolectric untuk menguji database Anda.

Kesimpulan

Room Database adalah solusi modern untuk mengelola SQLite di Android. Dengan Room, Anda dapat menghindari banyak boilerplate code dan memastikan aplikasi Anda tetap efisien dan bebas dari bug. Contoh di atas menunjukkan cara mengimplementasikan Room Database dari awal hingga integrasi ke UI. Pastikan Anda mengikuti best practices agar aplikasi Anda lebih optimal.