android 使用预填充的房间数据库时,应用程序崩溃

wvyml7n5  于 2023-09-29  发布在  Android
关注(0)|答案(1)|浏览(151)

我尝试使用预填充的Room数据库,但在尝试使用该数据库时出现崩溃,并出现以下错误:java.lang.NullPointerException: cursor.getString(toColumnIndex) must not be null
在我添加createFromAsset("databses/test.db")方法之前,一切都很正常,但当它崩溃时。如果我不执行getAllCocktails()方法,就不会崩溃。我已经检查了我插入的数据库是否都是一样的,我找不到任何差异(我还检查了数据是否用AppInspection加载)
我不仅测试了getAllCocktails(),还尝试了使用完全参数化的构造函数插入
我将张贴(一些)我的代码和日志,以便更容易诊断我尝试与房间的版本2.5.1和2.5.2
test.db文件
活动内容:

public class PedirTragoActivity extends AppCompatActivity {

    AppDatabase db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pedir_trago1_1);

        db = AppDatabase.getInstance(this.getApplication());

        db.cocktailDAO().getAllCocktails();
    }
}

房间数据库:

@Database(entities = {
        BottleEntity.class,
        BottleIngredientEntity.class,
        CocktailEntity.class,
        CocktailIngredientEntity.class,
        IngredientTypeEntity.class
},
        version = 7)
public abstract class AppDatabase extends RoomDatabase {
    public static AppDatabase INSTANCE;

    public abstract BottleDAO bottleDAO();

    public abstract BottleIngredientDAO bottleIngredientDAO();

    public abstract CocktailDAO cocktailDAO();

    public abstract CocktailIngredientDAO cocktailIngredientDAO();

    public abstract IngredientTypeDAO ingredientTypeDAO();
    public static AppDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            INSTANCE = Room.databaseBuilder(context, AppDatabase.class, "barbotApp.db")
                    .allowMainThreadQueries()
                    .fallbackToDestructiveMigration()
                    .createFromAsset("databases/test.db")
                    .build();

        }
        return INSTANCE;
    }
}

瓶实体:

@Entity(tableName = "bottles")
public class BottleEntity {
    @PrimaryKey
    @NonNull
    private int position;//Del 0 al 7 son alcoholes, el 8 es shaker y del 9 al 16 son mezclas
    private String name;
    @NonNull
    private int capacity;
    @NonNull
    private int currentAmount;

    public BottleEntity() {
    }

    public BottleEntity(int position, String name, int capacity, int currentAmount) {
        this.position = position;
        this.name = name;
        this.capacity = capacity;
        this.currentAmount = currentAmount;
    }
    @Ignore
    public BottleEntity(int position, String name, int capacity) {
        this.position = position;
        this.name = name;
        this.capacity = capacity;
        this.currentAmount = capacity;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    public String getName() {
        return name;
    }

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

    public int getCapacity() {
        return capacity;
    }

    public void setCapacity(int capacity) {
        this.capacity = capacity;
    }

    public int getCurrentAmount() {
        return currentAmount;
    }

    public void setCurrentAmount(int currentAmount) {
        this.currentAmount = currentAmount;
    }
}

瓶成分实体:

@Entity(tableName = "bottle_ingredient",
        foreignKeys = {
                @ForeignKey(entity = BottleEntity.class, parentColumns = "position", childColumns = "bottlePos"),
                @ForeignKey(entity = IngredientTypeEntity.class, parentColumns = "id", childColumns = "ingredientId")
        })
public class BottleIngredientEntity {

    @PrimaryKey(autoGenerate = true)
    @NonNull
    private int id;

    @NonNull
    private int bottlePos;

    @NonNull
    private int ingredientId;

    public BottleIngredientEntity() {
    }

    public BottleIngredientEntity(int bottlePos, int ingredientId) {
        this.bottlePos = bottlePos;
        this.ingredientId = ingredientId;
    }

    public int getId() {
        return id;
    }

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

    public int getBottlePos() {
        return bottlePos;
    }

    public void setBottlePos(int bottlePos) {
        this.bottlePos = bottlePos;
    }

    public int getIngredientId() {
        return ingredientId;
    }

    public void setIngredientId(int ingredientId) {
        this.ingredientId = ingredientId;
    }
}

CocktailIngredientEntity:

@Entity(tableName = "cocktail_ingredients",
        foreignKeys = {
                @ForeignKey(entity = IngredientTypeEntity.class, parentColumns = "id", childColumns = "typeId"),
                @ForeignKey(entity = CocktailEntity.class, parentColumns = "id", childColumns = "cocktailId")
        })
public class CocktailIngredientEntity {
    @PrimaryKey(autoGenerate = true)
    @NonNull
    private int id;
    @NonNull
    private int typeId;
    @NonNull
    private int quantity;
    @NonNull
    private int cocktailId;

