• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Issue with writing Room migration even for just adding a new (empty) table to an existing database

This is the third time that I've attempted to write a migration in order to update my database layout with Room (in java). The first two times I ended up just giving up, because no matter what I tried I couldn't get anything to work. I was in a place, previously, where I could just allow a destructive migration, instead, but at this point in development I really need a working migration; I do not want to have to rely on destructive migrations with the amount of data that I currently have in the database. I mean it's not distributed yet, so there aren't other users that'd have to deal with the fallout, but I know I need to learn how do to it The Right Way(tm) anyway, so I'm persisting in working on this migration until it happens, or until I've learned that it's just impossible with Room, which hopefully is not the case. I doubt it would've been recommended to me on #android-dev in the first place if such were the case, though I've found a lot of people that are urging me away from it now due to issues like this that seem to be a bit more common than I would like.

So my problem is fairly simple to describe, at least. I've got an existing database with a couple of tables that are working just fine, but I need to add a new table for some features that are now up on the implementation list. Oh, and I have updated to the latest version of Room, which is (if I'm not mistaken) 1.1.1 currently.

The new table (UsualSuspects.java) that I'm trying to add follows (er well the class that implements it, anyway):

Code:
package com.example.sprite.half_lifetimer;

import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;

@Entity(tableName="UsualSuspect")
public class UsualSuspect {
    @PrimaryKey(autoGenerate = true)
    private int         id;
    @ColumnInfo(name="name")
    private String      name;
    @ColumnInfo(name="sid")
    private int         sid;
    @ColumnInfo(name="dosage")
    private float       dosage;
    @ColumnInfo(name="notes")
    private String      notes;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public float getDosage() {
        return dosage;
    }

    public void setDosage(float dosage) {
        this.dosage = dosage;
    }

    public String getNotes() {
        return notes;
    }

    public void setNotes(String notes) {
        this.notes = notes;
    }

    public UsualSuspect() { }
}

AppDatabase code, including the new migration, follows:
Code:
package com.example.sprite.half_lifetimer;

import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.room.Database;
import android.arch.persistence.room.Room;
import android.arch.persistence.room.RoomDatabase;
import android.arch.persistence.room.TypeConverters;
import android.arch.persistence.room.migration.Migration;
import android.content.Context;

@Database(entities = {Substance.class, Usage.class, UsualSuspect.class}, version = 4)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
    private static AppDatabase INSTANCE;

    public abstract SubstanceDao getSubstanceDao();
    public abstract UsageDao getUsageDao();
    public abstract UsualSuspectDao getUsualSuspectDao();

    public static AppDatabase getDatabase(Context ctxt) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(ctxt.getApplicationContext(), AppDatabase.class,
                                            GlobalMisc.DbName)
                    .allowMainThreadQueries()
                    //.fallbackToDestructiveMigration()
                    //.addMigrations(MIGRATION_2_3)
                    .addMigrations(MIGRATION_3_4)
                    .build();
        }
        return INSTANCE;
    }

    static final Migration MIGRATION_3_4 = new Migration(3, 4) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("CREATE TABLE IF NOT EXISTS `UsualSuspect` (`id` INTEGER, `sid` " +
                    "INTEGER NOT NULL, `dosage` REAL NOT NULL, `notes` VARCHAR NOT NULL, " +
                    "PRIMARY KEY (`id`))");
        }
    };

    public static void destroyInstance() { INSTANCE = null; }
}

