0

I have a Room database with two entities. One of them requires a prepopulation, so I have added a callback to the builder in order to this.

For some reason the callback isn't invoked . I've read the answer here, and I do call a DAO method (which returns a LiveData object, if it matters, but I have also tried a calling a DAO method which returns a non-LiveData type), but the issue persists.

The weird thing is that after I clean the app's data from the device's settings, and open the app again, the callback is invoked. This happens both on my physical device and on an AVD one.

Help will be much appreciated.

This is my Database Class:

@Database(entities = {MusicItem.class, 
FormatItem.class}, version = 11)
@TypeConverters(Converters.class)
public abstract class ItemsRoomDatabase extends RoomDatabase {

private static final String TAG = ItemsRoomDatabase.class.getSimpleName();

private static ItemsRoomDatabase INSTANCE;

static ItemsRoomDatabase getDatabase(final Context context) {
    if (INSTANCE == null) {
        synchronized (ItemsRoomDatabase.class) {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                        ItemsRoomDatabase.class, "items_database")
                        .fallbackToDestructiveMigration()
                        .addCallback(new Callback() {
                            @Override
                            public void onCreate(@NonNull SupportSQLiteDatabase db) {
                                Log.d(TAG, "onCreate callback");
                                super.onCreate(db);
                                db.execSQL("INSERT INTO format_items_table (id, name) VALUES (1, 'Unlabeled')");
                                db.execSQL("INSERT INTO format_items_table (id, name) VALUES (2, 'CD')");
                                db.execSQL("INSERT INTO format_items_table (id, name) VALUES (3, 'Vinyl')");


                            }
                        })
                        .build();
            }
        }
    }
    return INSTANCE;
}

public abstract MusicItemDao musicItemDao();

public abstract FormatItemDao formatItemDao();
}

These are my entities classes (parts that seem relevant to me): MusicItem:

@Entity(tableName = "music_items_table",
        indices ={@Index("formatId")},
        foreignKeys = @ForeignKey(entity = FormatItem.class,
                parentColumns = "id", childColumns = "formatId", onDelete = NO_ACTION))
class MusicItem implements Parcelable {


    enum Classification {
        COLLECTION,
        WISHLIST,
        NONE;
    }

    @PrimaryKey
    @NonNull
    private final String id;

    @NonNull
    private String title;
    @NonNull
    private String artist;
    private final String thumbUrl;
    private List<String> genres;
    @NonNull
    private List<Track> tracks;
    private String year;
    @NonNull
    private Classification classification;
    @NonNull
    private int formatId;


     
class Converters {

    private static Gson gson;

    private static Gson getGson() {
        if (gson == null) {
            gson = new Gson();
        }
        return gson;
    }

    // Type converters must be public
    @SuppressWarnings("WeakerAccess")
    @TypeConverter
    public static String fromGenreList(List<String> genres) {

        return getGson().toJson(genres);
    }

    // Type converters must be public
    @SuppressWarnings("WeakerAccess")
    @TypeConverter
    public static List<String> toGenreList(String genres) {
        return getGson().fromJson(genres, new TypeToken<List<String>>() {
        }.getType());
    }

    // Type converters must be public
    @SuppressWarnings("WeakerAccess")
    @TypeConverter
    public static String fromTrackList(List<MusicItem.Track> tracks) {

        return getGson().toJson(tracks);


    }

    // Type converters must be public
    @SuppressWarnings("WeakerAccess")
    @TypeConverter
    public static List<MusicItem.Track> toTrackList(String tracks) {
        return getGson().fromJson(tracks, new TypeToken<List<MusicItem.Track>>() {
        }.getType());
    }

    // Type converters must be public
    @SuppressWarnings("WeakerAccess")
    @TypeConverter
    public static String fromClassification(MusicItem.Classification classification) {
        return classification.name();
    }

    // Type converters must be public
    @SuppressWarnings("WeakerAccess")
    @TypeConverter
    public static MusicItem.Classification toClassification(String classification) {
        return MusicItem.Classification.valueOf(classification);
    }
}

FormatItem:

@Entity(tableName = "format_items_table")
class FormatItem {
    @PrimaryKey(autoGenerate = true)
    private int id;
    @NonNull
    private final String name;
    FormatItem(String name) {
        this.name = name;
    }

    int getId() {
        return id;
    }

    String getName() {
        return name;
    }

    void setId(int id) {
        this.id = id;
    }

    @NonNull
    @Override
    public String toString() {
        return getName();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        FormatItem that = (FormatItem) o;
        return id == that.id &&
                name.equals(that.name);
    }
}

These are my DAOs:

@Dao
abstract class FormatItemDao {

