관리 메뉴

막내의 막무가내 프로그래밍 & 일상

[안드로이드] Room 데이터베이스 본문

안드로이드/자바 & Previous

[안드로이드] Room 데이터베이스

막무가내막내 2019. 8. 25. 10:10
728x90

 

이전에 SqliteDatabse와 Relam은 간략하게라도 써봤었는데 Room은 써본적이 없어 이번에 공부도할겸 간략하게 써보고 정리를 해봤다.

 

https://www.youtube.com/watch?v=LRMfm458E3k   

먼저 오준석 강사님의 강의를 수강하고 여러 블로그를 참조했다.

 

https://codelabs.developers.google.com/codelabs/android-room-with-a-view/#8   

그리고 이 사이트대로 코드를 레퍼지토리로 짜볼려고 했으나 짜다가 중간에 좀 복잡해져서 나중에 실력이 향상되면 해보기로한다.. MVVM패턴을 사용하는 것 같은데 내가 아직 MVVM패턴을 모르고 저 사이트대로 하는건 배보다 배꼽이 더커지는 것 같다 생각했다. 나중에 MVVM 레퍼지토리 패턴(?)을 공부한 후에 해봐야겠다.

 

먼저 Room 라이브러리에 대한 설명은 다음과 같다.

 라이브러리는 SQLite의 모든 기능을 활용하면서보다 강력한 데이터베이스에 액세스 할 수 있도록 SQLite를 통해 추상화 계층을 제공합니다. (출처 : https://developer.android.com/topic/libraries/architecture/room)

 

Room도 Relam과 마찬가지로 ORM(Object Relational Mapping) 라이브러리이다고 안드로이드 Jetpack 아키텍처에 속해있다. Room은 Sqlite에 비해 많은 편리함과 여러가지 기능들을 제공한다. 그에대한 것은 다음 블로그나 다른 블로그에 많이써져있으므로 참고하도록하자.

https://namget.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-ROOM-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%BD%94%EB%A3%A8%ED%8B%B4

 

 

난 레포지토리클래스를 사용하지않고 AsyncTask를 액티비티에서 구현하는식으로 구현해봤다.

들어가기 앞서 Room은 @Entity, @Dao, @Database 로 이루어진다.

이제 코드를 통해 살펴보도록하자 (주석참고)

 

 

1. 먼저 Todo라는 모델클래스를 생성한다. (프라이머리 키를 설정할 수 있고 테이블이름이나 컬럼명도 변경이 가능하다. 만약 테이블이름이나 컬럼명을 따로 설정하지않으면 원래의 클래스이름과 변수명으로 매칭된다. 이 밖에 여러가지 기능이 있으나 필요해질때 구글링해서 찾아보도록 하자.

package com.example.myroom;

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

//@Entity(primaryKeys = {"firstName", "lastName"}) =>
//@Entity(tableName = "Todo")

@Entity
public class Todo {
    @PrimaryKey(autoGenerate = true) //autoGenerate는 알아서 id를 1씩 증가시켜준다. autoincrement와 똑같
    private int id;
    //@ColumnInfo(name = "first_name") ==>컬럼명 변수명과 다르게 사용 가능
    private String title;

    public Todo(String title) {
        this.title = title;
    }

    public int getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "\n id=> " + this.id + " , title=> " + this.title;
    }
}

 

 

 

2. TodoDao 인터페이스를 만들어서 쿼리문을 작성하도록한다. 어노테이션을 보면 대충 짐작갈거라고 본다. (쿼리문이 작성되지않고 그냥 @Delete 이렇게 되있는거같은 경우는 room에서 알아서 매개변수로 받은 객체를 찾아 제거해준다. 똑똑해라...) LiveData<>로 감싸면 주석에 써있듯이 디비에 변화가 생기면 해당 변화를 감지하고 백그라운드로 작업을 처리할 수 있게해준다. 메인액티비티에서 옵저버와 함께 사용된다.

참고로 디비 접근 즉 CRUD 작업은 모두 백그라운드로 실행되어야한다. 안그럼 에러난다.

package com.example.myroom;

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

import java.util.List;

//모든 디비 CRUD작업은 메인스레드가아닌 백그라운드로 작업해야한다. (단, 라이브데이터는 반응시 자기가 알아서 백그라운드로 작업을 처리해준다. 굳)
@Dao
public interface TodoDao {
    @Query("SELECT * FROM Todo")
    LiveData<List<Todo>> getAll(); //LiveData => Todo테이블에 있는 모든 객체를 계속 관찰하고있다가 변경이 일어나면 그것을 자동으로 업데이트하도록한다.
                                    //getAll() 은 관찰 가능한 객체가 된다.(즉 디비변경시 반응하는)
    @Insert
    void insert(Todo todo);

    @Update
    void update(Todo todo);

    @Delete
    void delete(Todo todo);

    @Query("DELETE FROM Todo")
    void deleteAll();

}

 

 

 

 

3. AppDatabase 혹은 TodoDatabase라는 이름으로(자유) 데이터베이스 생성클래스를 만든다. 데이터베이스 객체는 생성하는 비용이 크므로 싱글톤으로 해야한다. Dao클래스를 포함하고있다.

package com.example.myroom;

import android.content.Context;
import android.os.AsyncTask;

import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;

@Database(entities = {Todo.class}, version =  1)
public abstract class TodoDatabase extends RoomDatabase {
    //데이터베이스를 매번 생성하는건 리소스를 많이사용하므로 싱글톤이 권장된다고한다.
    private static TodoDatabase INSTANCE;

    public abstract TodoDao todoDao();


    //디비객체생성 가져오기
    public static TodoDatabase getAppDatabase(Context context){
        if(INSTANCE == null){
            INSTANCE = Room.databaseBuilder(context, TodoDatabase.class , "todo-db")
                    .build();

            /*INSTANCE = Room.databaseBuilder(context, TodoDatabase.class , "todo-db")
                    .allowMainThreadQueries() =>이걸 추가해서 AsyncTask를 사용안하고 간편하게할수있지만 오류가많아 실제 앱을 만들때 사용하면 안된다고한다.
                    .build();*/
        }
        return  INSTANCE;
    }

    //디비객체제거
    public static void destroyInstance() {
        INSTANCE = null;
    }
}

 

 

 

 

4. 마지막으로 메인액티비티에서 갖다가 쓰면된다.(주석참고) LiveData였던 getAll()과 옵저버를 통해 디비변화를 감지하고 그 때 할 일을 구현할 수 있다.

package com.example.myroom;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;

import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private EditText mTodoEditText;
    private TextView mResultTextView;


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

        mTodoEditText = findViewById(R.id.et_todo);
        mResultTextView =  findViewById(R.id.tv_content);
        //디비생성
        TodoDatabase db = TodoDatabase.getAppDatabase(this);

        //UI 갱신 (라이브데이터 Observer 이용, 해당 디비값이 변화가생기면 실행됨)
        db.todoDao().getAll().observe(this, new Observer<List<Todo>>() {
            @Override
            public void onChanged(List<Todo> todos) {
                mResultTextView.setText(todos.toString());
            }
        });

        //DB 데이터 불러오기 (SELECT)
        mResultTextView.setText(db.todoDao().getAll().toString());

        //추가버튼시 DB에 데이터 INSERT
        findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mTodoEditText.getText().toString().trim().length() <= 0) {
                    Toast.makeText(MainActivity.this, "한글자 이상입력해주세요.", Toast.LENGTH_SHORT).show();
                }else{
                    new InsertAsyncTask(db.todoDao()).execute(new Todo(mTodoEditText.getText().toString()));
                    mTodoEditText.setText("");
                }
            }
        });
    }

    //메인스레드에서 데이터베이스에 접근할 수 없으므로 AsyncTask를 사용하도록 한다.
    public static class InsertAsyncTask extends AsyncTask<Todo, Void, Void> {
        private TodoDao mTodoDao;

        public  InsertAsyncTask(TodoDao todoDao){
            this.mTodoDao = todoDao;
        }

        @Override //백그라운드작업(메인스레드 X)
        protected Void doInBackground(Todo... todos) {
            //추가만하고 따로 SELECT문을 안해도 라이브데이터로 인해
            //getAll()이 반응해서 데이터를 갱신해서 보여줄 것이다,  메인액티비티에 옵저버에 쓴 코드가 실행된다. (라이브데이터는 스스로 백그라운드로 처리해준다.)
            mTodoDao.insert(todos[0]);
            return null;
        }
    }

}

 

 

 

5.  결과화면이다 . 글을 추가하면 라이브데이터(getAll, 옵저버)가 반응해서 밑 TextView에 해당 작성한게 자동으로 추가됨을 볼 수 있따.

 

 

이상 room을 간략하게 살펴보았고 애용하도록해야겠다. 또 room에 다양한 기능과 복잡한 기능들이 많은데 더 공부하고 알아봐야겠다.

 

 

 

코틀린으로 되있는 코드나 좀 더 설명이 필요하시다면 다음 링크를 참고해주세요 :)

youngest-programming.tistory.com/179 

 

[코틀린] Kotlin Architecture Components (안드로이드 코틀린 아키텍처 구성요소)

안드로이드 개발자 문서만 보고 공부하면 이해가 안 되거나 감이 안잡히는 부분들도 있어 코드랩과 코드를 보면서 공부하는 중이다. 출처는 다음과 같다. 코드랩 : Android Room with a View - Kotl

youngest-programming.tistory.com

 

 

댓글과 공감은 큰 힘이됩니다. 감사합니다.!

728x90
Comments