When I'm attempting to run the application with the following migration, I'm receiving the following error from Room (forgive the lack of a full stack trace, I'll run it again and include that at the end here, though I do believe this is the applicable bit from it:
Code:
Expected:
    TableInfo{name='UsualSuspect', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1}, notes=Column{name='notes', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}

Found:
    TableInfo{name='UsualSuspect', columns={dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, notes=Column{name='notes', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, name=Column{name='name', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}

As you can see, the only discrepancy (as far as I can tell) between what is expected and what is found is that the columns are arranged differently; however, I can't for the life of me find where that is echoed in the code that I've written. I'm really not sure where on earth to go with this next.

In case it's necessary, the following snippets (in order) are for UsualSuspectDbase and UsualSuspectDao:
Code:
package com.example.sprite.half_lifetimer;

import android.arch.persistence.room.Database;
import android.arch.persistence.room.RoomDatabase;

@Database(entities = {UsualSuspect.class}, version = 1)
public abstract class UsualSuspectDbase extends RoomDatabase {
    public abstract UsualSuspectDao usualSuspectDao();
}

Code:
package com.example.sprite.half_lifetimer;

import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query;

import java.util.List;

@Dao
public interface UsualSuspectDao {
    @Query("SELECT * FROM UsualSuspect")
    List<UsualSuspect> getAll();
}

I would be very grateful for any help that anyone might be able to offer in how I can go about fixing this issue, or some good resources that would spell it out to me a little bit better than what I've found already. I've looked at a ton of google hits already on StackExchange and the like and I'm not able to find anything that's helped so far. Also grateful for any other pointers in the right direction, though honestly I really don't want to try to rewrite this application with something other than Room ORM unless it's absolutely the only way to do it. I'm still kind of an Android environment newbie, and I don't want to have to try to learn a whole different ORM (provided some exist) unless there's no other route to go.

I'm very grateful for any assistance, as I've said, and thank you in advance for anything that you might have to offer. Please let me know if more information is necessary in order to track down this bug appropriately and I'll get it up as soon as I can. TIA
 
On first glance you seem to be doing everything by the book. Must say I've only had cursory experience with Room. To me it looked like a good system, but I didn't really take it as far as the migration process.
That error does look quite weird, in particular this looks as if something got screwed up in the parsing. This doesn't look right at all -

Code:
columns={name=Column{name='name',....

A stack trace may shed some more light on it.

Worst case, you may have to debug into the migration methods, like addMigrations() and even build(), to see exactly what it's doing.

Another approach you could try is to simplify the upgrade. Pare it back to the simplest possible scenario, just a very simple table definition. Does it work? Build things up from there and see when it goes wrong.
 
Last edited by a moderator:
  • Like
Reactions: GameTheory
Upvote 0
So this is what I got from the Debug->Console view in Android Studio. It's missing a bit of the stack trace, I believe; can anybody tell me how to get those '... 9 more' that it's talking about? I'd greatly appreciate it. I'm still kind of a newb for developing in this particular environment.

Code:
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.sprite.half_lifetimer, PID: 18130
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sprite.half_lifetimer/com.example.sprite.half_lifetimer.SubData}: java.lang.IllegalStateException: Migration didn't properly handle UsualSuspect(com.example.sprite.half_lifetimer.UsualSuspect).
     Expected:
    TableInfo{name='UsualSuspect', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1}, notes=Column{name='notes', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='UsualSuspect', columns={dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, notes=Column{name='notes', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, name=Column{name='name', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2858)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2933)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1612)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6710)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
     Caused by: java.lang.IllegalStateException: Migration didn't properly handle UsualSuspect(com.example.sprite.half_lifetimer.UsualSuspect).
     Expected:
    TableInfo{name='UsualSuspect', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1}, notes=Column{name='notes', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='UsualSuspect', columns={dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, notes=Column{name='notes', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, name=Column{name='name', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
        at com.example.sprite.half_lifetimer.AppDatabase_Impl$1.validateMigration(AppDatabase_Impl.java:114)
        at android.arch.persistence.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:87)
        at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:133)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:302)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:194)
        at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:96)
E/AndroidRuntime:     at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:54)
        at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:233)
        at com.example.sprite.half_lifetimer.SubstanceDao_AppDatabase_Impl.getAll(SubstanceDao_AppDatabase_Impl.java:147)
        at com.example.sprite.half_lifetimer.Permanence.loadSubstances(Permanence.java:39)
        at com.example.sprite.half_lifetimer.SubData.onCreate(SubData.java:82)
        at android.app.Activity.performCreate(Activity.java:6982)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2811)
            ... 9 more

        at void android.app.ActivityThread.-wrap11(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1612)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
        at void android.os.Looper.loop() (Looper.java:164)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6710)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.Zygote$MethodAndArgsCaller.run() (Zygote.java:240)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:770)
