The room database is re-populated every time I re-run Android so any changes I made before quitting Android are not saved
I want this data to persist forever without Prepopulate the Room database once it's imported Here's the code I created to prepopulate
@Database(entities = {DeviceInfo.class, AddressInfo.class},version = 1)
public abstract class AppDataBase extends RoomDatabase {
public abstract DeviceDao deviceDao();
public abstract AddressDao addressDao();
private static AppDataBase instance;
public static synchronized AppDataBase getInstance(Context context){
if(instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(), AppDataBase.class,"device.db")
.createFromAsset("device.db")
.fallbackToDestructiveMigration()
.build();
}
return instance;
}
}
Here's the update code
@Update
public void updateAddressInfo(AddressInfo addressInfo);
I believe that removing .fallbackToDestructiveMigration()
will resolve your issue; an alternative is to ensure that the prepopulated database's user_version is set to the coded database version.
However, there shouldn't really be the need as the destruction should only be invoked if the database version differs from the coded version and the database's version should be set to the coded version.
Some basic testing appears to highlight what may well be a bug. That is, if the prepopulated version is set to 0 (using PRAGMA user_version = 0;
) then the copied database appears to be destroyed and copied again. If however, the prepopulated database's version (PRAGMA user_version = 1
) then the database persists.
Demonstration
First some basic @Entity
annotated classes were created as per:-
@Entity
class DeviceInfo {
@PrimaryKey
Long id=null;
String name;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
and :-
@Entity
class AddressInfo {
@PrimaryKey
Long id=null;
String name;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Also some @Dao
annotated interfaces:-
@Dao
interface DeviceDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(DeviceInfo deviceInfo);
@Query("SELECT * FROM deviceinfo")
List<DeviceInfo> getAllDeviceInfoRows();
}
and :-
@Dao
interface AddressDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(AddressInfo addressInfo);
@Query("SELECT * FROM addressinfo")
List<AddressInfo> getAllAddressInfoRows();
@Update
public void updateAddressInfo(AddressInfo addressInfo);
}
Then a modified (to add a calback) @Database
annotated AppDataBase class:-
@Database(entities = {DeviceInfo.class, AddressInfo.class},version = 1)
public abstract class AppDataBase extends RoomDatabase {
public abstract DeviceDao deviceDao();
public abstract AddressDao addressDao();
private static AppDataBase instance;
public static synchronized AppDataBase getInstance(Context context){
if(instance == null){
instance = Room.databaseBuilder(context.getApplicationContext(), AppDataBase.class,"device.db")
.createFromAsset("device.db")
.fallbackToDestructiveMigration()
.addCallback(new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
Log.d(MainActivity.TAG,"ONCREATE INVOKED.");
}
@Override
public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
super.onDestructiveMigration(db);
Log.d(MainActivity.TAG,"ONDESTRUCTIVEMIGRATION INVOKED DBVERSION=" + db.getVersion());
}
@Override
public void onOpen(@NonNull SupportSQLiteDatabase db) {
super.onOpen(db);
Log.d(MainActivity.TAG,"ONOPEN INVOKED");
}
})
.allowMainThreadQueries() /* added for brevity */
.build();
}
return instance;
}
}
.allowMainThreadQueries
added so the main thread could be utilised and thus reducing the code needed.Using Navicat the following SQL was used to create 2 database files one with user_vesrion as 0, other as 1 using:-
DROP TABLE IF EXISTS `DeviceInfo`;
DROP TABLE IF EXISTS `AddressInfo`;
CREATE TABLE IF NOT EXISTS `DeviceInfo` (`id` INTEGER, `name` TEXT, PRIMARY KEY(`id`));
CREATE TABLE IF NOT EXISTS `AddressInfo` (`id` INTEGER, `name` TEXT, PRIMARY KEY(`id`));
INSERT OR IGNORE INTO `DeviceInfo` VALUES (100,'PPDEVICE001'),(null,'PPDEVICE002');
INSERT OR IGNORE INTO `AddressInfo` VALUES (100,'PPADDRESS001'),(null,'PPADDRESS002');
PRAGMA user_version = 0;
PRAGMA user_version;
The files were copied into the assets folder e.g. :-
device.db
file being deleted and copied from the respective other file depending on the testing being undertaken.Finally some activity code to test:-
public class MainActivity extends AppCompatActivity {
public static final String TAG = "DBINFO";
AppDataBase db;
AddressDao addressDao;
DeviceDao deviceDao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = AppDataBase.getInstance(this);
deviceDao = db.deviceDao();
addressDao = db.addressDao();
getAll("_S1", db);
for (AddressInfo ai: addressDao.getAllAddressInfoRows()) {
ai.setName(ai.getName() + ai.getName());
addressDao.updateAddressInfo(ai);
}
getAll("S2", db);
}
void getAll(String tag_suffix, AppDataBase adb) {
logDBInfo(tag_suffix, adb);
logDevices(tag_suffix);
logAddresses(tag_suffix);
}
void logDBInfo(String tag_suffix,AppDataBase adb) {
SupportSQLiteDatabase sdb = adb.getOpenHelper().getWritableDatabase();
Log.d(TAG + tag_suffix, "DB VERSION is " + sdb.getVersion() + "DB PATH is " + sdb.getPath());
}
void logDevices(String tag_suffix) {
for(DeviceInfo di: deviceDao.getAllDeviceInfoRows()) {
Log.d(TAG + tag_suffix,"Device ID is " + di.getId() + " NAME is " + di.getName());
}
}
void logAddresses(String tag_suffix) {
for(AddressInfo ai: addressDao.getAllAddressInfoRows()) {
Log.d(TAG + tag_suffix,"Address ID is " + ai.getId() + " NAME is " + ai.getName());
}
}
}
TESTING
Test 1
Initially device_userversion0.db
was copied as device.db
. The app uninstalled and then run (i.e. brand new install). Resulting in the log showing:-
2023-12-20 15:58:01.204 D/DBINFO: ONCREATE INVOKED.
2023-12-20 15:58:01.211 D/DBINFO: ONOPEN INVOKED
2023-12-20 15:58:01.214 D/DBINFO_S1: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 15:58:01.219 D/DBINFO_S1: Device ID is 100 NAME is PPDEVICE001
2023-12-20 15:58:01.219 D/DBINFO_S1: Device ID is 101 NAME is PPDEVICE002
2023-12-20 15:58:01.220 D/DBINFO_S1: Address ID is 100 NAME is PPADDRESS001
2023-12-20 15:58:01.220 D/DBINFO_S1: Address ID is 101 NAME is PPADDRESS002
2023-12-20 15:58:01.224 D/DBINFOS2: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 15:58:01.229 D/DBINFOS2: Device ID is 100 NAME is PPDEVICE001
2023-12-20 15:58:01.229 D/DBINFOS2: Device ID is 101 NAME is PPDEVICE002
2023-12-20 15:58:01.231 D/DBINFOS2: Address ID is 100 NAME is PPADDRESS001PPADDRESS001
2023-12-20 15:58:01.231 D/DBINFOS2: Address ID is 101 NAME is PPADDRESS002PPADDRESS002
Test 2
The App is simply rerun, the output:-
2023-12-20 16:01:11.520 D/DBINFO: ONCREATE INVOKED.
2023-12-20 16:01:11.527 D/DBINFO: ONOPEN INVOKED
2023-12-20 16:01:11.530 D/DBINFO_S1: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:01:11.533 D/DBINFO_S1: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:01:11.533 D/DBINFO_S1: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:01:11.534 D/DBINFO_S1: Address ID is 100 NAME is PPADDRESS001
2023-12-20 16:01:11.534 D/DBINFO_S1: Address ID is 101 NAME is PPADDRESS002
2023-12-20 16:01:11.538 D/DBINFOS2: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:01:11.539 D/DBINFOS2: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:01:11.539 D/DBINFOS2: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:01:11.542 D/DBINFOS2: Address ID is 100 NAME is PPADDRESS001PPADDRESS001
2023-12-20 16:01:11.542 D/DBINFOS2: Address ID is 101 NAME is PPADDRESS002PPADDRESS002
Test 3
The line .fallbackToDestructiveMigration
has been commented out and the App rerun.
This time the output is:-
2023-12-20 16:05:31.133 D/DBINFO: ONOPEN INVOKED
2023-12-20 16:05:31.136 D/DBINFO_S1: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:05:31.140 D/DBINFO_S1: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:05:31.140 D/DBINFO_S1: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:05:31.142 D/DBINFO_S1: Address ID is 100 NAME is PPADDRESS001PPADDRESS001
2023-12-20 16:05:31.142 D/DBINFO_S1: Address ID is 101 NAME is PPADDRESS002PPADDRESS002
2023-12-20 16:05:31.164 D/DBINFOS2: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:05:31.166 D/DBINFOS2: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:05:31.167 D/DBINFOS2: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:05:31.169 D/DBINFOS2: Address ID is 100 NAME is PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001
2023-12-20 16:05:31.169 D/DBINFOS2: Address ID is 101 NAME is PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002
Test 4 The App is uninstalled and run 3 times with .fallbackToDestructiveMigration
commented out. This time the address rows are:-
D/DBINFOS2: Address ID is 100 NAME is PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001
D/DBINFOS2: Address ID is 101 NAME is PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002
**So at this stage not including .fallback....
get's around the issue (bug).
Test5 The App is uninstalled and the device.db
file is deleted and copied from device_userversion1.db and .fallbackToDestructiveMigration
is reinstated as per:-
The output from the first run is:-
2023-12-20 16:16:23.257 D/DBINFO: ONOPEN INVOKED
2023-12-20 16:16:23.260 D/DBINFO_S1: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:16:23.264 D/DBINFO_S1: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:16:23.264 D/DBINFO_S1: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:16:23.266 D/DBINFO_S1: Address ID is 100 NAME is PPADDRESS001
2023-12-20 16:16:23.266 D/DBINFO_S1: Address ID is 101 NAME is PPADDRESS002
2023-12-20 16:16:23.275 D/DBINFOS2: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:16:23.279 D/DBINFOS2: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:16:23.279 D/DBINFOS2: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:16:23.283 D/DBINFOS2: Address ID is 100 NAME is PPADDRESS001PPADDRESS001
2023-12-20 16:16:23.283 D/DBINFOS2: Address ID is 101 NAME is PPADDRESS002PPADDRESS002
Test 6 The App is rerun, the output:-
2023-12-20 16:19:39.703 D/DBINFO: ONOPEN INVOKED
2023-12-20 16:19:39.705 D/DBINFO_S1: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:19:39.709 D/DBINFO_S1: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:19:39.709 D/DBINFO_S1: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:19:39.712 D/DBINFO_S1: Address ID is 100 NAME is PPADDRESS001PPADDRESS001
2023-12-20 16:19:39.712 D/DBINFO_S1: Address ID is 101 NAME is PPADDRESS002PPADDRESS002
2023-12-20 16:19:39.728 D/DBINFOS2: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:19:39.730 D/DBINFOS2: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:19:39.730 D/DBINFOS2: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:19:39.731 D/DBINFOS2: Address ID is 100 NAME is PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001
2023-12-20 16:19:39.731 D/DBINFOS2: Address ID is 101 NAME is PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002
Test 7 a third run:-
2023-12-20 16:21:52.877 D/DBINFO: ONOPEN INVOKED
2023-12-20 16:21:52.879 D/DBINFO_S1: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:21:52.883 D/DBINFO_S1: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:21:52.883 D/DBINFO_S1: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:21:52.886 D/DBINFO_S1: Address ID is 100 NAME is PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001
2023-12-20 16:21:52.886 D/DBINFO_S1: Address ID is 101 NAME is PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002
2023-12-20 16:21:52.901 D/DBINFOS2: DB VERSION is 1DB PATH is /data/user/0/a.a.so77689044javaroomalwaysprepopulates/databases/device.db
2023-12-20 16:21:52.904 D/DBINFOS2: Device ID is 100 NAME is PPDEVICE001
2023-12-20 16:21:52.904 D/DBINFOS2: Device ID is 101 NAME is PPDEVICE002
2023-12-20 16:21:52.905 D/DBINFOS2: Address ID is 100 NAME is PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001PPADDRESS001
2023-12-20 16:21:52.905 D/DBINFOS2: Address ID is 101 NAME is PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002PPADDRESS002