房间中的多态实体 [英] Polymorphic entities in Room
问题描述
我的Room DB中有3个实体:
There are 3 entities in my Room DB:
Album
,PhotosMediaItem
和VideosMediaItem
.
VideosMediaItem
和PhotosMediaItem
继承自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.
因此,查询将创建MediaItems列表,但带有派生类型. (PhotoMediaItem
或VideoMediaItem
)以多态方式显示.
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对象,这不是我的意图.我希望此查询启动派生类PhotoMediaItem
或VideoMediaItem
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
这是我的查询在使用常规SQLiteHelper迁移到Room之前的样子,并且运行良好:
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?
推荐答案
我认为您在这里有很多选择:
I think you have a number of options here:
选项1
您使用一个表存储所有MediaItem
,并使用区分符"列来区分视频和照片.
您有一个执行查询,应用order by
并返回Cursor
的DAO方法.然后,您可以使用现有的光标操作逻辑来返回List<MediaItem>
看起来可能像这样:
You use a single table to store all MediaItem
s 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
您使用两个不同的表来存储VideosMediaItem
和PhotosMediaItem
.您有一个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;
}
}
如何利用此选项的实时数据?
How to leverage live data for this option?
正如我提到的,您应该使用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.
您使用两个不同的表来存储VideosMediaItem
和PhotosMediaItem
.您每个人都有一个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;
}
}
这篇关于房间中的多态实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!