I/zygote64: Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/view/View$OnUnhandledKeyEventListener;
        at void android.support.v4.view.ViewCompat.setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener) (ViewCompat.java:2203)
        at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:637)
        at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
        at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
        at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
        at void com.example.sprite.half_lifetimer.SubData.onCreate(android.os.Bundle) (SubData.java:41)
        at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6982)
        at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1214)
        at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2811)
        at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2933)
        at void android.app.ActivityThread.-wrap11(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1612)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
        at void android.os.Looper.loop() (Looper.java:164)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6710)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.Zygote$MethodAndArgsCaller.run() (Zygote.java:240)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:770)
    Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.View$OnUnhandledKeyEventListener" on path: DexPathList[[zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/base.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_resources_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.sprite.half_
        at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:93)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
        at void android.support.v4.view.ViewCompat.setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener) (ViewCompat.java:2203)
        at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:637)
        at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
        at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
        at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
        at void com.example.sprite.half_lifetimer.SubData.onCreate(android.os.Bundle) (SubData.java:41)
        at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6982)
        at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1214)
        at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2811)
        at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2933)
        at void android.app.ActivityThread.-wrap11(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1612)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
        at void android.os.Looper.loop() (Looper.java:164)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6710)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.Zygote$MethodAndArgsCaller.run() (Zygote.java:240)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:770)
I/zygote64: Rejecting re-init on previously-failed class java.lang.Class<android.support.v4.view.ViewCompat$OnUnhandledKeyEventListenerWrapper>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/view/View$OnUnhandledKeyEventListener;
        at void android.support.v4.view.ViewCompat.setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener) (ViewCompat.java:2203)
        at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:637)
        at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
        at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
        at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
        at void com.example.sprite.half_lifetimer.SubData.onCreate(android.os.Bundle) (SubData.java:41)
        at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6982)
        at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1214)
        at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2811)
        at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2933)
        at void android.app.ActivityThread.-wrap11(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1612)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
        at void android.os.Looper.loop() (Looper.java:164)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6710)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.Zygote$MethodAndArgsCaller.run() (Zygote.java:240)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:770)
    Caused by: java.lang.ClassNotFoundException: Didn't find class "android.view.View$OnUnhandledKeyEventListener" on path: DexPathList[[zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/base.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_dependencies_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_resources_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_0_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_1_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_2_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_3_apk.apk", zip file "/data/app/com.example.sprite.half_lifetimer-yUem3V7qOCQOMoGaFSaKEQ==/split_lib_slice_4_apk.apk", zip file "/data/app/com.example.sprite.half_
        at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:93)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
        at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
        at void android.support.v4.view.ViewCompat.setOnApplyWindowInsetsListener(android.view.View, android.support.v4.view.OnApplyWindowInsetsListener) (ViewCompat.java:2203)
        at android.view.ViewGroup android.support.v7.app.AppCompatDelegateImpl.createSubDecor() (AppCompatDelegateImpl.java:637)
        at void android.support.v7.app.AppCompatDelegateImpl.ensureSubDecor() (AppCompatDelegateImpl.java:518)
        at void android.support.v7.app.AppCompatDelegateImpl.setContentView(int) (AppCompatDelegateImpl.java:466)
        at void android.support.v7.app.AppCompatActivity.setContentView(int) (AppCompatActivity.java:140)
        at void com.example.sprite.half_lifetimer.SubData.onCreate(android.os.Bundle) (SubData.java:41)
        at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6982)
        at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1214)
        at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2811)
        at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2933)
        at void android.app.ActivityThread.-wrap11(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
        at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1612)
        at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
I/zygote64:     at void android.os.Looper.loop() (Looper.java:164)
        at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6710)
        at java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[]) (Method.java:-2)
        at void com.android.internal.os.Zygote$MethodAndArgsCaller.run() (Zygote.java:240)
        at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:770)
D/loadSubstance: Initializing database
I/SQLiteOpenHelper: Upgrade halflife-db
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.sprite.half_lifetimer, PID: 22703
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sprite.half_lifetimer/com.example.sprite.half_lifetimer.SubData}: java.lang.IllegalStateException: Migration didn't properly handle UsualSuspect(com.example.sprite.half_lifetimer.UsualSuspect).
     Expected:
    TableInfo{name='UsualSuspect', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1}, notes=Column{name='notes', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='UsualSuspect', columns={dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, notes=Column{name='notes', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, name=Column{name='name', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2858)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2933)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1612)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6710)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
     Caused by: java.lang.IllegalStateException: Migration didn't properly handle UsualSuspect(com.example.sprite.half_lifetimer.UsualSuspect).
     Expected:
    TableInfo{name='UsualSuspect', columns={name=Column{name='name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1}, notes=Column{name='notes', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='UsualSuspect', columns={dosage=Column{name='dosage', type='REAL', affinity='4', notNull=true, primaryKeyPosition=0}, notes=Column{name='notes', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, name=Column{name='name', type='VARCHAR', affinity='2', notNull=true, primaryKeyPosition=0}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1}, sid=Column{name='sid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0}}, foreignKeys=[], indices=[]}
        at com.example.sprite.half_lifetimer.AppDatabase_Impl$1.validateMigration(AppDatabase_Impl.java:114)
        at android.arch.persistence.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:87)
        at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:133)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:302)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:194)
        at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:96)