    @Insert
    abstract void insert(FormatItem formatItem);

    @Query("SELECT name FROM format_items_table WHERE id = :position ORDER BY name ASC")
    abstract String getFormatName(int position);

    @Query("SELECT COUNT(*) FROM format_items_table")
    abstract LiveData<Integer> getNumOfFormatsLiveData();

    @Query("SELECT * from format_items_table ORDER BY id ASC")
    abstract List<FormatItem> getFormats();


    @Query("SELECT * from format_items_table ORDER BY id ASC")
    abstract LiveData<List<FormatItem>> getFormatsLiveData();

    @Delete
    abstract void delete(FormatItem formatItem);

    @Query("SELECT name from format_items_table WHERE name = :name COLLATE NOCASE")
    abstract String queryName(String name);

    @Query("SELECT * from format_items_table WHERE id = :formatId")
    abstract FormatItem getFormatById(int formatId);
}


@Dao
abstract class MusicItemDao {

    @Insert
    abstract void insert(MusicItem musicItem);

    @Query("DELETE FROM music_items_table WHERE classification = 'COLLECTION'")
    abstract void deleteAllFromCollection();

    @Query("DELETE FROM music_items_table WHERE classification = 'WISHLIST'")
    abstract void deleteAllFromWishlist();

    @Query("SELECT * FROM music_items_table WHERE classification = 'COLLECTION' ORDER BY title ASC")
    abstract LiveData<List<MusicItem>> getAllCollectionItems();

    @Query("SELECT * from music_items_table WHERE classification = 'WISHLIST' ORDER BY title ASC")
    abstract LiveData<List<MusicItem>> getAllWishlistItems();

    @Query("SELECT * from music_items_table WHERE id = :id")
    abstract MusicItem getItem(String id);

    @Query("SELECT id from music_items_table WHERE id = :id")
    abstract String queryId(String id);

    @Query("DELETE FROM music_items_table WHERE id = :id AND classification = :classification")
    abstract void remove(String id, String classification);

    @Update
    abstract void update(MusicItem musicItem);

    @Query("SELECT COUNT(*) FROM music_items_table WHERE formatId = :formatId")
    abstract Integer getNumOfItemsInFormat(Integer formatId);

    @Query("SELECT * FROM music_items_table WHERE formatId = :formatId")
    abstract List<MusicItem> getAllItemsInFormat(Integer formatId);
}

The logcat doesn't mention anything room related.

tfreifeld
  • 183
  • 11
  • Is it clear that [onCreate()](https://developer.android.com/reference/androidx/room/RoomDatabase.Callback) is only called once, when the database is first created? For the cases where you think the database has not been created, what happens when you call one of the DAO methods? Do you get an error? – Bob Snyder Jul 06 '20 at 13:55
  • @BobSnyder After some additional testing, it seems that the database does get created, but the callback isn't invoked (not unless I delete the app's data as described in the question). I get no errors when I call a DAO method. I call the `getFormatsLiveData` method seen above and upon observing the LiveData I see that the list of formats is empty, which should not have happened if the callback would have been invoked. I've also added a log statement in the callback to be entirely sure it's not invoked, and it confirmed it. – tfreifeld Jul 06 '20 at 14:11
  • Besides clearing app data, what action do you use to trigger DB creation--changing schema version number? – Bob Snyder Jul 06 '20 at 14:40
  • I'm clearing app data and uninstalling the app. Then I install it again hoping that the callback will invoked, but it doesn't. I've changed the schema versions several times, a few times increasing the number even though no changes were made (I thought maybe something was wrong with how it saved the schema). – tfreifeld Jul 06 '20 at 14:44
  • Room seems to behave erratically. Upon advancing the version number and then running the app, I get weird error messages in the build - it says it doesn't recognize the parameters in the `Query` annotations for each method in the DAOs, then immeadiately below it it says that those same parameters in the corresponding method aren't used. In the next build attempt it just gives me a warning saying "Major version 53 is newer than 52, the highest major version supported by this compiler". Next attempt - no errors or warnings in the build.. I don't know what's going on =\ – tfreifeld Jul 06 '20 at 14:55
  • The message about "highest major version supported by this compiler" seems to indicate there is a problem with your configuration for building/compiling related to use of JDK v7/v8. See these [related questions](https://stackoverflow.com/search?q=highest+major+version+supported+by+this+compiler). – Bob Snyder Jul 06 '20 at 15:21

0 Answers0