web-dev-qa-db-ja.com

部屋の多形エンティティ

ルームDBには3つのエンティティがあります。

AlbumPhotosMediaItemおよびVideosMediaItem

VideosMediaItemPhotosMediaItemMediaItemから継承します。

MediaItemはDB内のエンティティではなく、単なる抽象基本クラスです。

特定のアルバム内のすべての写真とビデオのメディアアイテムを、作成日に基づいて降順で返すクエリを作成したいと思います。

したがって、クエリはMediaItemのリストを作成しますが、派生型を使用します。 (PhotoMediaItemまたはVideoMediaItem)多形的に。

これが私が試したことです:

    @Query("SELECT * FROM PhotosMediaItem WHERE PhotosMediaItem = :albumId " +
        "UNION SELECT * FROM VideosMediaItem WHERE VideosMediaItem = :albumId" +
        " ORDER by CreationDate DESC")
    List<MediaItem> getAllMediaInAlbum(int albumId);

MediaItemオブジェクトを開始しようとするため、これは明らかに機能しません。これは私の意図ではありません。このクエリで派生クラスPhotoMediaItemまたはVideoMediaItemを開始したい

通常のSQLiteHelperを使用して、Roomに移行する前のクエリは次のようになり、正常に機能しました。

public ArrayList<MediaItem> getMediaListByAlbumId(int palbumId)
{
    Cursor cursor = null;
    try{
        ArrayList<MediaItem> mediaList = new ArrayList<>();
        String selectQuery = "SELECT "+ mPhotoId +","+ mPhotoCreationDate +", 0 AS mediaType, '' FROM "+ mPhotosTableName + " WHERE " + this.mPhotoAlbumId + "="+palbumId +
                " UNION " +
                "SELECT "+ mVideoId +","+ mVideoCreationDate + " ,1 AS mediaType, " + mVideoLength + " FROM " + mVideosTableName + " WHERE " + this.mVideoAlbumId +"="+palbumId +
                " ORDER BY CreationDate DESC";
        cursor = mDB.rawQuery(selectQuery, null);
        // looping through all rows and adding to list
        if (cursor.moveToFirst()){
            do {
                // MediaHolder consists of the media ID and its type
                int mediaType = cursor.getInt(2);
                MediaItem mediaItem = null;
                if (mediaType == 0) {
                    mediaItem = new PhotoMediaItem(cursor.getInt(0), null, palbumId);
                } else if (mediaType == 1) {
                    mediaItem = new VideoMediaItem(cursor.getInt(0), null, palbumId, cursor.getLong(3));
                }
                mediaList.add(mediaItem);
            }
            while (cursor.moveToNext());
        }
        return mediaList;
    }
    finally  {
        if(cursor != null){
            cursor.close();
        }
    }

}

では、Roomを使用して同じ効果を実現するにはどうすればよいですか?

12
idish

ここにはいくつかのオプションがあると思います。

オプション1

単一のテーブルを使用してすべてのMediaItemを格納し、識別子列を使用してビデオと写真を区別します。クエリを実行し、order byを適用し、Cursorを返す単一のDAOメソッドがあります。次に、既存のカーソル操作ロジックを使用してList<MediaItem>を返すことができます。次のようになります。

@Dao
public abstract class MediaItemDao() {

    @Query("you query here")
    protected Cursor getByAlbumIdInternal(int albumId);

    public List<MediaItem> getByAbumId(int albumId) {
        Cursor cursor = null;
        try{
            List<MediaItem> mediaList = new ArrayList<>();
            cursor = getByAlbumIdInternal(albumId);
            // looping through all rows and adding to list
            if (cursor.moveToFirst()){
                do {
                    // use the discriminator value here
                    int mediaType = cursor.getInt(cursor.getColumnIndex("you discriminator column name here"));
                    MediaItem mediaItem = null;
                    if (mediaType == 0) {
                        mediaItem = new PhotoMediaItem(cursor.getInt(0), null, palbumId);
                    } else if (mediaType == 1) {
                        mediaItem = new VideoMediaItem(cursor.getInt(0), null, palbumId, cursor.getLong(3));
                    }
                    mediaList.add(mediaItem);
                } while (cursor.moveToNext());
            }
            return mediaList;
        }
        finally  {
            if(cursor != null){
                cursor.close();
            }
        }
    }
}

オプション2

VideosMediaItemPhotosMediaItemを格納するために2つの異なるテーブルを使用します。クエリを実行するための2つの内部メソッドと2つの結果セットをマージしてJavaコード)で並べ替えを適用する単一のパブリックメソッドを持つMediaItemDaoがあります。このような:

@Dao
public abstract class MediaItemDao() {

    @Query("your query to get the videos, no order by")
    protected List<VideoMediaItem> getVideosByAlbumId(int albumId);

    @Query("your query to get the photos, no order by")
    protected List<PhotosMediaItem> getPhotosByAlbumId(int albumId);