E/AndroidRuntime:     at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:54)
        at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:233)
        at com.example.sprite.half_lifetimer.SubstanceDao_AppDatabase_Impl.getAll(SubstanceDao_AppDatabase_Impl.java:147)
        at com.example.sprite.half_lifetimer.Permanence.loadSubstances(Permanence.java:39)
        at com.example.sprite.half_lifetimer.SubData.onCreate(SubData.java:82)
        at android.app.Activity.performCreate(Activity.java:6982)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2811)
            ... 9 more
 
Upvote 0
The '9 more' are irrelevant and won't provide any more useful information. Stack traces read from top to bottom, and the most relevant line is at the top, just before the exception message.
In particular you want to be looking at this line

Code:
at com.example.sprite.half_lifetimer.AppDatabase_Impl$1.validateMigration(AppDatabase_Impl.java:114)
 
  • Like
Reactions: GameTheory
Upvote 0
Other than that, however, I must admit that, after looking at AppDatabase_Impl.java (line 114 and the associated block), I'm still utterly clueless. If there's anything of relevance there that I'm missing, I'd sure appreciate if anybody could point it out to me so that I know what to look for in the future.

Code:
package com.example.sprite.half_lifetimer;

import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.db.SupportSQLiteOpenHelper;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Callback;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
import android.arch.persistence.room.DatabaseConfiguration;
import android.arch.persistence.room.InvalidationTracker;
import android.arch.persistence.room.RoomOpenHelper;
import android.arch.persistence.room.RoomOpenHelper.Delegate;
import android.arch.persistence.room.util.TableInfo;
import android.arch.persistence.room.util.TableInfo.Column;
import android.arch.persistence.room.util.TableInfo.ForeignKey;
import android.arch.persistence.room.util.TableInfo.Index;
import java.lang.IllegalStateException;
import java.lang.Override;
import java.lang.String;
import java.lang.SuppressWarnings;
import java.util.HashMap;
import java.util.HashSet;

@SuppressWarnings("unchecked")
public class AppDatabase_Impl extends AppDatabase {
  private volatile SubstanceDao _substanceDao;

  private volatile UsageDao _usageDao;

  private volatile UsualSuspectDao _usualSuspectDao;

