Room을 사용하여 투영 처리

14018 단어 AndroidRoom

개요


Android & Room에서 투영을 처리하는 방법을 말씀드리겠습니다.Room이 무엇인지 Room이 어떻게 사용되는지에 대한 자세한 내용은 다른 글을 참조하십시오.

구상된 응용 프로그램


청공 라이브러리 뷰어

기능


  • 제목 목록 보이기
  • 다른 화면에 표시된 일람표에서 클릭한 책의 상세한 내용
  • Book


    표현서 반이야.원래 Primary Key는 ISDN을 사용해야 하는지, title과 author의 복합 키를 사용해야 하는지 등을 사용해야 했지만 이번에는 사랑을 끊었다.
    @Entity
    class Book {
    
        /** 本の名前 */
        @PrimaryKey
        var title: String = ""
    
        /** 本の中身 */
        var content: String = ""
    
        /** いつ読んだか */
        var lastRead: Long = 0L
    
        /** どこまで読んだか */
        var markIndex: Int = 0
    }
    

    Dao


    책의 일람을 표시하는 데 사용되는 getall, 클릭한 책의 내용을 표시하기 위해 내용을 가져오는 findContentByTitle을 정의합니다.
    @Dao
    interface BookRepository {
    
        @Query("SELECT * FROM book ORDER BY lastRead DESC LIMIT 500")
        fun getAll(): List<Book>
    
        @Query("SELECT content FROM book WHERE title = :title LIMIT 1")
        fun findContentByTitle(title: String): String?
    }
    

    모든 열을 꺼내는 데 무슨 문제가 있습니까?



    위의 Book 객체를 RecyclerView에서 처리하면 스크롤이 매우 무거워집니다.
    그림과 같은 책의 일람을 표시할 때 책의 내용이 필요하지 않습니다.콘텐츠에 커다란 데이터가 있기 때문에 상세한 페이지를 열 때마다 읽으면 충분합니다.따라서 Dao의 getall 메서드는 콘텐츠를 추출할 필요가 없습니다.

    룸으로 사영을 처리할 수 있습니까?


    투영


    관계 데이터베이스 테이블에서 일부 열의 작업을 검색합니다.물론 SQLite에서 SELECT에 열을 지정하면 투영을 체크 아웃할 수도 있습니다.
    검색할 열이 하나만 있는 경우 @Query SELECT 에서 열을 지정하고 해당 유형을 반환 값의 유형으로 지정할 수 있습니다.

    룸 투영


    다음 예제에서는 여러 행을 읽어들이고 목록에 지정합니다.
    @Dao
    interface BookRepository {
    
        @Query("SELECT content FROM book")
        fun getAllBookTitles(): List<String>
    

    룸으로 투영?


    그러나 추출하려는 열이 2개 이상인 경우 예를 들어 Pair 등을 사용해도 제대로 매핑되지 않습니다.
    @Dao
    interface BookRepository {
    
        @Query("SELECT title, last_read FROM book")
        fun getAllBookForList(title: String): List<Pair<String, Long>>
    
    다음 컴파일 오류를 내보냅니다.
    エラー: Not sure how to convert a Cursor to this method's return type
    
    이 오류를 보면 Cursor라면 비추기 때문에 Cursor로 받아서 사용하는 것도 방법이겠죠.예전부터 익숙했던 방법으로 처리할 수 있다.다만, Cursor의 경우 어렵게 Room을 사용한다는 의미가 희미해지기 때문에 다른 방법을 찾아보고 싶습니다.
    문외한으로 Room은 SQL에 문제가 있을 때 컴파일이 잘못된 형식으로 지적하는 것이 가장 좋다.

    그럼 어떡하지?


    투영을 나타내는 데이터 클래스를 정의하고 이를 Dao의@Query 함수의 반환값 유형으로 지정하면 투영을 검색할 수 있습니다.

    투영 클래스


    투영으로 읽어들일 열과 같은 이름과 형식을 가진 데이터 클래스를 정의합니다.
    data class BookListItem(
        val title: String,
        val lastRead: Long
        val markIndex: Int
    )
    
    테이블의 열 이름이 다른 필드 이름을 가지려면 @ColumnInfo 에서 열 이름을 지정합니다.
    import androidx.room.ColumnInfo
    
    data class BookListItem(
        @ColumnInfo(name = "title") val bookTitle: String,
        @ColumnInfo(name = "lastRead") val bookLastRead: Long
        @ColumnInfo(name = "markIndex") val markedIndex: Int
    )
    

    Dao 수정


    그런 다음 Dao의 반환 값 유형에 투영된 데이터 클래스를 지정합니다.
    @Dao
    interface BookRepository {
    
        @Query("SELECT title, lastRead, markIndex FROM book")
        fun getAllBookForList(): List<BookListItem>
    
    이렇게 하면 방에서 투영을 처리할 수 있다.

    Room 생성 코드


    Room에서는 SQLite를 사용하여 코드를 자동으로 생성합니다.실제로 어떤 코드가 만들어졌는지 보면 Room에서 사용할 수 없는 환경에서 SQLite에 접근하는 코드를 써야 할 때 유용하다.하지만 Room을 사용할 수 있도록 노력하면 앞으로의 효율이 더욱 좋아질 수 있습니다
      @Override
      public List<BookListItem> getAllBookForList() {
        final String _sql = "SELECT title, lastRead, markIndex FROM book";
        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
        final Cursor _cursor = __db.query(_statement);
        try {
          final int _cursorIndexOfTitle = _cursor.getColumnIndexOrThrow("title");
          final int _cursorIndexOfLastRead = _cursor.getColumnIndexOrThrow("lastRead");
          final int _cursorIndexOfMarkedIndex = _cursor.getColumnIndexOrThrow("markIndex");
          final List<BookListItem> _result = new ArrayList<BookListItem>(_cursor.getCount());
          while(_cursor.moveToNext()) {
            final BookListItem _item;
            final String _tmpTitle;
            _tmpTitle = _cursor.getString(_cursorIndexOfTitle);
            final long _tmpLastRead;
            _tmpLastRead = _cursor.getLong(_cursorIndexOfLastRead );
            final int _tmpMarkedIndex;
            _tmpMarkedIndex = _cursor.getInt(_cursorIndexOfMarkedIndex);
            _item = new BookListItem(_tmpTitle,_tmpLastRead,_tmpMarkedIndex);
            _result.add(_item);
          }
          return _result;
        } finally {
          _cursor.close();
          _statement.release();
        }
      }
    
    룸이 자동으로 이런 정형 코드를 생성할 수 있다는 것을 알면 가져올 때도 쉽게 설득할 수 있다.

    총결산


    Android와 Room을 사용하여 SQLite 처리를 수행할 때 테이블의 일부만 필요하면 투영 클래스에서 열을 정의하여 투영을 읽어들일 수 있습니다.
    그러나 이번 경우처럼 거대한 값을 열의 하나로 SQLite에 넣는 것보다 다른 방법을 취하는 것이 좋다.예를 들어, 이미지를 처리하는 경우 이미지는 응용 프로그램에 파일로 저장되고 SQLite에서는 참조 목적지에만 저장됩니다.이렇게 하면 몇 GB의 데이터를 SQLite에 잘못 넣어 성능에 치명적인 악영향을 끼칠 염려가 없다.
    "표를 정규화하면 되지 않나요?"그렇게 생각하지만 Android & SQLite에서 정규화되면 퍼포먼스가 없을 거예요.나 혼자 안 해봤어.

    참고 자료

  • Returning subsets of columns
  • 좋은 웹페이지 즐겨찾기