Room 中的多态实体 [英] Polymorphic entities in Room

查看:24
本文介绍了Room 中的多态实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的 Room DB 中有 3 个实体:

There are 3 entities in my Room DB:

相册PhotosMediaItemVideosMediaItem.

VideosMediaItemPhotosMediaItem 继承自 MediaItem.

MediaItem 不是数据库中的实体,它只是一个抽象基类.

MediaItem is not an entity in the DB, it's just an abstract base class.

我想创建一个查询,以根据创建日期降序返回特定相册中的所有照片和视频媒体项.

I would like to create a query that returns all the photos and videos media items in a specific album with descending order based on their creation date.

因此查询将创建一个包含派生类型的 MediaItem 列表.(PhotoMediaItemVideoMediaItem) 以多态方式.

So the query will create a list of MediaItems but with the derived types. (PhotoMediaItem or VideoMediaItem) in a polymorphic way.

这是我尝试过的:

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

这显然不起作用,因为它试图启动 MediaItem 对象,这不是我的意图.我希望此查询启动派生类 PhotoMediaItemVideoMediaItem

This won't work obviously, because it tries to initiate MediaItem object, and it is not my intention. I want this query to initiate the derived class, PhotoMediaItem or VideoMediaItem

这是我的查询在迁移到 Room 之前的样子,使用常规 SQLiteHelper,它工作得很好:

Here's how my query looked like before the migration to Room, using the regular SQLiteHelper, and it worked just fine:

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 达到同样的效果呢?

How can I achieve the same effect using Room then?

推荐答案

我认为你有很多选择:

选项 1

您使用单个表来存储所有 MediaItem,并使用鉴别器列来区分视频和照片.您有一个执行查询、应用 order by 并返回 Cursor 的单一 DAO 方法.然后,您可以使用现有的光标操作逻辑返回 List<MediaItem>它可能看起来像这样:

You use a single table to store all MediaItems and you use a discriminator column to make the difference between a video and a photo. You have a single DAO method that performs the query, applies the order by and returns a Cursor. Then you can use your existing cursor manipulation logic to return a List<MediaItem> It can look like this:

@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.您有一个 MediaItemDao,它有两个执行查询的内部方法和一个将两个结果集合并在一起并在 java 代码中应用排序的公共方法.它可能看起来像这样:

You use two different tables to store VideosMediaItem and PhotosMediaItem. You have a MediaItemDao that has two internal methods to perform the queries and a single public method that merges the two result sets together and applies the sorting in java code. It can look like this:

@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 作为受保护方法的返回类型,以便在这些表的基础更改时收到通知.所以它们应该是这样的:

As I mentioned, you should use LiveData as the return type of your protected methods so you get notified for underlying changes on those tables. So they should look like this:

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

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

为了向客户端返回单个 LiveData,您应该将这两个方法的输出压缩到单个流中.您可以使用自定义 MediatorLiveData 实现来实现此目的.它可能看起来像这样:

In order to return a single LiveData to the client, you should zip the outputs of those two methods into a single stream. You can achieve this using a custom MediatorLiveData implementation. It may look like this:

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);

    }
}

然后您只需在存储库公共方法中使用它,如下所示:

Then you just use it in your repository public method like this:

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;
        }
}

选项 3

这仅适用于您有存储库层的情况.

This is applicable only if you have a repository layer in place.

您使用两个不同的表来存储 VideosMediaItemPhotosMediaItem.每个都有一个 DAO 类.您有一个依赖于两个 DAO 并组合结果集并应用排序的存储库.它可能看起来像这样:

You use two different tables to store VideosMediaItem and PhotosMediaItem. You have a DAO class for each one. You have a repository that depends on the both DAOs and combines the result sets, applying the sorting. It can look like this:

@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;
    }

}

这篇关于Room 中的多态实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