  @Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(4) {
      @Override
      public void createAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("CREATE TABLE IF NOT EXISTS `Substance` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `units` INTEGER, `cname` TEXT, `sname` TEXT, `halflife` INTEGER NOT NULL, `detectable_halflife` INTEGER NOT NULL, `lipid_soluble` INTEGER NOT NULL)");
        _db.execSQL("CREATE TABLE IF NOT EXISTS `Usage` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sub_id` INTEGER NOT NULL, `dosage` REAL NOT NULL, `timestamp` INTEGER, `notes` TEXT)");
        _db.execSQL("CREATE TABLE IF NOT EXISTS `UsualSuspect` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `sid` INTEGER NOT NULL, `dosage` REAL NOT NULL, `notes` TEXT)");
        _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
        _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"bbc676cc6d4f19002e4df137eff5f3f8\")");
      }

      @Override
      public void dropAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("DROP TABLE IF EXISTS `Substance`");
        _db.execSQL("DROP TABLE IF EXISTS `Usage`");
        _db.execSQL("DROP TABLE IF EXISTS `UsualSuspect`");
      }

      @Override
      protected void onCreate(SupportSQLiteDatabase _db) {
        if (mCallbacks != null) {
          for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
            mCallbacks.get(_i).onCreate(_db);
          }
        }
      }

      @Override
      public void onOpen(SupportSQLiteDatabase _db) {
        mDatabase = _db;
        internalInitInvalidationTracker(_db);
        if (mCallbacks != null) {
          for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
            mCallbacks.get(_i).onOpen(_db);
          }
        }
      }

      @Override
      protected void validateMigration(SupportSQLiteDatabase _db) {
        final HashMap<String, TableInfo.Column> _columnsSubstance = new HashMap<String, TableInfo.Column>(7);
        _columnsSubstance.put("id", new TableInfo.Column("id", "INTEGER", true, 1));
        _columnsSubstance.put("units", new TableInfo.Column("units", "INTEGER", false, 0));
        _columnsSubstance.put("cname", new TableInfo.Column("cname", "TEXT", false, 0));
        _columnsSubstance.put("sname", new TableInfo.Column("sname", "TEXT", false, 0));
        _columnsSubstance.put("halflife", new TableInfo.Column("halflife", "INTEGER", true, 0));
        _columnsSubstance.put("detectable_halflife", new TableInfo.Column("detectable_halflife", "INTEGER", true, 0));
        _columnsSubstance.put("lipid_soluble", new TableInfo.Column("lipid_soluble", "INTEGER", true, 0));
        final HashSet<TableInfo.ForeignKey> _foreignKeysSubstance = new HashSet<TableInfo.ForeignKey>(0);
        final HashSet<TableInfo.Index> _indicesSubstance = new HashSet<TableInfo.Index>(0);
        final TableInfo _infoSubstance = new TableInfo("Substance", _columnsSubstance, _foreignKeysSubstance, _indicesSubstance);
        final TableInfo _existingSubstance = TableInfo.read(_db, "Substance");
        if (! _infoSubstance.equals(_existingSubstance)) {
          throw new IllegalStateException("Migration didn't properly handle Substance(com.example.sprite.half_lifetimer.Substance).\n"
                  + " Expected:\n" + _infoSubstance + "\n"
                  + " Found:\n" + _existingSubstance);
        }
        final HashMap<String, TableInfo.Column> _columnsUsage = new HashMap<String, TableInfo.Column>(5);
        _columnsUsage.put("id", new TableInfo.Column("id", "INTEGER", true, 1));
        _columnsUsage.put("sub_id", new TableInfo.Column("sub_id", "INTEGER", true, 0));
        _columnsUsage.put("dosage", new TableInfo.Column("dosage", "REAL", true, 0));
        _columnsUsage.put("timestamp", new TableInfo.Column("timestamp", "INTEGER", false, 0));
        _columnsUsage.put("notes", new TableInfo.Column("notes", "TEXT", false, 0));
        final HashSet<TableInfo.ForeignKey> _foreignKeysUsage = new HashSet<TableInfo.ForeignKey>(0);
        final HashSet<TableInfo.Index> _indicesUsage = new HashSet<TableInfo.Index>(0);
        final TableInfo _infoUsage = new TableInfo("Usage", _columnsUsage, _foreignKeysUsage, _indicesUsage);
        final TableInfo _existingUsage = TableInfo.read(_db, "Usage");
        if (! _infoUsage.equals(_existingUsage)) {
          throw new IllegalStateException("Migration didn't properly handle Usage(com.example.sprite.half_lifetimer.Usage).\n"
                  + " Expected:\n" + _infoUsage + "\n"
                  + " Found:\n" + _existingUsage);
        }
        final HashMap<String, TableInfo.Column> _columnsUsualSuspect = new HashMap<String, TableInfo.Column>(5);
        _columnsUsualSuspect.put("id", new TableInfo.Column("id", "INTEGER", true, 1));
        _columnsUsualSuspect.put("name", new TableInfo.Column("name", "TEXT", false, 0));
        _columnsUsualSuspect.put("sid", new TableInfo.Column("sid", "INTEGER", true, 0));
        _columnsUsualSuspect.put("dosage", new TableInfo.Column("dosage", "REAL", true, 0));
        _columnsUsualSuspect.put("notes", new TableInfo.Column("notes", "TEXT", false, 0));
        final HashSet<TableInfo.ForeignKey> _foreignKeysUsualSuspect = new HashSet<TableInfo.ForeignKey>(0);
        final HashSet<TableInfo.Index> _indicesUsualSuspect = new HashSet<TableInfo.Index>(0);
        final TableInfo _infoUsualSuspect = new TableInfo("UsualSuspect", _columnsUsualSuspect, _foreignKeysUsualSuspect, _indicesUsualSuspect);
        final TableInfo _existingUsualSuspect = TableInfo.read(_db, "UsualSuspect");
        if (! _infoUsualSuspect.equals(_existingUsualSuspect)) {
          throw new IllegalStateException("Migration didn't properly handle UsualSuspect(com.example.sprite.half_lifetimer.UsualSuspect).\n"
                  + " Expected:\n" + _infoUsualSuspect + "\n"
                  + " Found:\n" + _existingUsualSuspect);
        }
      }
    }, "bbc676cc6d4f19002e4df137eff5f3f8", "734a65b5a98466c1af75b68af768a084");
    final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
        .name(configuration.name)
        .callback(_openCallback)
        .build();
    final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
    return _helper;
  }

  @Override
  protected InvalidationTracker createInvalidationTracker() {
    return new InvalidationTracker(this, "Substance","Usage","UsualSuspect");
  }

  @Override
  public void clearAllTables() {
    super.assertNotMainThread();
    final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase();
    try {
      super.beginTransaction();
      _db.execSQL("DELETE FROM `Substance`");
      _db.execSQL("DELETE FROM `Usage`");
      _db.execSQL("DELETE FROM `UsualSuspect`");
      super.setTransactionSuccessful();
    } finally {
      super.endTransaction();
      _db.query("PRAGMA wal_checkpoint(FULL)").close();
      if (!_db.inTransaction()) {
        _db.execSQL("VACUUM");
      }
    }
  }

  @Override
  public SubstanceDao getSubstanceDao() {
    if (_substanceDao != null) {
      return _substanceDao;
    } else {
      synchronized(this) {
        if(_substanceDao == null) {
          _substanceDao = new SubstanceDao_AppDatabase_Impl(this);
        }
        return _substanceDao;
      }
    }
  }

  @Override
  public UsageDao getUsageDao() {
    if (_usageDao != null) {
      return _usageDao;
    } else {
      synchronized(this) {
        if(_usageDao == null) {
          _usageDao = new UsageDao_AppDatabase_Impl(this);
        }
        return _usageDao;
      }
    }
  }

  @Override
  public UsualSuspectDao getUsualSuspectDao() {
    if (_usualSuspectDao != null) {
      return _usualSuspectDao;
    } else {
      synchronized(this) {
        if(_usualSuspectDao == null) {
          _usualSuspectDao = new UsualSuspectDao_AppDatabase_Impl(this);
        }
        return _usualSuspectDao;
      }
    }
  }
}
 