    public CocktailIngredientEntity(int typeId, int quantity, int cocktailId) {
        this.typeId = typeId;
        this.quantity = quantity;
        this.cocktailId = cocktailId;
    }

    public int getId() {
        return id;
    }

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

    public int getTypeId() {
        return typeId;
    }

    public void setTypeId(int typeId) {
        this.typeId = typeId;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public int getCocktailId() {
        return cocktailId;
    }

    public void setCocktailId(int cocktailId) {
        this.cocktailId = cocktailId;
    }
}

成分类型实体:

@Entity(tableName = "ingredient_types")
public class IngredientTypeEntity {
    @PrimaryKey(autoGenerate = true)
    @NonNull
    private int id;
    private String name;

    public IngredientTypeEntity() {
    }

    public IngredientTypeEntity(String name) {
        this.name = name;
    }

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

鸡尾酒实体:

@Entity(tableName = "cocktails")
public class CocktailEntity {
    @PrimaryKey(autoGenerate = true)
    @NonNull
    private int id;

    private String name;

    @NonNull
    private boolean isOnStock;
    @NonNull
    private boolean hasIce;

    private String imgName;

    public CocktailEntity(String name, boolean isOnStock, boolean hasIce, String imgName) {
        this.name = name;
        this.isOnStock = isOnStock;
        this.hasIce = hasIce;
        this.imgName = imgName;
    }

    @Ignore
    public CocktailEntity(String name, boolean hasIce, String imaName) {
        this.name = name;
        this.isOnStock = false;
        this.hasIce = hasIce;
    }

    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 boolean isOnStock() {
        return isOnStock;
    }

    public void setOnStock(boolean onStock) {
        isOnStock = onStock;
    }

    public boolean isHasIce() {
        return hasIce;
    }

    public void setHasIce(boolean hasIce) {
        this.hasIce = hasIce;
    }

    public String getImgName() {
        return imgName;
    }

    public void setImgName(String imgName) {
        this.imgName = imgName;
    }
}

鸡尾酒道:

@Dao
public interface CocktailDAO {

    @Insert
    long insertCocktail(CocktailEntity cocktail);

    @Query("SELECT id FROM cocktails WHERE name = :name")
    int getCocktailId(String name);

    @Query("SELECT c.* FROM cocktails AS c WHERE c.id = :id")
    CocktailEntity getCocktail(int id); //Need to use a repository, separate this 2 parts

    @Query("SELECT * FROM cocktails")
    List<CocktailEntity> getAllCocktails();

    @Query("SELECT cocktails.id FROM cocktails")
    List<Integer> getAllCocktailIds();

    @Query("SELECT * FROM cocktails" +
            " WHERE cocktails.isOnStock = 1")
    List<CocktailEntity> getAllCocktailsInStock();

    @Query("SELECT isOnStock FROM cocktails WHERE id = :id")
    boolean isCocktailInStock(int id);

