There are 3 entities in my Room DB:
Album
, PhotosMediaItem
and VideosMediaItem
.
VideosMediaItem
and PhotosMediaItem
inherit from 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.
So the query will create a list of MediaItems but with the derived types. (PhotoMediaItem
or VideoMediaItem
) in a polymorphic way.
Here's what I've tried:
@Query("SELECT * FROM PhotosMediaItem WHERE PhotosMediaItem = :albumId " +
"UNION SELECT * FROM VideosMediaItem WHERE VideosMediaItem = :albumId" +
" ORDER by CreationDate DESC")
List<MediaItem> getAllMediaInAlbum(int albumId);
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
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();
}
}
}
How can I achieve the same effect using Room then?
I think you have a number of options here:
Option 1
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 theorder by
and returns aCursor
. Then you can use your existing cursor manipulation logic to return aList<MediaItem>
It can look like this:Option 2
You use two different tables to store
VideosMediaItem
andPhotosMediaItem
. You have aMediaItemDao
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:EDIT: How to leverage live data for this option?
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: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 customMediatorLiveData
implementation. It may look like this:Then you just use it in your repository public method like this:
Option 3
This is applicable only if you have a repository layer in place.
You use two different tables to store
VideosMediaItem
andPhotosMediaItem
. 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:I was playing a bit with it and seems found a generic solution to zip multiple LiveData sources together.
And usage:
Not sure if it's the most elegant solution though