Upvote 0
Your HashMap equality test returns false, because they have different entries.
This line

Code:
_columnsUsualSuspect.put("name", new TableInfo.Column("name", "TEXT", false, 0));

Differs from the column definition in your existing database table, which has a value of 'true' for the notNull attribute.
If you had written instead

Code:
_columnsUsualSuspect.put("name", new TableInfo.Column("name", "TEXT", true, 0));

then assuming there aren't any other discrepancies, then the equality test would return true, and you wouldn't see that error message.

So the code is behaving exactly as you have written it. My question to you is, what did you expect to happen?
 
  • Like
Reactions: GameTheory
Upvote 0
Well, actually AppDatabase_Impl.java is generated code, so I didn't write it. That's one of the reasons that I'm not so clear on what it does. I understand what you're saying about the 2 different lines of code, but I'm pretty sure that it was generated from the following (in AppDatabase.java):

Code:
static final Migration MIGRATION_3_4 = new Migration(3, 4) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("CREATE TABLE IF NOT EXISTS `UsualSuspect` (`id` INTEGER, `name` " +
                "VARCHAR NOT NULL, `sid` " + "INTEGER NOT NULL, `dosage` REAL NOT NULL, " +
                "`notes` VARCHAR NOT NULL, PRIMARY KEY (`id`))");
    }
};

At least I think so, being as it has the filename of code that I did write, which is vaguely similar to AppDatabase_Impl.java.

The other possibility is that it was generated from UsualSuspects.java, which I'm not going to paste here again for brevity's sake, but is available in the original post. However, there's nothing in that particular class/table definition that has to due with 'null=false', as there is in the above snippet, so I doubt that that has anything to do with it.

I may be missing something obvious here, but I'm pretty sure that every column in the table defined above does, indeed, have NOT NULL specified for it. So the question at this point would be why AppDatabase_Impl.java is being generated with things the way that they are in there right now, right?