    @Query("UPDATE cocktails SET isOnStock = :isOnStock WHERE id = :id")
    void updateCocktailStock(int id, boolean isOnStock);
}

Logcat:

2023-09-23 17:17:20.600  8250-8250  Choreographer           com.mecatronica.barbot               I  Skipped 67 frames!  The application may be doing too much work on its main thread.
2023-09-23 17:17:20.801  8250-8594  AdrenoGLES-0            com.mecatronica.barbot               I  QUALCOMM build                   : 03e27f8, I326e6aff90
                                                                                                    Build Date                       : 11/02/20
                                                                                                    OpenGL ES Shader Compiler Version: EV031.32.02.04
                                                                                                    Local Branch                     : mybrancheb1d781c-1a78-f1f4-8c78-ac1f6bcc2cee
                                                                                                    Remote Branch                    : quic/gfx-adreno.lnx.1.0.r116-rel
                                                                                                    Remote Branch                    : NONE
                                                                                                    Reconstruct Branch               : NOTHING
2023-09-23 17:17:20.801  8250-8594  AdrenoGLES-0            com.mecatronica.barbot               I  Build Config                     : S P 10.0.7 AArch64
2023-09-23 17:17:20.801  8250-8594  AdrenoGLES-0            com.mecatronica.barbot               I  Driver Path                      : /vendor/lib64/egl/libGLESv2_adreno.so
2023-09-23 17:17:20.813  8250-8594  AdrenoGLES-0            com.mecatronica.barbot               I  PFP: 0x016ee190, ME: 0x00000000
2023-09-23 17:17:21.019  8250-8594  LB                      com.mecatronica.barbot               E  fail to open file: No such file or directory
2023-09-23 17:17:21.021  8250-8594  OpenGLRenderer          com.mecatronica.barbot               I  Davey! duration=1547ms; Flags=1, IntendedVsync=42335033169415, Vsync=42336149836037, OldestInputEvent=9223372036854775807, NewestInputEvent=0, HandleInputStart=42336161943432, AnimationStart=42336162004370, PerformTraversalsStart=42336162065047, DrawStart=42336409293120, SyncQueued=42336445054213, SyncStart=42336445465515, IssueDrawCommandsStart=42336445624838, SwapBuffers=42336577074578, FrameCompleted=42336581385203, DequeueBufferDuration=272760, QueueBufferDuration=682187, GpuCompleted=1893829464, 
2023-09-23 17:17:21.027  8250-8250  Looper                  com.mecatronica.barbot               W  PerfMonitor doFrame : time=424ms vsyncFrame=0 latency=1127ms procState=2 historyMsgCount=10 (msgIndex=1 wall=1223ms seq=4 running=1128ms runnable=12ms late=2999ms h=android.app.ActivityThread$H w=159) (msgIndex=5 wall=1007ms seq=8 running=2ms runnable=1ms io=4ms late=3591ms h=android.app.ActivityThread$H w=127)
2023-09-23 17:17:21.154  8250-8250  Looper                  com.mecatronica.barbot               W  PerfMonitor doFrame : time=35ms vsyncFrame=0 latency=459ms procState=2 historyMsgCount=4 (msgIndex=1 wall=424ms seq=14 running=255ms runnable=1ms late=1127ms h=android.view.Choreographer$FrameHandler c=android.view.Choreographer$FrameDisplayEventReceiver) (msgIndex=4 wall=78ms seq=17 running=70ms runnable=2ms late=413ms h=android.view.ViewRootImpl$ViewRootHandler c=androidx.appcompat.app.AppCompatDelegateImpl$2)
2023-09-23 17:17:28.708  8250-8250  MiuiFrameworkFactory    com.mecatronica.barbot               V  get AllImpl object = android.common.MiuiFrameworkFactoryImpl@b038154
2023-09-23 17:17:28.726  8250-8250  MirrorManager           com.mecatronica.barbot               W  this model don't Support
2023-09-23 17:17:28.773  8250-8250  Timeline                com.mecatronica.barbot               I  Timeline: Activity_launch_request time:42344334
2023-09-23 17:17:28.996  8250-8250  DecorView[]             com.mecatronica.barbot               D  getWindowModeFromSystem  windowmode is 1
2023-09-23 17:17:28.997  8250-8250  DecorView               com.mecatronica.barbot               D  createDecorCaptionView windowingMode:1 mWindowMode 1 isFullscreen: true
2023-09-23 17:17:30.631  8250-8250  AndroidRuntime          com.mecatronica.barbot               D  Shutting down VM
2023-09-23 17:17:30.646  8250-8250  AndroidRuntime          com.mecatronica.barbot               E  FATAL EXCEPTION: main
                                                                                                    Process: com.mecatronica.barbot, PID: 8250
                                                                                                    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mecatronica.barbot/com.mecatronica.barbot.PedirTragoActivity}: java.lang.NullPointerException: cursor.getString(toColumnIndex) must not be null
                                                                                                        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3550)
                                                                                                        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3710)
                                                                                                        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
                                                                                                        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
                                                                                                        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
                                                                                                        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2146)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                                                        at android.os.Looper.loop(Looper.java:236)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:8057)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
                                                                                                    Caused by: java.lang.NullPointerException: cursor.getString(toColumnIndex) must not be null
                                                                                                        at androidx.room.util.TableInfoKt.readForeignKeyFieldMappings(TableInfo.kt:536)
                                                                                                        at androidx.room.util.TableInfoKt.readForeignKeys(TableInfo.kt:488)
                                                                                                        at androidx.room.util.TableInfoKt.readTableInfo(TableInfo.kt:472)
                                                                                                        at androidx.room.util.TableInfo$Companion.read(TableInfo.kt:130)
                                                                                                        at androidx.room.util.TableInfo.read(Unknown Source:2)
                                                                                                        at com.mecatronica.barbot.database.AppDatabase_Impl$1.onValidateSchema(AppDatabase_Impl.java:136)
                                                                                                        at androidx.room.RoomOpenHelper.onCreate(RoomOpenHelper.kt:72)
                                                                                                        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onCreate(FrameworkSQLiteOpenHelper.kt:244)
                                                                                                        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:411)
                                                                                                        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
                                                                                                        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableOrReadableDatabase(FrameworkSQLiteOpenHelper.kt:232)
                                                                                                        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.innerGetDatabase(FrameworkSQLiteOpenHelper.kt:190)
                                                                                                        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getSupportDatabase(FrameworkSQLiteOpenHelper.kt:151)
                                                                                                        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.kt:104)
                                                                                                        at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.kt:71)
                                                                                                        at androidx.room.RoomDatabase.inTransaction(RoomDatabase.kt:638)
                                                                                                        at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.kt:457)
                                                                                                        at com.mecatronica.barbot.database.daos.CocktailDAO_Impl.getAllCocktails(CocktailDAO_Impl.java:177)
                                                                                                        at com.mecatronica.barbot.PedirTragoActivity.onCreate(PedirTragoActivity.java:63)
                                                                                                        at android.app.Activity.performCreate(Activity.java:8157)
                                                                                                        at android.app.Activity.performCreate(Activity.java:8129)
                                                                                                        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1310)
                                                                                                        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3523)
                                                                                                        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3710) 
                                                                                                        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) 
                                                                                                        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
                                                                                                        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
                                                                                                        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2146) 
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:106) 
                                                                                                        at android.os.Looper.loop(Looper.java:236) 
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:8057) 
                                                                                                        at java.lang.reflect.Method.invoke(Native Method) 
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656) 
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967) 
2023-09-23 17:17:30.716  8250-8250  Process                 com.mecatronica.barbot               I  Sending signal. PID: 8250 SIG: 9

