Полагаю, вы могли бы до создания базы данных комнат: -
Проверьте, нужно ли что-нибудь сделать, например, используя: -
SELECT count() FROM sqlite_master WHERE name = 'myTable' AND instr(sql,' BOOL ') AND instr(sql,' BYTE ');
и затем проверка результата.
Если это 0, больше ничего не делать (хотя для безопасности вы можете использовать DROP TABLE, ЕСЛИ oldmyTable, если он равен 0).
ТОЛЬКО Если приведенное выше возвращает 1, то: -
удалить переименованную исходную таблицу (см. Ниже и выше) на тот случай, если она существует: -
DROP TABLE IF EXISTS oldmyTable;
определить другую таблицу, используя
заполнить новую таблицу, используя
INSERT INTO myOtherTable SELECT * FROM myTable;
переименуйте mytable, используя: -
ALTER TABLE mytable RENAME TO oldmyTable;
переименуйте myOtherTable, используя оригинальное имя: -
ALTER TABLE myOtherTable RENAME TO mytable;
удалить переименованную исходную таблицу (очевидно, только при тестировании): -
Чистый результат заключается в том, что таблица должна быть такой же, как Ожидается .
По поводу комментария: -
Проблема в том, что мне нужно перенести 16-20 таблиц.
Вы можете использовать что-то вроде: -
public static int preMigrateAdjustment(SQLiteDatabase mDB) {
String original_rename_prefix = "old";
String tempname_suffix = "temp";
String newsql_column = "newsql";
String[] columns = new String[]{
"name",
"replace(replace(sql,' BOOL ',' INTEGER '),' BYTE ',' INTEGER ') AS " + newsql_column
};
int count_done = 0;
String whereclause = "name LIKE('" +
original_rename_prefix +
"%') AND type = 'table'";
Cursor csr = mDB.query("sqlite_master",null,whereclause,null,null,null,null);
while (csr.moveToNext()) {
mDB.execSQL("DROP TABLE IF EXISTS " + csr.getString(csr.getColumnIndex("name")));
}
whereclause = "type = 'table' AND (instr(sql,' BOOL ') OR instr(sql,' BYTE '))";
csr = mDB.query(
"sqlite_master",
columns,
whereclause,
null,null,null,null
);
while (csr.moveToNext()) {
String base_table_name = csr.getString(csr.getColumnIndex("name"));
String newsql = csr.getString(csr.getColumnIndex(newsql_column));
String temp_table_name = base_table_name + tempname_suffix;
String renamed_table_name = original_rename_prefix+base_table_name;
mDB.execSQL(newsql.replace(base_table_name,temp_table_name));
mDB.execSQL("INSERT INTO " + temp_table_name + " SELECT * FROM " + base_table_name);
mDB.execSQL("ALTER TABLE " + base_table_name + " RENAME TO " + renamed_table_name);
mDB.execSQL("ALTER TABLE " + temp_table_name + " RENAME TO " + base_table_name);
count_done++;
}
whereclause = "name LIKE('" +
original_rename_prefix +
"%') AND type = 'table'";
csr = mDB.query("sqlite_master",null,whereclause,null,null,null,null);
while (csr.moveToNext()) {
mDB.execSQL("DROP TABLE IF EXISTS " + csr.getString(csr.getColumnIndex("name")));
}
csr.close();
return count_done;
}
- Обратите внимание, что это не защищает от дурака, например если у вас есть таблицы, которые уже начинаются со старых, то они будут удалены.
- В приведенном выше примере предполагается второй запуск, чтобы фактически удалить переименованные исходные таблицы.
Дополнительно
При рассмотрении этого и фактическом тестировании (в данном случае с использованием 5 таблиц) идентичной схемы после разрешения типов BOOL BYTE в этой кодировке обнаруживается дополнительная проблема
_id INTEGER PRIMARY KEY AUTOINCREMENT
приводит к notNull = false , в то время как кодирование
@PrimaryKey(autoGenerate = true)
private long _id;
приводит к notNull = true
Например, быстрое исправление, предполагающее, что AUTOINCREMENT NOT NULL
не закодировано, строка в preMigrateAdjustment была изменена с: -
mDB.execSQL((newsql.replace(base_table_name,temp_table_name)));
до: -
mDB.execSQL((newsql.replace(base_table_name,temp_table_name)).replace("AUTOINCREMENT","AUTOINCREMENT NOT NULL"));
Рабочая демоверсия
Создание и заполнение старых (предварительных) таблиц.
Создание и заполнение старых таблиц выполняется в Помощнике по базам данных OrginalDBHelper.java : -
public class OriginalDBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "mydb";
public static final int DBVERSION = 1;
int tables_to_create = 5; //<<<<<<<<<< 5 sets of tables
SQLiteDatabase mDB;
public OriginalDBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase();
}
@Override
public void onCreate(SQLiteDatabase db) {
for (int i=0;i < tables_to_create;i++) {
db.execSQL("CREATE TABLE IF NOT EXISTS myTable" + String.valueOf(i) + "X (_id INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
" my_first_field BOOL NOT NULL DEFAULT 0,\n" +
" my_second_field BYTE NOT NULL DEFAULT 0)"
);
db.execSQL("INSERT INTO myTable" + String.valueOf(i) + "X (my_first_field,my_second_field) VALUES(0,0),(1,0),(1,1),(0,1)");
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Преобразование таблиц перед миграцией
т.е. настроить схему в соответствии с помещением) PreMigrationAdjustment.java
public class PreMigrationAdjustment {
public static int preMigrateAdjustment(SQLiteDatabase mDB) {
String original_rename_prefix = "old";
String tempname_suffix = "temp";
String newsql_column = "newsql";
String[] columns = new String[]{
"name",
"replace(replace(sql,' BOOL ',' INTEGER '),' BYTE ',' INTEGER ') AS " + newsql_column
};
int count_done = 0;
String whereclause = "name LIKE('" +
original_rename_prefix +
"%') AND type = 'table'";
Cursor csr = mDB.query("sqlite_master",null,whereclause,null,null,null,null);
while (csr.moveToNext()) {
mDB.execSQL("DROP TABLE IF EXISTS " + csr.getString(csr.getColumnIndex("name")));
}
whereclause = "type = 'table' AND (instr(sql,' BOOL ') OR instr(sql,' BYTE '))";
csr = mDB.query(
"sqlite_master",
columns,
whereclause,
null,null,null,null
);
while (csr.moveToNext()) {
String base_table_name = csr.getString(csr.getColumnIndex("name"));
String newsql = csr.getString(csr.getColumnIndex(newsql_column));
String temp_table_name = base_table_name + tempname_suffix;
String renamed_table_name = original_rename_prefix+base_table_name;
mDB.execSQL((newsql.replace(base_table_name,temp_table_name)).replace("AUTOINCREMENT","AUTOINCREMENT NOT NULL"));
//mDB.execSQL((newsql.replace(base_table_name,temp_table_name)));
mDB.execSQL("INSERT INTO " + temp_table_name + " SELECT * FROM " + base_table_name);
mDB.execSQL("ALTER TABLE " + base_table_name + " RENAME TO " + renamed_table_name);
mDB.execSQL("ALTER TABLE " + temp_table_name + " RENAME TO " + base_table_name);
count_done++;
}
whereclause = "name LIKE('" +
original_rename_prefix +
"%') AND type = 'table'";
csr = mDB.query("sqlite_master",null,whereclause,null,null,null,null);
while (csr.moveToNext()) {
mDB.execSQL("DROP TABLE IF EXISTS " + csr.getString(csr.getColumnIndex("name")));
}
csr.close();
return count_done;
}
}
- ВНИМАНИЕ, это слишком просто для использования без учета его недостатков и предназначено только для демонстрации.
Объекты для комнаты
только 1 из 5 показанных для краткости, т.е. myTable0X.java
Очевидно, что они должны быть тщательно написаны, чтобы соответствовать таблицам перед комнатой.
@Entity()
public class myTable0X {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private long id;
@ColumnInfo(name = "my_first_field")
private boolean my_first_field;
@ColumnInfo(name = "my_second_field")
private boolean my_second_field;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isMy_first_field() {
return my_first_field;
}
public void setMy_first_field(boolean my_first_field) {
this.my_first_field = my_first_field;
}
public boolean isMy_second_field() {
return my_second_field;
}
public void setMy_second_field(boolean my_second_field) {
this.my_second_field = my_second_field;
}
}
Один интерфейс DAO DAOmyTablex.java
@Dao
public interface DAOmyTablex {
@Query("SELECT * FROM myTable0X")
List<myTable0X> getAllFrommyTable0();
@Query("SELECT * FROM myTable1X")
List<myTable1X> getAllFrommyTable1();
@Query("SELECT * FROM myTable2X")
List<myTable2X> getAllFrommyTable2();
@Query("SELECT * FROM myTable3X")
List<myTable3X> getAllFrommyTable3();
@Query("SELECT * FROM myTable4X")
List<myTable4X> getAllFrommyTable4();
@Insert
long[] insertAll(myTable0X... myTable0XES);
@Insert
long[] insertAll(myTable1X... myTable1XES);
@Insert
long[] insertAll(myTable2X... myTable2XES);
@Insert
long[] insertAll(myTable3X... myTable3XES);
@Insert
long[] insertAll(myTable4X... myTable4XES);
@Delete
int delete(myTable0X mytable0X);
@Delete
int delete(myTable1X mytable1X);
@Delete
int delete(myTable2X mytable2X);
@Delete
int delete(myTable3X mytable3X);
@Delete
int delete(myTable4X mytable4X);
}
База данных mydb.java
@Database(entities = {myTable0X.class, myTable1X.class, myTable2X.class, myTable3X.class, myTable4X.class},version = 2)
public abstract class mydb extends RoomDatabase {
public abstract DAOmyTablex dbDAO();
}
- обратите внимание, что все 5 сущностей были использованы.
- обратите внимание, что, поскольку текущая версия базы данных равна 1, номер требует увеличения номера версии, следовательно, version = 2
Собираем все вместе MainActivity.java
Это состоит из 3 основных стадий
- Построение базы данных предварительной комнаты.
- Переоборудование столов в номер.
- Открытие (передача) базы данных через комнату.
Когда приложение запускается, оно автоматически выполняет этапы 1 и 2, добавляется кнопка, которая при нажатии затем выполняет этап 3 (только один раз).
Наконец, данные извлекаются из таблиц (это фактически открывает базу данных Room)
и данные из одной из таблиц выводятся в журнал.
public class MainActivity extends AppCompatActivity {
OriginalDBHelper mDBHlpr;
Button mGo;
mydb mMyDB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGo = this.findViewById(R.id.go);
mGo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
goForIt();
}
});
mDBHlpr = new OriginalDBHelper(this);
Log.d("STAGE1","The original tables");
dumpAllTables();
Log.d("STAGE2", "Initiaing pre-mirgration run.");
Log.d("STAGE2 A RESULT",
String.valueOf(
PreMigrationAdjustment.preMigrateAdjustment(mDBHlpr.getWritableDatabase()
)
) + " tables converted."
); //<<<<<<<<<< CONVERT THE TABLES
Log.d("STAGE2 B","Dumping adjusted tables");
dumpAllTables();
Log.d("STAGE2 C","Second run Cleanup");
Log.d("STAGE2 DRESULT",
String.valueOf(
PreMigrationAdjustment.preMigrateAdjustment(mDBHlpr.getWritableDatabase()
)
) + " tables converted."
); //<<<<<<<<<< CONVERT THE TABLES
dumpAllTables();
Log.d("STAGE3","Handing over to ROOM (when button is clicked)");
}
private void goForIt() {
if (mMyDB != null) return;
mMyDB = Room.databaseBuilder(this,mydb.class,OriginalDBHelper.DBNAME).addMigrations(MIGRATION_1_2).allowMainThreadQueries().build();
List<myTable0X> mt0 = mMyDB.dbDAO().getAllFrommyTable0();
List<myTable1X> mt1 = mMyDB.dbDAO().getAllFrommyTable1();
List<myTable2X> mt2 = mMyDB.dbDAO().getAllFrommyTable2();
List<myTable3X> mt3 = mMyDB.dbDAO().getAllFrommyTable3();
List<myTable4X> mt4 = mMyDB.dbDAO().getAllFrommyTable4();
for (myTable0X mt: mt0) {
Log.d("THIS_MT","ID is " + String.valueOf(mt.getId()) + " FIELD1 is " + String.valueOf(mt.isMy_first_field()) + " FIELD2 is " + String.valueOf(mt.isMy_second_field()));
}
// etc.......
}
private void dumpAllTables() {
SQLiteDatabase db = mDBHlpr.getWritableDatabase();
Cursor c1 = db.query("sqlite_master",null,"type = 'table'",null,null,null,null);
while (c1.moveToNext()) {
Log.d("TABLEINFO","Dmuping Data for Table " + c1.getString(c1.getColumnIndex("name")));
Cursor c2 = db.query(c1.getString(c1.getColumnIndex("name")),null,null,null,null,null,null);
DatabaseUtils.dumpCursor(c2);
c2.close();
}
c1.close();
}
public final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
/**NOTES
//Tried the pre-migration here BUT SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY)
//Cannot use SupportSQLiteDatabase as that locks out access to sqlite_master
//PreMigrationAdjustment.preMigrateAdjustment(mDBHlpr.getWritableDatabase()); //Initial run
//PreMigrationAdjustment.preMigrateAdjustment(mDBHlpr.getWritableDatabase()); //Cleanup run
*/
}
};
}
- Поскольку комната рассмотрит происходящую миграцию, объект миграции имеет метод миграции, переопределенный методом, который ничего не делает.
- Согласно комментариям были предприняты попытки использовать миграцию,проблема заключается в том, что база данных заблокирована комнатой и что SupportSQliteDatabase , переданный методу миграции , не разрешает доступ к sqlite_master .
Результат
Результат (только вывод STAGE ????): -
2019-05-19 13:18:12.227 D/STAGE1: The original tables
2019-05-19 13:18:12.244 D/STAGE2: Initiaing pre-mirgration run.
2019-05-19 13:18:12.281 D/STAGE2 A RESULT: 5 tables converted.
2019-05-19 13:18:12.281 D/STAGE2 B: Dumping adjusted tables
2019-05-19 13:18:12.303 D/STAGE2 C: Second run Cleanup
2019-05-19 13:18:12.304 D/STAGE2 DRESULT: 0 tables converted.
2019-05-19 13:18:12.331 D/STAGE3: Handing over to ROOM (when button is clicked)
Финальные строки: -
2019-05-19 13:20:03.090 D/THIS_MT: ID is 1 FIELD1 is false FIELD2 is false
2019-05-19 13:20:03.090 D/THIS_MT: ID is 2 FIELD1 is true FIELD2 is false
2019-05-19 13:20:03.090 D/THIS_MT: ID is 3 FIELD1 is true FIELD2 is true
2019-05-19 13:20:03.090 D/THIS_MT: ID is 4 FIELD1 is false FIELD2 is true