I'm confused as all hell by how this is playing out, and do apologize sincerely if I'm missing something or being (not deliberately, at least) obtuse about what's going on here.

Thanks for the assistance.
 
Upvote 0
Ah ok, another piece falls into place :)
I'm kind of flying in the dark here, as I've not been through the process of upgrading a Room database. But that auto-generated code doesn't make a lot of sense to me. Why produce all that column checking code, if the intention is just to create a table if it doesn't exist?
Could you modify the generated code to take out the column checking stuff? In fact, how is that code generated in the first place?
 
Upvote 0
It doesn't make sense to me, either. I wish to the higher powers that be that I could find anything more than just the original dox for Room when I'm searching around... There's really a lack of good resources on this ORM, unless they're all just hiding in a crevasse that I haven't found yet.

The auto-generated code, after I click the generated code link in the stack trace in order to view it, gives me a warning that it shouldn't be modified, but I guess I might as well give it a shot. I can always do a git reset --hard afterwards, if it screws things up as badly as AS seems to think that it might. If it's generated fresh every time, though, it's not going to do much good if I have to do that every single time that I build the app from here on out. :|

I'm starting to think that maybe I should just focus more on a JSON dump & import capability in my app, and fall back to destructive migration again. That'd really limit me to making sure that my database has pretty much every scenario taken into account before I distribute it, though, and if I do end up running into a scenario that requires a database update, any users will have no choice except to wipe their database or do a nasty JSON dump & re-import afterwards. :p
 
Upvote 0
Sure! The repo is here, let me know if you have any problems accessing it.

I'm a little self-conscious about it, so just let me say before you delve into anything that I was picking up Java again, after a very long time, while writing this project, and definitely learning Android Studio as I went. So there are some weird bits in the code, such as a spot where I didn't know how to add things to a layout programmatically yet, so I had added them via the layout editor, then removed everything from the view, and then added them again, etc... If you're just looking at this particular issue, things should be mostly clean and tidy, but I just wanted to let you know, should you happen to stumble onto the earlier bits of code in this project. Don't hold it too much against me, I was learning through trial & error. :)

Also, I really wanted to say thank you again about taking at look at this, and all. It's pretty awesome that you're actually willing to take a look at the repo and the codebase itself if you've got a chance, and I really appreciate it.
 
  • Like
Reactions: GameTheory
Upvote 0
Oh, my bad. I should've specified that those changes are in a branch aside from master. I'm using the git flow methodology, so they currently reside in feature/add-usual-suspects. Also, it should be triggering the migration, if you previously built from master, but if you build straight from feature/add-usual-suspects without having a previously built database from master or develop, you may not see the error. Not sure because I've not been through that scenario yet.

Going to be traveling for a bit today, so my apologies if my replies take a bit for the next while...
 
Upvote 0
Well I can confirm that this is indeed frustrating as hell :mad:
I feel though that I've wandered into the story part way through. You're already on version 4 of the DB. What was the deal with versions 1..3?

And another thing is that your migration code is attempting to create a table, which already exists in the DB. Why? Normally a table upgrade would typically be adding/removing, or modifying table columns in some way.

And also I just don't get this bit of autogen'd code

Code:
_columnsUsualSuspect.put("id", new TableInfo.Column("id", "INTEGER", true, 1));
_columnsUsualSuspect.put("name", new TableInfo.Column("name", "TEXT", false, 0));
_columnsUsualSuspect.put("sid", new TableInfo.Column("sid", "INTEGER", true, 0));
_columnsUsualSuspect.put("dosage", new TableInfo.Column("dosage", "REAL", true, 0));
_columnsUsualSuspect.put("notes", new TableInfo.Column("notes", "TEXT", false, 0));

Those true/false values are the NULLABLE parameters for the columns. Where are these values coming from because they are not specified in your DB Entity class (UsualSuspect).
And they certainly don't agree with what's currently defined on the existing UsualSuspect table.
 
  • Like
Reactions: GameTheory
Upvote 0
I cracked it!!
In your migrate() method, the correct table creation script is as follows:

Code:
database.execSQL("CREATE TABLE IF NOT EXISTS `UsualSuspect` " +
        "(`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
        "`name` TEXT, `sid` INTEGER NOT NULL, `dosage` REAL NOT NULL, " +
        "`notes` TEXT)");

I simply copied this statement from the AppDatabase_Impl class at line 37.
 