SQL转储表单test.db:

PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "ingredient_types"
(
    id   INTEGER not null
        constraint ingredient_types_pk
            primary key,
    name TEXT
);
INSERT INTO ingredient_types VALUES(1,'SHAKE');
INSERT INTO ingredient_types VALUES(2,'limón');
INSERT INTO ingredient_types VALUES(3,'ron');
INSERT INTO ingredient_types VALUES(4,'tonica');
INSERT INTO ingredient_types VALUES(5,'ginebra');
INSERT INTO ingredient_types VALUES(6,'campari');
INSERT INTO ingredient_types VALUES(7,'naranja');
INSERT INTO ingredient_types VALUES(8,'fernet');
INSERT INTO ingredient_types VALUES(9,'coca');
INSERT INTO ingredient_types VALUES(10,'durazno');
INSERT INTO ingredient_types VALUES(11,'granadina');
INSERT INTO ingredient_types VALUES(12,'vodka');
CREATE TABLE IF NOT EXISTS "bottle_ingredient"
(
    id           INTEGER not null
        constraint bottle_ingredient_pk
            primary key,
    bottlePos    INTEGER not null
        constraint bottle_ingredient_bottles_position_fk
            references bottles,
    ingredientId INTEGER not null
        constraint bottle_ingredient_ingredient_types_id_fk
            references ingredient_types
);
INSERT INTO bottle_ingredient VALUES(1,8,5);
INSERT INTO bottle_ingredient VALUES(2,9,6);
INSERT INTO bottle_ingredient VALUES(3,10,8);
INSERT INTO bottle_ingredient VALUES(4,11,3);
INSERT INTO bottle_ingredient VALUES(5,12,12);
INSERT INTO bottle_ingredient VALUES(7,0,2);
INSERT INTO bottle_ingredient VALUES(8,1,4);
INSERT INTO bottle_ingredient VALUES(9,2,7);
INSERT INTO bottle_ingredient VALUES(10,3,9);
INSERT INTO bottle_ingredient VALUES(11,4,10);
INSERT INTO bottle_ingredient VALUES(12,5,11);
INSERT INTO bottle_ingredient VALUES(13,16,1);
CREATE TABLE IF NOT EXISTS "bottles"
(
    position      INTEGER not null
        constraint bottles_pk
            primary key,
    name          TEXT,
    capacity      INTEGER not null,
    currentAmount INTEGER not null
);
INSERT INTO bottles VALUES(0,'limon',2000,2000);
INSERT INTO bottles VALUES(1,'tonica',2000,2000);
INSERT INTO bottles VALUES(2,'naranja',2000,2000);
INSERT INTO bottles VALUES(3,'coca',2000,2000);
INSERT INTO bottles VALUES(4,'durazno',2000,2000);
INSERT INTO bottles VALUES(5,'granadina',2000,2000);
INSERT INTO bottles VALUES(6,'Mezcla 7',1,1);
INSERT INTO bottles VALUES(7,'Mezcla 8',1,1);
INSERT INTO bottles VALUES(8,'ginebra',700,700);
INSERT INTO bottles VALUES(9,'campari',750,750);
INSERT INTO bottles VALUES(10,'fernet',750,750);
INSERT INTO bottles VALUES(11,'ron',750,750);
INSERT INTO bottles VALUES(12,'vodka',700,700);
INSERT INTO bottles VALUES(13,'Bottle 6',1,1);
INSERT INTO bottles VALUES(14,'Bottle 7',1,1);
INSERT INTO bottles VALUES(15,'Bottle 8',1,1);
INSERT INTO bottles VALUES(16,'Shakeo',1000,1000);
CREATE TABLE IF NOT EXISTS "cocktail_ingredients"
(
    id         INTEGER not null
        constraint cocktail_ingredients_pk
            primary key,
    typeId     INTEGER not null
        constraint cocktail_ingredients_ingredient_types_id_fk
            references ingredient_types,
    quantity   INTEGER not null,
    cocktailId INTEGER not null
        constraint cocktail_ingredients_cocktails_id_fk
            references cocktails
);
INSERT INTO cocktail_ingredients VALUES(1,6,60,1);
INSERT INTO cocktail_ingredients VALUES(2,7,136,1);
INSERT INTO cocktail_ingredients VALUES(3,9,146,2);
INSERT INTO cocktail_ingredients VALUES(4,8,50,2);
INSERT INTO cocktail_ingredients VALUES(5,5,60,3);
INSERT INTO cocktail_ingredients VALUES(6,4,136,3);
INSERT INTO cocktail_ingredients VALUES(7,3,50,4);
INSERT INTO cocktail_ingredients VALUES(8,9,120,4);
INSERT INTO cocktail_ingredients VALUES(9,2,10,4);
INSERT INTO cocktail_ingredients VALUES(10,12,40,5);
INSERT INTO cocktail_ingredients VALUES(11,10,20,5);
INSERT INTO cocktail_ingredients VALUES(12,7,40,5);
INSERT INTO cocktail_ingredients VALUES(13,1,30,5);
INSERT INTO cocktail_ingredients VALUES(14,11,40,5);
CREATE TABLE IF NOT EXISTS "cocktails"
(
    id        INTEGER not null
        constraint cocktails_pk
            primary key,
    name      TEXT,
    isOnStock INTEGER not null,
    hasIce    INTEGER not null,
    imgName   TEXT
);
INSERT INTO cocktails VALUES(1,'campari',1,1,'campari');
INSERT INTO cocktails VALUES(2,'fernet',1,1,'fernet');
INSERT INTO cocktails VALUES(3,'gin tonic',1,1,'gintonic');
INSERT INTO cocktails VALUES(4,'ron cola',1,1,'roncola');
INSERT INTO cocktails VALUES(5,'sex On  the beach',1,1,'sexOnTheBeach');
COMMIT;
2lpgd968

