250x250
11-29 14:18
관리 메뉴

막내의 막무가내 프로그래밍

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

안드로이드/자바 & Previous

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

막내 프로그래밍막내 2019. 8. 25. 10:10
300x250
SMALL

 

이전에 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

 

 

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

300x250
LIST
14 Comments
  • 프로필사진 jinho9610 2020.02.16 13:19 안녕하세요
    혹시, AsyncTask의 doInBackground 부분에서 왜 Todos[0]를 insert해주는지 알 수 있을까요...? 옵저버에서 리스트가 생성되는건가요..? 그리고 인덱스 0을 사용하는 것도 궁급합니다...ㅠㅠ
  • 프로필사진 Favicon of https://youngest-programming.tistory.com BlogIcon 막내 프로그래밍막내 2020.02.16 13:35 신고 안녕하세요~
    excute() 메소드를 호출할 때 사용되는 파라미터를 배열을 doinBackGround() 에서 전달받을 수 있습니다.
    (ex)
    new InsertAsyncTask(db.todoDao()).execute(new Todo(mTodoEditText.getText().toString()));


    doinBackGround에서 ... 보이시면 이게 가변배열을 뜻합니다.
    ex)
    protected Void doInBackground(Todo... todos)



    전 하나의 ToDo객체를 보냈으므로 가변배열의 0번쨰 인덱스로 전달받은 값을 꺼내올 수 있을 것입니다.
  • 프로필사진 jinho9610 2020.02.16 20:25 아하 저게 가변배열이고 하나의 객체가 전달되니까 그냥 제일 첫 인덱스인 0으로 접근을 하면 되는 거군요! 업로드 해주신 내용을 바탕으로 하나의 db에서 두개이상의 dao를 이용해보려니 상당히 어렵네요 ㅠㅠ 좋은 자료 감사합니다~
  • 프로필사진 Favicon of https://youngest-programming.tistory.com BlogIcon 막내 프로그래밍막내 2020.02.16 20:43 신고 도움이 되셨다니 다행이네요.
    요즘은 asynctask가 deprecated되고 잘안쓰이고있으므로
    가볍게 이해하고만 넘어가시길 추천드립니다
  • 프로필사진 Favicon of https://yonoo88.tistory.com BlogIcon  " 2020.04.01 23:41 신고 DB컬럼이 4개가 있는데 그럼 컬럼은 0번쨰~3번째 까지 있는거고

    예제에서는 title하나만 있어서 가능한데 만약 3번째 컬럼에 insert를 하고 싶으면 어떻게 해주면 될까요?
  • 프로필사진 Favicon of https://youngest-programming.tistory.com BlogIcon 막내 프로그래밍막내 2020.04.02 11:58 신고 예제는 아이디, 제목인데 윤호님은 컬럼이 4개시라면 데이터클래스에 값이 4개가 있으실텐데 그 객체를 insert하면 4개값 모두 잘들어가실거에요
    특정방법으로 컨트롤하고싶으면 어노테이션으로 sql문 작성해주시면 됩니다. dao쪽 보심될거같아요
  • 프로필사진 jong 2020.11.23 01:18 live Data 에서 그 안의 리스트만 빼서 쓰고 싶을 때에는 어떻게 하면 될까요?
    getAll.getValue 하면 계속 널 포인터 오류가 나네요
  • 프로필사진 Favicon of https://youngest-programming.tistory.com BlogIcon 막내 프로그래밍막내 2020.11.23 01:25 신고 안녕하세요 자바를 쓰시고 계신거같은데 혹시 데이터 불러오실때 비동기로 불러오신건 맞으신가요??
  • 프로필사진 kim 2021.04.14 19:00 Result Text View에 DB에 들어간 값을 출력하면 주소값이 나오는데 왜 그러는건가요?
    DB에 제대로 값이 들어가는지 확인 하는 다른 방법이 있나요?
  • 프로필사진 Favicon of https://youngest-programming.tistory.com BlogIcon 막내 프로그래밍막내 2021.04.14 19:17 신고 안녕하세요 ㅎㅎ
    예제를 확인해봤는데 주소값이 나올 일은 없는 것 같아요 ... ㅠ

    감사합니다
  • 프로필사진 kim 2021.04.14 19:23 아 ㅜㅜㅜ 그럼 혹시 DB에 값을 확인하는 방법이 따로 없나요? 제대로 들어갔는지 확인을 해볼려고하는데 ㅜㅜ
  • 프로필사진 Favicon of https://youngest-programming.tistory.com BlogIcon 막내 프로그래밍막내 2021.04.14 19:27 신고 전 예전에 Sheto를 통해 확인한적이 있는데요.

    다음 링크를 참고해주시면 좋을 것 같아요!
    https://gun0912.tistory.com/69

    그리고 작년에 안드로이드 스튜디오 새버전에서 Room DB 확인하는게 나왔던걸로 기억나요. 한번 찾아보시면 좋을 것 같아요 ㅎㅎ
    https://stackoverflow.com/questions/17529766/view-contents-of-database-file-in-android-studio

    이 링크에서 밑 답변이 아마 새로나온 DB 확인하는 방법일걸거에요

    Even though this is pretty old question but I think it's still relevant today. With the latest version of android studio I found a way to see it [ I am not sure if it was available in the previous versions. ]

    Here is how you can see the data in your database :

    steps :

    Go to View > Tool Windows > Device File Explorer

    Then the folder data > data and you will see applications lists.

    There scroll down a little bit and see the package [ your package for application ].

    After finding your package, see databases folder and select file, right click it and save [ download ]

    As you can't see the data directly, there is a tool, DB Browser for SQLite, go website https://sqlitebrowser.org/

    Open Db browser and click open database and choose the database table you downloaded and it will show you the data.
  • 프로필사진 kim 2021.04.14 19:39 감사합니다 ㅜㅜㅜㅜ
  • 프로필사진 Favicon of https://youngest-programming.tistory.com BlogIcon 막내 프로그래밍막내 2021.04.15 23:31 신고 화이팅욤 :)
댓글쓰기 폼