从 SQLite 迁移到 Room DB

Migration from SQLite to Room DB

我在从 SQLite 迁移到 Room DB 时遇到问题。

问题是我的旧 SQLite 模式与我旧 SQLite 数据库中的新 Room DB 模式不匹配我忘记将 primaryKey 设置为 NOT NULL 而且我还有一列 URL 而该列不没有任何类型,例如 TEXT、INTEGER 或 BOOLEAN。

所以现在,当我尝试将我的 SQLite 迁移到 Room 时,出现架构不匹配错误,并且我的应用程序是使用 SQLite DB 在 Play 商店上发布的。

因此,我们将不胜感激任何帮助。

我的旧 SQLite 数据库代码:

private static final String DATABASE_NAME = "mylist.db";
private static final String TABLE_NAME = "mylist_data";
private static final String POST_TITLE = "ITEM1";
private static final String POST_URL = "URL";
private static final String KEY_ID = "ID";


public BookmarksDb(Context context) {
    super(context, DATABASE_NAME, null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
    String createTable = "CREATE TABLE " + TABLE_NAME + " (ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
            " ITEM1 TEXT," +
            " URL)";

    db.execSQL(createTable);
}

我的新房间数据库代码:

@Entity(tableName = "mylist_data")
public class Bookmark {
@PrimaryKey()
@ColumnInfo(name = "ID")
private int id;

@ColumnInfo(name = "ITEM1")
private String postTitle;

@ColumnInfo(name = "URL")
private String postUrl;

问题是:

  1. ID 在 SQLite 中为 NOT NULL = false,在 Room 中默认为 true(无法更改)
  2. URL SQLite 中的列没有任何类型,在 Room 中默认为 TEXT。

我不想丢失存储在 SQLite 中的旧数据并且我的应用已发布,所以现在我想迁移到 Room 而不会丢失旧用户数据。

请帮我解决这个问题。

如果 create table 语句中未定义类型,sqlite 默认使用 Blob 列类型。第 3.1.3 of sqlite doc 段。这就是为什么您可以使用 @ColumnInfo(name = "URL", typeAffinity = ColumnInfo.BLOB) 来解决第二个问题的原因。您使用不能为空的类型 int 声明 id,尝试使用 Integer 而不是 int - 我认为它解决了您的第一个问题。

我认为您还有其他选择可以在房间内迁移而不丢失数据:use migration mechanism.

您有两个问题,第一个要求 NOT NULL 是由于 Room 处理原语的方式。因此,不要使用 int,而是使用 Integer(尽管实际上您应该使用 Long)。因此,将实体更改为:-

@Entity(tableName = "mylist_data")
public class Bookmark {
@PrimaryKey()
@ColumnInfo(name = "ID")
private Integer id;

@ColumnInfo(name = "ITEM1")
private String postTitle;

@ColumnInfo(name = "URL")
private String postUrl;

第二个问题是列亲和力,您需要更改 table 以适应实体,因为您有 private String postUrl; 然后您发现 Room 期望列类型为 TEXT 而不是什么都没有(未定义亲和力 = 1)。

为了避免这种情况,您可以 运行 以下 SQL 将 table 转换为适合 Room:-

DROP TABLE IF EXISTS converted_mylist_data;
DROP TABLE IF EXISTS old_mylist_data; 
CREATE TABLE IF NOT EXISTS converted_mylist_data (ID INTEGER PRIMARY KEY AUTOINCREMENT, ITEM1 TEXT, URL TEXT);
INSERT INTO converted_mylist_data SELECT * FROM mylist_data; /* copies existing data into new table */
ALTER TABLE mylist_data RENAME TO old_mylist_data;
ALTER TABLE converted_mylist_data RENAME TO mylist_data;
DROP TABLE IF EXISTS old_mylist_data;
  • 请注意,您实际上可以从 java(生成的)
  • 中检索 SQL 以创建新的 table

例子

运行 1 创建不使用 Room 的数据库(版本 1):-

db.execSQL("CREATE TABLE mylist_data (ID INTEGER PRIMARY KEY AUTOINCREMENT,ITEM1 TEXT, URL);");
db.execSQL("INSERT INTO mylist_data VALUES(null,'item1','my url');");

然后进行以下更改:-

  1. 添加实体

:-

@Entity(tableName = "mylist_data")
public class Bookmark {
    @PrimaryKey()
    @ColumnInfo(name = "ID")
    private Long id; /* <<<<<<<<<< CHANGED (could be Integer) from primative to object*/

    @ColumnInfo(name = "ITEM1")
    private String postTitle;

    @ColumnInfo(name = "URL")
    private String postUrl;

    public Bookmark(){}

    public Long getId() {
        return id;
    }

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

    public String getPostTitle() {
        return postTitle;
    }

    public void setPostTitle(String postTitle) {
        this.postTitle = postTitle;
    }

    public String getPostUrl() {
        return postUrl;
    }

    public void setPostUrl(String postUrl) {
        this.postUrl = postUrl;
    }
}
  1. 阿刀

:-

@Dao
interface AllDao {
    @Query("SELECT * FROM mylist_data")
    List<Bookmark> getAll();
}
  1. 版本增加的数据库和版本 1 到 2 的迁移

:-

@Database(entities = Bookmark.class,version = 2 /*<<<<<<<<<<*/,exportSchema = false)
abstract class TheDatabase extends RoomDatabase {
    abstract AllDao getAllDao();    
    private static volatile TheDatabase instance;

    public static TheDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context,TheDatabase.class,"mylist.db")
                    .allowMainThreadQueries()
                    .addMigrations(MIGRATION_1_2)
                    .build();
        }
        return instance;
    }

    static final Migration MIGRATION_1_2 = new Migration(1,2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {

            database.beginTransaction();
            database.execSQL("DROP TABLE IF EXISTS converted_mylist_data;");
            database.execSQL("DROP TABLE IF EXISTS oldmylist_data;");
            database.execSQL("CREATE TABLE IF NOT EXISTS converted_mylist_data (ID INTEGER PRIMARY KEY AUTOINCREMENT, ITEM1 TEXT, URL TEXT);");
            database.execSQL("INSERT INTO converted_mylist_data SELECT * FROM mylist_data;");
            database.execSQL("ALTER TABLE mylist_data RENAME TO oldmylist_data;");
            database.execSQL("ALTER TABLE main.converted_mylist_data RENAME TO mylist_data;");
            database.setTransactionSuccessful();
            database.endTransaction();

        }
    };
}
  1. 已更改 invoking/using activity(从 SQLite 到 Room,旧代码已注释掉)

:-

public class MainActivity extends AppCompatActivity {

    //DBHelper db; /* Run 1 */
    TheDatabase db; /* Run 2  NEW */
    AllDao dao; /* Run 2  NEW */

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /* Run 1 create the SQLite based database */
        /*
        db = new DBHelper(this);
        db.getWritableDatabase();
         */
        /* Run 2  NEW */
        db = TheDatabase.getInstance(this);
        dao = db.getAllDao();
        for (Bookmark b: dao.getAll()) {
            Log.d("BOOKMARKINFO","ID = " + b.getId() + " PostTitle = " + b.getPostTitle() + " PostURL =" + b.getPostUrl());
        }
    }
}

结果 :-

成功 运行s 并输出 :-

D/BOOKMARKINFO: ID = 1 PostTitle = item1 PostURL =my url

即数据已保存。