2lpgd9681#

在我添加createFromAsset(“databses/test.db”)方法之前,一切都很正常
然后从logcat:
cursor.getString(toColumnIndex)不能为null
可能是由于当@Entity注解类中的列不允许空值时,资产中的数据在列中具有空值(或至少一个空值)。
还应该注意的是,如果排除db.cocktailDAO().getAllCocktails();,则数据库甚至不会被访问,因此甚至不会被创建。也就是说,只是获取数据库的示例并不打开数据库,而是延迟打开,直到尝试访问数据库。

调查结果摘要

问题是,正如预测的那样,资产。然而,它与getAllCocktails方法无关,除了这是导致数据库打开的原因,因此是资产的副本,然后是数据库的打开。
在这种情况下,由于资产首先被复制到临时数据库,然后被复制到实际的房间数据库,因此异常是(据信)从资产到发生异常的实际数据库的复制。
实际问题是由于Room预期(实际需要)的数据库模式与资产模式之间的差异。
正如人们一直建议的那样,与其试图预测Room VERY STRICT模式需求,不如使用在成功编译项目后生成的可获得的模式。

有关如何解决此特定问题,请参阅下面的修复程序。

在您实际尝试检索数据之前,它不会成为一个问题,因此只有当您从资产中引入数据时才会出现问题。
你有两个选择:
1.修改@Entity注解类以允许列为空,或者
1.修改源数据使其不包含空值。
你可以修改你的问题,包括:

  1. @Entity注解的类。
  2. @Dao注解类。

工作示例显示问题很可能与资产有关

使用您的代码创建了AS项目(而不是使用MainActivity而不是PedirTragoActivity),并将MainActivity更改为:-

public class MainActivity extends AppCompatActivity {

    AppDatabase db;
    String TAG = "DBINFO";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        db = AppDatabase.getInstance(this.getApplication());
        logAllCocktails(db.cocktailDAO().getAllCocktails(),"STG01");
        db.cocktailDAO().insertCocktail(new CocktailEntity("C1",false,"Blah1"));
        db.cocktailDAO().insertCocktail(new CocktailEntity("C2",true,"Blah2"));
        db.cocktailDAO().insertCocktail(new CocktailEntity("C3",false,"Blah3"));
        logAllCocktails(db.cocktailDAO().getAllCocktails(),"STG02");
        db.close();
    }

    void logAllCocktails(List<CocktailEntity> allCocktails,String tag_suffix) {
        Log.d(TAG +tag_suffix,"Starting Log of All Cocktails where supplied list has " + allCocktails.size() + " items.");
        for (CocktailEntity ce: allCocktails) {
            Log.d(
                    TAG+tag_suffix,
                    "Cocktail Name is " + ce.getName() + " HasIce is " + ce.isHasIce() + " ID is " + ce.getId() + " ImageName is " + ce.getImgName()
            );
        }
    }
}

在createFromAsset被注解掉的情况下运行了上述两次。日志显示前0个鸡尾酒,3个插入3个鸡尾酒后。第二次运行显示3和6。
请注意添加的close(这将强制提交WAL,因此只有一个文件)。
使用设备资源管理器将数据库文件复制到assets文件夹中,然后复制到assets/database文件夹中作为test.db,例如。:-

卸载的应用程序包括从资产中删除代码,并重新运行:-
结果日志:-

2023-09-24 07:51:34.929 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG01: Starting Log of All Cocktails where supplied list has 6 items.
2023-09-24 07:51:34.929 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG01: Cocktail Name is C1 HasIce is false ID is 1 ImageName is null
2023-09-24 07:51:34.929 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG01: Cocktail Name is C2 HasIce is true ID is 2 ImageName is null
2023-09-24 07:51:34.929 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG01: Cocktail Name is C3 HasIce is false ID is 3 ImageName is null
2023-09-24 07:51:34.929 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG01: Cocktail Name is C1 HasIce is false ID is 4 ImageName is null
2023-09-24 07:51:34.929 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG01: Cocktail Name is C2 HasIce is true ID is 5 ImageName is null
2023-09-24 07:51:34.929 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG01: Cocktail Name is C3 HasIce is false ID is 6 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Starting Log of All Cocktails where supplied list has 9 items.
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C1 HasIce is false ID is 1 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C2 HasIce is true ID is 2 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C3 HasIce is false ID is 3 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C1 HasIce is false ID is 4 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C2 HasIce is true ID is 5 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C3 HasIce is false ID is 6 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C1 HasIce is false ID is 7 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C2 HasIce is true ID is 8 ImageName is null
2023-09-24 07:51:34.953 21370-21370/a.a.so77164866javaroomnpefromasset D/DBINFOSTG02: Cocktail Name is C3 HasIce is false ID is 9 ImageName is null
  • 即无NPE崩溃
  • 最初是前面运行的6行,然后
  • 插入3个新行后的9行。
  • 请注意,使用了构造器public CocktailEntity(String name, boolean hasIce, String imaName),因此imageName为null(并且这些为null不是问题)
    使用下载的mediafile test.db编辑

简而言之,这是很好的工作(在版本1和版本7)。
因此,问题似乎不是与提供的代码(数据库明智的),也不是与文件本身。
即在版本7,对于全新安装,则日志(根据上面的代码)导致:

2023-09-24 12:01:19.596 D/DBINFOSTG01: Starting Log of All Cocktails where supplied list has 5 items.
2023-09-24 12:01:19.596 D/DBINFOSTG01: Cocktail Name is campari HasIce is true ID is 1 ImageName is campari
2023-09-24 12:01:19.596 D/DBINFOSTG01: Cocktail Name is fernet HasIce is true ID is 2 ImageName is fernet
2023-09-24 12:01:19.596 D/DBINFOSTG01: Cocktail Name is gin tonic HasIce is true ID is 3 ImageName is gintonic
2023-09-24 12:01:19.596 D/DBINFOSTG01: Cocktail Name is ron cola HasIce is true ID is 4 ImageName is roncola
2023-09-24 12:01:19.596 D/DBINFOSTG01: Cocktail Name is sex On  the beach HasIce is true ID is 5 ImageName is sexOnTheBeach
2023-09-24 12:01:19.608 D/DBINFOSTG02: Starting Log of All Cocktails where supplied list has 8 items.
2023-09-24 12:01:19.608 D/DBINFOSTG02: Cocktail Name is campari HasIce is true ID is 1 ImageName is campari
2023-09-24 12:01:19.608 D/DBINFOSTG02: Cocktail Name is fernet HasIce is true ID is 2 ImageName is fernet
2023-09-24 12:01:19.608 D/DBINFOSTG02: Cocktail Name is gin tonic HasIce is true ID is 3 ImageName is gintonic
2023-09-24 12:01:19.608 D/DBINFOSTG02: Cocktail Name is ron cola HasIce is true ID is 4 ImageName is roncola
2023-09-24 12:01:19.609 D/DBINFOSTG02: Cocktail Name is sex On  the beach HasIce is true ID is 5 ImageName is sexOnTheBeach
2023-09-24 12:01:19.609 D/DBINFOSTG02: Cocktail Name is C1 HasIce is false ID is 6 ImageName is null
2023-09-24 12:01:19.609 D/DBINFOSTG02: Cocktail Name is C2 HasIce is true ID is 7 ImageName is null
2023-09-24 12:01:19.609 D/DBINFOSTG02: Cocktail Name is C3 HasIce is false ID is 8 ImageName is null

