Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active November 21, 2025 14:02
Show Gist options
  • Select an option

  • Save sunmeat/b3cb8e635f1050e28d70186ca389d54e to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/b3cb8e635f1050e28d70186ca389d54e to your computer and use it in GitHub Desktop.
room database example android
settings.gradle.kts:
pluginManagement {
repositories {
gradlePluginPortal() // обов’язково поставити зверху! тут лежить kotlin("android")
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "StudentRoom"
include(":app")
============================================================================================================
build.gradle.kts:
plugins {
alias(libs.plugins.android.application)
kotlin("android") version "2.1.20" // !!! https://kotlinlang.org/docs/whatsnew2120.html
kotlin("kapt") version "2.1.20" // !!! плагін Gradle, який потрібен, щоб обробляти анотації в Kotlin-коді
}
android {
namespace = "site.sunmeat.helloworld"
compileSdk = 36
defaultConfig {
applicationId = "site.sunmeat.helloworld"
minSdk = 33
targetSdk = 36
versionCode = 1
versionName = "1.0"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17" // !!!
}
buildFeatures {
viewBinding = true // !!!
}
}
dependencies {
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.activity:activity:1.9.3")
implementation("androidx.constraintlayout:constraintlayout:2.2.1") // https://developer.android.com/jetpack/androidx/releases/constraintlayout
implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.4.0") // https://developer.android.com/jetpack/androidx/releases/recyclerview
implementation("com.google.android.material:material:1.12.0")
// Room
val roomVersion = "2.8.3" // https://developer.android.com/jetpack/androidx/releases/room
implementation("androidx.room:room-runtime:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
// Lifecycle
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4") // https://developer.android.com/jetpack/androidx/releases/lifecycle
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.9.4")
}
============================================================================================================
MainActivity.java:
package site.sunmeat.helloworld;
import android.os.Bundle;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.*;
import androidx.annotation.NonNull;
import androidx.lifecycle.*;
import androidx.room.*;
import java.util.*;
// ======================== ROOM КЛАСИ ========================
@Entity(tableName = "students")
class Student {
@PrimaryKey(autoGenerate = true)
public long _id; // для майбутнього контент резолвера з його курсорами бажано поставити саме тип лонг + _ для поля айді
public String firstName;
public String lastName;
public int age;
public Student() {}
public Student(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
@NonNull
@Override
public String toString() {
return firstName + " " + lastName + ", вік: " + age;
}
}
@Dao // DAO (Data Access Object) - це інтерфейс, який відповідає за всі операції з базою даних
// для таблиці "students". Room автоматично згенерує реалізацію цього інтерфейсу під час компіляції
interface StudentDao {
@Insert void insert(Student student);
@Query("SELECT * FROM students ORDER BY lastName") LiveData<List<Student>> getAllStudents(); // клас з Android Architecture Components,
// який дозволяє спостерігати за даними і автоматично оновлювати UI, коли вони змінюються
@Query("DELETE FROM students") void deleteAll();
}
@Database(entities = {Student.class}, version = 1, exportSchema = false)
abstract class AppDatabase extends RoomDatabase {
abstract StudentDao studentDao();
private static volatile AppDatabase INSTANCE;
static AppDatabase getDatabase(final android.content.Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "student_database").build();
// Room працює саме з SQLite - це не окрема БД, а зручна надбудова (обгортка) над класичною Android-базою SQLite
}
}
}
return INSTANCE;
}
}
class StudentRepository {
private final StudentDao dao;
private final LiveData<List<Student>> allStudents;
StudentRepository(android.app.Application app) {
AppDatabase db = AppDatabase.getDatabase(app);
dao = db.studentDao();
allStudents = dao.getAllStudents();
}
LiveData<List<Student>> getAllStudents() { return allStudents; }
void insert(Student s) { new Thread(() -> dao.insert(s)).start(); }
void deleteAll() { new Thread(dao::deleteAll).start(); }
}
class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.VH> {
private List<Student> data = new ArrayList<>();
void setStudents(List<Student> list) {
data = list;
notifyDataSetChanged();
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull android.view.ViewGroup parent, int viewType) {
android.view.View v = android.view.LayoutInflater.from(parent.getContext())
.inflate(android.R.layout.simple_list_item_1, parent, false);
return new VH(v);
}
@Override
public void onBindViewHolder(@NonNull VH holder, int pos) {
holder.tv.setText(data.get(pos).toString());
}
@Override
public int getItemCount() { return data.size(); }
static class VH extends RecyclerView.ViewHolder {
android.widget.TextView tv;
VH(android.view.View v) { super(v); tv = v.findViewById(android.R.id.text1); }
}
}
public class MainActivity extends AppCompatActivity {
// в ідеалі, звісно, всі ці класи треба рознести по окремим файлам
public static class StudentViewModel extends AndroidViewModel {
private final StudentRepository repo;
private final LiveData<List<Student>> allStudents;
public StudentViewModel(android.app.Application app) {
super(app);
repo = new StudentRepository(app);
allStudents = repo.getAllStudents();
}
public LiveData<List<Student>> getAllStudents() { return allStudents; }
public void insert(Student s) { repo.insert(s); }
public void deleteAll() { repo.deleteAll(); }
}
private StudentViewModel viewModel;
private final StudentAdapter adapter = new StudentAdapter();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText etFirstName = findViewById(R.id.etFirstName);
EditText etLastName = findViewById(R.id.etLastName);
EditText etAge = findViewById(R.id.etAge);
Button btnAdd = findViewById(R.id.btnAdd);
Button btnClear = findViewById(R.id.btnClear);
RecyclerView rv = findViewById(R.id.recyclerView);
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(adapter);
// отримуємо ViewModel, який має доступ до Application (потрібно для Room)
// без другого параметра (фабрики) крошиться, бо AndroidViewModel вимагає Application у конструкторі
viewModel = new ViewModelProvider(
this,
ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())
).get(StudentViewModel.class);
viewModel.getAllStudents().observe(this, adapter::setStudents);
btnAdd.setOnClickListener(v -> {
String fn = etFirstName.getText().toString().trim();
String ln = etLastName.getText().toString().trim();
String ageStr = etAge.getText().toString().trim();
if (!fn.isEmpty() && !ln.isEmpty() && !ageStr.isEmpty()) {
int age = Integer.parseInt(ageStr);
viewModel.insert(new Student(fn, ln, age));
etFirstName.setText("");
etLastName.setText("");
etAge.setText("");
}
});
btnClear.setOnClickListener(v -> viewModel.deleteAll());
}
}
============================================================================================================
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F5F5"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:gravity="center"
android:text="База студентів"
android:textColor="#1976D2"
android:textSize="26sp"
android:textStyle="bold" />
<EditText
android:id="@+id/etFirstName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:background="@android:drawable/editbox_background_normal"
android:hint="Ім'я"
android:padding="12dp" />
<EditText
android:id="@+id/etLastName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:background="@android:drawable/editbox_background_normal"
android:hint="Прізвище"
android:padding="12dp" />
<EditText
android:id="@+id/etAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:background="@android:drawable/editbox_background_normal"
android:hint="Вік"
android:inputType="number"
android:padding="12dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnAdd"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:backgroundTint="#4CAF50"
android:text="Додати" />
<Button
android:id="@+id/btnClear"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="#F44336"
android:text="Очистити" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp"
android:text="Список студентів"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#FFFFFF"
android:clipToPadding="false"
android:padding="8dp"
android:scrollbars="vertical" />
</LinearLayout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment