我正在尝试使用预先填充的 Room 数据库,但在尝试使用该数据库时出现崩溃,并出现以下错误: java.lang.NullPointerException: cursor.getString(toColumnIndex) must not be null
一切正常,直到我添加该createFromAsset("databses/test.db")
方法,当它崩溃时。如果我不执行该getAllCocktails()
方法,则不会发生任何崩溃。我已经检查了我插入的数据库是否完全相同,并且找不到任何差异(我还检查了使用 AppInspection 加载的数据)
我不仅进行了测试,getAllCocktails()
还尝试使用完全参数化的构造函数进行插入
我将发布(一些)我的代码和日志,以便更容易诊断我尝试使用 Room 的版本 2.5.1 和 2.5.2
测试.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;
}
}
鸡尾酒成分实体:
@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 dump form 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;
and then from the logcat:-
Is probably due to the data from the asset having nulls (or at least one null) in a column when the column in the
@Entity
annotated class does not allow nulls.It should also be noted that if you exclude
db.cocktailDAO().getAllCocktails();
then the database will not even be accessed and thus not even created. That is just getting an instance of the database does not open the database, the open is delayed until an attempt is made to access the database.Summary of findings
The issue, is as predicted, the asset. However it has nothing to do with the getAllCocktails method other than this is what causes the database to be opened and thus the copy of the asset then the opeing of the database.
In this case, as the asset is first copied to a temporary database and then to the actual Room database, the exception was (it is believed) the copy from the asset to the actual database where the exception occurred.
The actual issue being due to discrepancies between the database schema expected (actually required) by Room and the schema of the asset.
As is always suggested, rather than trying to predict Room VERY STRICT schema requirements, the schema that is available in the generated after successfully compiling the project, should be used.
See fix below for how this specific issue can be resolved.
It won't appear to be an issue until you actually try to retrieve the data and hence why the issue only appears when you introduce the data from the asset.
You have two options:-
@Entity
annotated class to allow nulls for the column(s), orYou may wish to modify your question to include:-
@Entity
annotated class or classes.@Dao
annotated class.Working example showing the issue is very likely with the asset
Created AS project using your code (other than using MainActivity instead of PedirTragoActivity) and altered MainActivity to be:-
Ran the above twice with createFromAsset commented out. Log showed first 0 Cocktails, the after the 3 inserts 3 Cocktails. Second run showed 3 and 6.
Note the addition of the close (this to force the WAL to be committed so just one file).
Used Device Explorer to copy the database file into the assets folder and then into the assets/database folder as test.db e.g. :-
Uninstalled the App included the createFromAsset code and reran:-
Resultant Log:-
public CocktailEntity(String name, boolean hasIce, String imaName)
was used, hence the nulls for imageName (and that these being null are not an issue)Edit using downloaded mediafile test.db
In short this is fine and works (at both Version 1 and Version 7).
As such the issue does not appear to be with the supplied code (database wise) nor with the file itself.
i.e. at Version 7, for a fresh install then the log (as per the code above) results in :-
Therefore the issue is perhaps likely to the differences between the two Apps, which could be many.
at androidx.room.util.TableInfoKt.readForeignKeyFieldMappings(TableInfo.kt:536)
Edit Adding additional Entities
So issue is with IngredientTypeEntity/BottleIngredientEnitity
As such issue very much appears to be with BottleIngredientEntity.
At this stage comment out createFromAsset to see if issue is with schema rather than data
include
db.getOpenHelper().getWritableDatabase();
to force open of the database.exclude inserts and logging as probably not an issue.
run App, no issue.
reintroduce BottleIngredientEntity and run App, not an issue.
reintroduce FKEY definitions for BottleIngredientEntity and run App, not an issue.
So the issue very much appears to be the BottleIngredientEntity and how Room manages handling the asset. iirc that a temp db is used which is then copied.
Asset is:-
Room is :-
Advised fix
My suggestion is to drop using the asset you currently have and use a Tool (know nothing about Datagrip) where YOU create the schema in accordance with Room and populate the data accordingly.
That is:-
java(generated)
(visible from Android Studio's Android View)room_master_table
or insert into it).note DO NOT turn Foreign Keys off, I suspect that the column cannot be null is due to an invalid reference in the data that you load and that this exception occurs as part of the copying of the asset from the temp to actual db.
example of getting the CREATE SQL :-
FINAL FIX
As per the above, I believe the core issue is that the SCHEMA of your asset database does not match what Room expects BUT this is masked due to an exception as part of the asset copy when copying the asset to the final Room database from the temporary initial copy.
So the fix is to use the expected schema, the SQL for which is available in the generate java in the
createAllTables
method of the AppDatabase_Impl class (i.e. the@Database
annotated class suffixed with _Impl).To utilise the existing data in the asset, then a conversion can be run. This will rename the existing tables, create the new tables as per the generated SQL and then load the data from the renamed tables. Additionally it will set the user_version (the database version) to 7 (to ensure that fallback doesn't clear the database as there appears to no Migrations, so a version less than 7 would then expect a Migration but in the absence clear the database).
The SQL script being:-
Once converted (and checked for the correct/expected data) then save the database file, ensuring that there is no -wal or -shm file (with Navicat you have to close/end Navicat for it to close the file). Then copy the file into the asset and all should be fine.
After following the above, uninstalling the App and running then the log (inserts commented out):-
Using App Inspection (to run a query) shows:-