因此,问题可能是两个应用程序之间的差异,这可能是很多。

  • 设备(在10.1设备API 28的AS仿真器上工作)
  • 活动(非常简单的初始活动,没有任何特殊的布局),即“活动”的布局不同。
  • 您在发布的代码中注解了实体但是您在测试/运行时是否排除了其他实体?如果不是,则日志指示外键活动。因此,这个问题可能是由于NULL是一个无效的外键。
  • 错误紧接在at androidx.room.util.TableInfoKt.readForeignKeyFieldMappings(TableInfo.kt:536)之后
    *kt可能表示Kotlin代码,对于Java App????
    编辑新增实体
  • 添加BottleEntity,运行后没有问题。
  • 添加BottleBottleDientEntity,需要BottleDientTypeEntity。
  • 添加IngredientTypeEntityNPE游标getString(toColumnIndex)不能为null
    因此问题与IngredientTypeEntity/BottleIngredientEntity有关
  • 注解掉FKEY定义,问题仍然存在
  • 已注解IngredientTYpeEntity,问题仍然存在
  • 注解掉BottleIngredientEntity,重新引入IngredientTypeEntity,运行后没有问题。

因此,此问题似乎与BottlebottledientEntity有关。

- note uninstalling App prior to each run.

在此阶段注解掉createFromAsset,以查看问题是否与模式有关,而不是与数据有关

  • 包括db.getOpenHelper().getWritableDatabase();以强制打开数据库。
  • 排除插入和日志记录,因为这可能不是问题。
  • 运行应用程序,没有问题。
  • 重新引入BottleIngredientEntity并运行App,这不是问题。
  • 重新引入BottleIngredientEntity的FKEY定义并运行App,这不是问题。

因此,问题很大程度上似乎是BottleIngredientEntity以及Room如何管理处理资产。使用临时数据库,然后复制该临时数据库。

  • 比较架构BottleIngredient表:-

资产为:-

CREATE TABLE "bottle_ingredient"
(
    id           INTEGER not null
        constraint bottle_ingredient_pk
            primary key,
    bottlePos    INTEGER not null
        constraint bottle_ingredient_bottles_position_fk
            references bottles,
    ingredientId INTEGER not null
        constraint bottle_ingredient_ingredient_types_id_fk
            references ingredient_types
)

房间是:-

CREATE TABLE `bottle_ingredient` (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    `bottlePos` INTEGER NOT NULL, 
    `ingredientId` INTEGER NOT NULL, 
    FOREIGN KEY(`bottlePos`) REFERENCES `bottles`(`position`) ON UPDATE NO ACTION ON DELETE NO ACTION , 
    FOREIGN KEY(`ingredientId`) REFERENCES `ingredient_types`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION 
)
  • 我怀疑,即使没有NPE问题,也会发生Expected but Found异常。
    建议修复

我的建议是放弃使用您目前拥有的资产,并使用工具(对Datagrip一无所知),您可以根据Room创建模式并相应地填充数据。
即:
1.编译项目,然后检查java(generated)(从Android Studio的Android View可见)
1.找到AppDatabase_Impl类。
1.在类中找到createAllTables方法,复制创建表的SQL(不需要创建room_master_table,也不需要插入)。
1.通过工具或SQL将数据库版本更改为版本7
1.根据需要填充表。
1.保存数据库以确保存在单个文件(即,no -wal或-shm文件)(例如,使用Navicat时,您必须关闭Navicat以使其完全提交)。
1.然后将此文件复制到资产中。

注意不要关闭外键,我怀疑列不能为空是由于您加载的数据中的无效引用,并且此异常是将资产从临时数据库复制到实际数据库的一部分。

获取CREATE SQL的示例:

最终修复

如上所述,我认为核心问题是您的资产数据库的SCHEMA与Room期望的不匹配但是由于将资产从临时初始副本复制到最终Room数据库时作为资产副本的一部分的异常,这被屏蔽了。
因此,修复方法是使用预期的模式,该模式的SQL在AppDatabase_Impl类的createAllTables方法中的generate java中可用(即后缀为_Impl的@Database注解类)。
要利用资产中的现有数据,则可以运行转换。这将重命名现有表,根据生成的SQL创建新表,然后从重命名的表中加载数据。此外,它会将user_version(数据库版本)设置为7(以确保回退不会清除数据库,因为似乎没有迁移,因此小于7的版本将期望迁移,但在不存在的情况下清除数据库)。
SQL脚本为:-