Upvote 0
Well I can confirm that this is indeed frustrating as hell :mad:
I feel though that I've wandered into the story part way through. You're already on version 4 of the DB. What was the deal with versions 1..3?

And another thing is that your migration code is attempting to create a table, which already exists in the DB. Why? Normally a table upgrade would typically be adding/removing, or modifying table columns in some way.

Okay, I may be mistaken in parts of this, but here is how I understand it... When I did migrations to version 2 and migration 3, my migrations failed miserably. I didn't have enough in the database at that point to warrant suffering through the debaucle that we're currently engaged in right now, so I added the line in AppDatabase.java (namely `.fallbackToDestructiveMigration()`), which allowed a destructive migration. I commented out the migrations from 1-2, and 2-3, because the destructive migration made those moot. Now that you mention it, though, maybe the missing migrations are, for some reason, causing migration 3-4 to fail... Being as I'm successfully at version 3, I wouldn't expect this to be the case, but things are hardly working as I expect them to at this point, so I'll give it a shot...

Okay, restoring those migrations still results in the exact same error, so I think that my beliefs as far as the methodology behind how this is working still stand. I did, however, just push a commit with those migrations restored (I'd deleted them despite them being commented out previously for some foolish reason). So if you pull a new version of the features/add-usual-suspects branch, you'll be able to see all of the database migration code, at least.

And also I just don't get this bit of autogen'd code

Code:
_columnsUsualSuspect.put("id", new TableInfo.Column("id", "INTEGER", true, 1));
_columnsUsualSuspect.put("name", new TableInfo.Column("name", "TEXT", false, 0));
_columnsUsualSuspect.put("sid", new TableInfo.Column("sid", "INTEGER", true, 0));
_columnsUsualSuspect.put("dosage", new TableInfo.Column("dosage", "REAL", true, 0));
_columnsUsualSuspect.put("notes", new TableInfo.Column("notes", "TEXT", false, 0));

Those true/false values are the NULLABLE parameters for the columns. Where are these values coming from because they are not specified in your DB Entity class (UsualSuspect).
And they certainly don't agree with what's currently defined on the existing UsualSuspect table.

Okay I'm not sure about that autogen code. Not sure what to tell you there.

However, regarding the NULLABLE parameters, I did specify those manually in AppDatabase.java's migration code.

I just got back from my trip, and I'm pretty exhausted, so I don't really have anything else to add to this at this point. I will, however, take another round of looking and debugging things in the morning after I've had some sleep and see if I have any other insights to add. Sorry I don't really have anything else at this point.
 
Upvote 0
Well I can confirm that this is indeed frustrating as hell :mad:
And another thing is that your migration code is attempting to create a table, which already exists in the DB. Why? Normally a table upgrade would typically be adding/removing, or modifying table columns in some way.

Oh, I also forgot to mention, I took a look at the database, myself, and in my database UsualSuspect hasn't been created at all. I suspect the different between our two databases may exist because there are no records in your existing database prior to attempt at creation or migration to version 4, or you jumped straight to creating a database at version 4 without 3 or prior being created at all. Not sure on that, though. :oops:
 
Upvote 0
I cracked it!!
In your migrate() method, the correct table creation script is as follows:

Code:
database.execSQL("CREATE TABLE IF NOT EXISTS `UsualSuspect` " +
        "(`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
        "`name` TEXT, `sid` INTEGER NOT NULL, `dosage` REAL NOT NULL, " +
        "`notes` TEXT)");

I simply copied this statement from the AppDatabase_Impl class at line 37.

Well holy crap. Thank you so very much, LV426. I would've never caught that on my own. I wish I could buy you a beer or something!
 
Upvote 0
Just deposit a couple of bottles of this in the staff fridge please :)

mead.jpg
 
Upvote 0
I posted a solution to the v3 - v4 migration issue in post #19. Your SQL code wasn't right. If you copy the table creation SQL from AppDatabase_Impl, (as per my given code) it works.
Dang LV, it's amazing how you troubleshooted through this and cracked the code. Such badassery! Very impressive!

I hereby dub thee, "Java Jedi". :cool: :D
 
Upvote 0

BEST TECH IN 2023

We've been tracking upcoming products and ranking the best tech since 2007. Thanks for trusting our opinion: we get rewarded through affiliate links that earn us a commission and we invite you to learn more about us.

Smartphones