    @Transaction
    public List<MediaItem> getByAlbumId(int albumId) {
        final List<MediaItem> mediaItems = new LinkedList<>();
        mediaItems.add(getVideosByAlbumId(albumId));
        mediaItems.add(getPhotosByAlbumId(albumId));
        Collections.sort(mediaItems, <you can add a comparator here>);
        return mediaItems;
    }
}

編集:このオプションにライブデータを活用する方法は?

前述したように、保護されたメソッドの戻り値の型としてLiveDataを使用して、これらのテーブルの根本的な変更について通知を受け取る必要があります。したがって、次のようになります。

protected LiveData<List<VideoMediaItem>> getVideosByAlbumId(int albumId);

protected LiveData<List<PhotosMediaItem>> getPhotosByAlbumId(int albumId);

単一のLiveDataをクライアントに返すには、これら2つのメソッドの出力を単一のストリームに圧縮する必要があります。これは、カスタムのMediatorLiveData実装を使用して実現できます。次のようになります。

public class ZipLiveData<T1, T2, R> extends MediatorLiveData<R> {

    private T1 mLastLeft;
    private T2 mLastRight;
    private Zipper<T1, T2, R> mZipper;

    public static final <T1, T2, R> LiveData<R> create(@NonNull LiveData<T1> left, @NonNull LiveData<T2> right, Zipper<T1, T2, R> zipper) {
        final ZipLiveData<T1, T2, R> liveData = new ZipLiveData(zipper);
        liveData.addSource(left, value -> {
            liveData.mLastLeft = value;
            update();
        });
        liveData.addSource(right, value -> {
            liveData.mLastRight = value;
            update();
        });
        return liveData;
    }

    private ZipLiveData(@NonNull Zipper<T1, T2, R> zipper) {
        mZipper = zipper;
    }

    private update() {
        final R result = zipper.Zip(mLastLeft, mLastRight);
        setValue(result);
    }

    public interface Zipper<T1, T2, R> {

        R Zip(T1 left, T2 right);

    }
}

次に、次のようにリポジトリのパブリックメソッドで使用します。

public List<MediaItem> getByAlbumId(int albumId) {
    return ZipLiveData.create(
        getVideosByAlbumId(albumId),
        getPhotosByAlbumId(albumId),
        (videos, photos) -> {
            final List<MediaItem> mediaItems = new LinkedList<>();
            mediaItems.add(videos);
            mediaItems.add(photos);
            Collections.sort(mediaItems, <you can add a comparator here>);
            return mediaItems;
        }
}

オプション

これは、リポジトリレイヤーが配置されている場合にのみ適用されます。

VideosMediaItemPhotosMediaItemを格納するために2つの異なるテーブルを使用します。それぞれにDAOクラスがあります。両方のDAOに依存し、結果セットを組み合わせて並べ替えを適用するリポジトリがあります。次のようになります。

@Dao
public abstract class VideosMediaItemDao {

    @Query("your query to get the videos, no order by")
    public List<VideoMediaItem> getByAlbumId(int albumId);

}

@Dao
public abstract class PhotosMediaItemDao {

    @Query("your query to get the photos, no order by")
    public List<PhotosMediaItem> getByAlbymId(int albumId);

}

public interface MediaItemRepository {

    public List<MediaItem> getByAlbumId(int albumId);

}

class MediaItemRepositoryImpl {

    private final VideosMediaItemDao mVideoDao;
    private final PhotosMediaItemDao mPhotoDao;

    MediaItemRepositoryImpl(VideosMediaItemDao videoDao, PhotosMediaItemDao photoDao) {
        mVideoDao = videoDao;
        mPhotoDao = photoDao;
    }

    @Override
    public List<MediaItem> getByAlbumId(int albumId) {
        final List<MediaItem> mediaItems = new LinkedList<>();
        mediaItems.add(mVideoDao.getByAlbumId(albumId));
        mediaItems.add(mPhotoDao.getByAlbumId(albumId));
        Collections.sort(mediaItems, <you can add a comparator here>);
        return mediaItems;
    }

}
6
Danail Alexiev

私はそれで少し遊んでいましたが、複数のLiveDataソースを一緒に圧縮するための一般的な解決策を見つけたようです。

import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData

class ZipLiveData<S>(private val process: (List<S>) -> List<S>) : MediatorLiveData<List<S>>() {
    val map = hashMapOf<String, List<S>>()
    fun addSource(source: LiveData<*>) {
        addSource(source) {
            map[source.toString()] = it as List<S>
            value = process(map.values.flatten())
        }
    }
}

そして使用法:

    @Transaction
    fun findItems(albumId: Int): LiveData<List<MediaItem>> {
        val liveData = ZipLiveData<MediaItem> { it.sortedBy { item -> item.weight } }
        liveData.addSource(getVideosByAlbumId(albumId))
        liveData.addSource(getPhotosByAlbumId(albumId))
        liveData.addSource(getSoundsByAlbumId(albumId))
        return liveData
    }

それが最もエレガントなソリューションかどうかはわかりませんが

0
dipcore