SELECT * FROM pragma_user_version;
/* RUN ALTERS ONCE ONLY ELSE WILL FAIL OR COPY POTENTIALLY NOT ORIGINAL DATA */
/* COMMENTED OUT AS ALREADY DO ONCE */
/*
ALTER TABLE bottles RENAME TO bottles_old;
ALTER TABLE bottle_ingredient RENAME TO bottle_ingredient_old;
ALTER TABLE cocktail_ingredients RENAME TO cocktail_ingredients_old;
ALTER TABLE cocktails RENAME TO cocktails_old;
ALTER TABLE ingredient_types RENAME TO ingredient_types_old;
*/
 /* Optional DROPS but ensures tables are recreated */
DROP TABLE IF EXISTS `cocktail_ingredients`; /* best to drop tables with FKEYS first */
DROP TABLE IF EXISTS `bottle_ingredient`; /* best to drop tables with FKEYS first */
DROP TABLE IF EXISTS `cocktails`;
DROP TABLE IF EXISTS `bottles`;
DROP TABLE IF EXISTS ingredient_types;
CREATE TABLE IF NOT EXISTS `cocktails` (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    `name` TEXT, `isOnStock` INTEGER NOT NULL, 
    `hasIce` INTEGER NOT NULL, `imgName` TEXT
);
CREATE TABLE IF NOT EXISTS `bottles` (
    `position` INTEGER NOT NULL, `name` TEXT, 
    `capacity` INTEGER NOT NULL, 
    `currentAmount` INTEGER NOT NULL, 
    PRIMARY KEY(`position`)
);
/* NOTE MOVED TO BEFORE bottle_ingredients */
CREATE TABLE IF NOT EXISTS `ingredient_types` (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    `name` TEXT
);
CREATE TABLE IF NOT EXISTS `bottle_ingredient` (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    `bottlePos` INTEGER NOT NULL, 
    `ingredientId` INTEGER NOT NULL, 
    FOREIGN KEY(`bottlePos`) REFERENCES `bottles`(`position`) ON UPDATE NO ACTION ON DELETE NO ACTION , 
    FOREIGN KEY(`ingredientId`) REFERENCES `ingredient_types`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION
);
CREATE TABLE IF NOT EXISTS `cocktail_ingredients` (
    `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    `typeId` INTEGER NOT NULL, 
    `quantity` INTEGER NOT NULL, 
    `cocktailId` INTEGER NOT NULL, 
    FOREIGN KEY(`typeId`) REFERENCES `ingredient_types`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , 
    FOREIGN KEY(`cocktailId`) REFERENCES `cocktails`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION 
);
INSERT INTO cocktails SELECT * FROM cocktails_old;
INSERT INTO bottles SELECT * FROM bottles_old;
INSERT INTO ingredient_types SELECT * FROM ingredient_types_old;
INSERT INTO bottle_ingredient SELECT * FROM bottle_ingredient_old;
INSERT INTO cocktail_ingredients SELECT * FROM cocktail_ingredients_old;
PRAGMA user_version = 7;
SELECT * FROM pragma_user_version;
  • 请注意,使用了Navicat,Navicat允许执行多个语句,也允许多个输出。
  • 请阅读注解特别是所有重要的ALTER语句都已注解掉**。

一旦转换(并检查正确/预期的数据),然后保存数据库文件,确保没有-wal或-shm文件(使用Navicat,您必须关闭/结束Navicat才能关闭文件)。然后将文件复制到资产中,一切都应该很好。

  • 注意你可能希望下降的??_旧的table(但是Room不会在意它们的存在,但是它们会浪费空间(每个组件最少4k(表索引....)。

按照上述步骤操作后,卸载应用程序并运行日志(插入注解):-

2023-09-24 14:01:14.970 D/DBINFOSTG02: Starting Log of All Cocktails where supplied list has 5 items.
2023-09-24 14:01:14.970 D/DBINFOSTG02: Cocktail Name is campari HasIce is true ID is 1 ImageName is campari
2023-09-24 14:01:14.970 D/DBINFOSTG02: Cocktail Name is fernet HasIce is true ID is 2 ImageName is fernet
2023-09-24 14:01:14.970 D/DBINFOSTG02: Cocktail Name is gin tonic HasIce is true ID is 3 ImageName is gintonic
2023-09-24 14:01:14.970 D/DBINFOSTG02: Cocktail Name is ron cola HasIce is true ID is 4 ImageName is roncola
2023-09-24 14:01:14.970 D/DBINFOSTG02: Cocktail Name is sex On  the beach HasIce is true ID is 5 ImageName is sexOnTheBeach

使用App Inspection(运行查询)显示:-

相关问题