Android Jetpack之Room

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mingyunxiaohai/article/details/89429969

Room是在Sqlite数据的一个抽象层,拥有更强大的数据访问能力。

导入依赖:

    def room_version = "2.1.0-alpha06"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    
     // kotlin扩展和协程支持
    implementation "androidx.room:room-ktx:$room_version"
    //RxJava 支持库
    implementation "androidx.room:room-rxjava2:$room_version"
    // 可选 - Guava 的支持库
    implementation "androidx.room:room-guava:$room_version"

下面开始使用

第一步创建实体类

假如我们有一个用户表,每个用户的实体就是表中的一列

@Entity
public class User {

    @PrimaryKey
    @NonNull
    @ColumnInfo(name = "id_")
    public int id;

    public String name;
    public String password;
    public String school;

}
  • @Entity: 代表一个表中的实体,默认类名就是表名,如果不想使用类名作为表名,可以给注解添加表名字段@Entity(tableName = "user_table")
  • @PrimaryKey: 每个实体都需要自己的主键
  • @NonNull 表示字段,方法,参数返回值不能为空
  • @ColumnInfo(name = “lastname”) 如果希望表中字段名跟类中的成员变量名不同,添加此字段指明

第二步创建DAO

  • DAO是数据访问对象,指定SQL查询,并让他与方法调用相关联。
  • DAO必须是一个接口或者抽象类。
  • 默认情况下,所有的查询都必须在单独的线程中执行
@Dao
public interface UserDao {
    @Insert
    void insert(User user);

    @Query("select * from user")
    List<User> getUserList();

    @Query("delete from user")
    void deleteAll();
    
     @Update
    void updateUsers(User... users);
}
  • 创建一个接口UserDao
  • 给它添加注解@Dao,表名它是Room的一个查询类
  • 声明一个插入用户的方法insert,并给它添加注解@Insert,不用提供任何SQL语句
  • 声明一个删除全部的方法,deleteAll(),删除方法没有便捷方法,需要使用@Query注解,并且提供相应的SQL语句delete from user
  • 声明一个getUserList方法来查询所有的用户,这个也没有便捷方法,,需要使用@Query注解,并且提供相应的SQL语句select * from user

第三步添加Database

Room是SQLite数据库之上的数据库层,可以让我们轻松的使用系统原始API:SQLiteOpenHelper

@Database(entities = {User.class},version = 1)
public abstract class UserRoomDatabase extends RoomDatabase {
   public abstract UserDao userDao();

   public static UserRoomDatabase instance;

   public static UserRoomDatabase getInstance(Context context){
       if(instance == null){
           synchronized (UserRoomDatabase.class){
               if(instance == null){
                   instance = Room.databaseBuilder(context.getApplicationContext(),UserRoomDatabase.class
                   ,"user_database").build();
               }
           }
       }
       return instance;
   }
}
  • 创建一个抽象类继承自RoomDatabase
  • 给他添加一个注解@Database表名它是一个数据库,注解有两个参数第一个是数据库的实体,它是一个数组,可以传多个,当数据库创建的时候,会默认给创建好对应的表,第二个参数是数据库的版本号
  • 定义跟数据库一起使用的相关的DAO类
  • 创建一个UserRoomDatabase的单例,防止同时打开多个数据库的实例
  • 使用Room提供的数据库构建器来创建该实例,第一个参数application,第二个参数当前数据库的实体类,第三个参数数据库的名字

第四步开始使用

前面三步主要步骤写完了,现在就可以开始使用了,使用的时候,为了让Activity中代码简洁,创建一个UserRepository类来管理这个数据库

public class UserRepository {

    private UserDao mUserDao;

    private List<User> allUser;

    public UserRepository(Application application) {
        //UserRoomDatabase db = UserRoomDatabase.getInstance(application);
        //mUserDao = db.userDao();
        //allUser = mUserDao.getUserList();
        //使用ViweModel可以直接使用上面注释了的
        new InitThread(application).start();
    }

    public List<User> getAllUser() {
        return allUser;
    }

    public void deleteAll(){
        new DeleteAsyncTask(mUserDao).execute();
    }
    
    public void update(User user){
       new UpdateAsyncTask(mUserDao).execute(user);
    }

    public void insert(User user){
     new InsertAsyncTask(mUserDao).execute(user);
    }

    private  class InitThread extends Thread{
        Application application;
        InitThread(Application application){
            this.application = application;
        }
        @Override
        public void run() {
            UserRoomDatabase db = UserRoomDatabase.getInstance(application);
            mUserDao = db.userDao();
            allUser = mUserDao.getUserList();
        }
    }
    //更新
    private static class UpdateAsyncTask extends AsyncTask<User, Void, Void> {

        private UserDao mAsyncTaskDao;

        UpdateAsyncTask(UserDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(final User... params) {
            mAsyncTaskDao.updateUsers(params[0]);
            return null;
        }
    }
    //插入
    private static class InsertAsyncTask extends AsyncTask<User, Void, Void> {

        private UserDao mAsyncTaskDao;

        InsertAsyncTask(UserDao dao) {
            mAsyncTaskDao = dao;
        }

        @Override
        protected Void doInBackground(final User... params) {
            mAsyncTaskDao.insert(params[0]);
            return null;
        }
    }
    //删除
    private static class DeleteAsyncTask extends AsyncTask<Void, Void, Void> {

        private UserDao mAsyncTaskDao;

        DeleteAsyncTask(UserDao dao) {
            mAsyncTaskDao = dao;
        }
        @Override
        protected Void doInBackground(Void... voids) {
            mAsyncTaskDao.deleteAll();
            return null;
        }
    }
}

这个类的作用就是初始化数据库和响应的DAO类,对外提供插入、查询等方法。

注意:数据库的创建,表的插入和删除操作,Room会强制要求在非UI线程中使用,否则会崩溃。

在Activity中初始化UserRepository之后,就可以进行相关的操作了

使用LiveData和ViewModel

当数据变化的时候,LiveData可以观察到数据的变化,可以让我们实时更新UI

ViewModel可以更好的保存Activity中的数据,比如屏幕旋转的时候数据不会丢失

ViewModel与Room和LiveData一起工作可以替换以前的loader。ViewModel确保数据在设备配置更改后仍然存在。当数据库发生更改时,Room会通LiveData,而LiveData反过来又用修改后的数据更的UI。

将DAO中的查询更改为下面

@Query("select * from user")
    LiveData<List<User>> getUserList();

然后创建ViewModel

public class UserViewModel extends AndroidViewModel {
    private LiveData<List<User>> mUsers;
    private UserRepository mRepository;

    public UserViewModel(Application application) {
        super(application);
        mRepository = new UserRepository(application);
        mUsers =  mRepository.getAllUser();
    }

    public LiveData<List<User>> getUsers(){
        return mUsers;
    }

    public void insertUser(User user){
        mRepository.insert(user);
    }

    public void deleteAll(){
        mRepository.deleteAll();
    }
    
    public void update(User user){
        mRepository.update(user);
    }
}

我们单独使用LiveData的时候,都是使用MutableLiveData,当与Room一块使用的时候只能使用LiveData。

Activity中使用

public class RoomActivity extends AppCompatActivity {
    List<User> mUsers = new ArrayList<>();
    UserRepository mRepository;
    MyAdapter mAdapter;
    int index = 0;
    private UserViewModel mViewModel;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_room);
//        mRepository = new UserRepository(getApplication());
//        mUsers = new ArrayList<>();
        RecyclerView recyclerView = findViewById(R.id.recycleview);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(manager);
        mAdapter = new MyAdapter(mUsers,this);
        recyclerView.setAdapter(mAdapter);

        mViewModel = ViewModelProviders.of(this).get(UserViewModel.class);

        mViewModel.getUsers().observe(this, new Observer<List<User>>() {
            @Override
            public void onChanged(List<User> users) {
                mUsers.clear();
                mUsers.addAll(users);
                mAdapter.notifyDataSetChanged();
            }
        });

    }

    public void insert(View view) {
        User user = new User();
        user.id =index;
        user.name = "张三"+ Math.random()*100;
        user.school = "北大"+ Math.random()*100;
        user.password = "123"+ Math.random()*100;
//        mRepository.insert(user);
        mViewModel.insertUser(user);
        index++;
    }

    public void query(View view) {
//        List<User> allUser = mRepository.getAllUser();
//        mUsers.addAll(allUser);
//        mAdapter.notifyDataSetChanged();
    }

    public void deleteAll(View view) {
        mViewModel.deleteAll();
    }
    
    public void update(View view) {
        User user = new User();
        user.id =0;
        user.name = "张三"+ Math.random()*100;
        user.school = "北大"+ Math.random()*100;
        user.password = "123"+ Math.random()*100;
        mViewModel.update(user);
    }
}

数据库升级

当数据库中的表或者表中的字段有变化的时候,我们需要升级数据的版本,这个时候,我们不希望现在数据库中的数据丢失

Room提供了相应的类(Migration)来完成数据库的迁移,需要传入一个旧版本和新版本,比如现在在user表中新加一个age字段

  1. 在User类中新加一个字段
public int age;
  1. 编写Migration类,编写sql更改数据库
  private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            database.execSQL("alter table user add age INTEGER NOT NULL default 0");
        }
    };
  1. 更改数据库的版本由1变成2
@Database(entities = {User.class},version = 2,exportSchema = false)
public abstract class UserRoomDatabase extends RoomDatabase {}
  1. 更改数据库的创建方法
instance = Room.databaseBuilder(context.getApplicationContext(),UserRoomDatabase.class
                   ,"user_database")
                           .addMigrations(MIGRATION_1_2)
                           .build();

然后重新运行程序就可以看到age字段已经加到表里了。

addMigrations方法,里面可以接收多个参数,比如现在只是编写了版本1-2的升级方法MIGRATION_1_2,假如我们还有2-3版本的还可以编写一个MIGRATION_2_3,添加到后面。

更新数据库的版本


OK,到这里Room的简单使用就完成啦。下面来看看它的源码吧

前面我们知道数据库的创建是从Room.databaseBuilder(...).build();方法开始,很明显看出来这是通过建造者模式创建出来的。传入一些参数到RoomDatabase.Builder中,最终的创建方法肯定就是在build中。

      public T build() {
      
           ......

            if (mFactory == null) {
                mFactory = new FrameworkSQLiteOpenHelperFactory();
            }
            //数据库配置
            DatabaseConfiguration configuration =
                    new DatabaseConfiguration(
                            mContext,
                            mName,
                            mFactory,
                            mMigrationContainer,
                            mCallbacks,
                            mAllowMainThreadQueries,
                            mJournalMode.resolve(mContext),
                            mQueryExecutor,
                            mTransactionExecutor,
                            mMultiInstanceInvalidation,
                            mRequireMigration,
                            mAllowDestructiveMigrationOnDowngrade,
                            mMigrationsNotRequiredFrom);
            //前面创建的UserRoomDatabase是个抽象类,编译期间会生成对应的实现类UserRoomDatabase_Impl,获取UserRoomDatabase的实现类
            T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
            //初始化数据库
            db.init(configuration);
            return db;
        }
    }

创建了一个SQLiteOpenHelper的工厂类FrameworkSQLiteOpenHelperFactory

public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
    @Override
    public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
        return new FrameworkSQLiteOpenHelper(
                configuration.context, configuration.name, configuration.callback);
    }
}

这个工厂方法可以创建一个FrameworkSQLiteOpenHelper,它是SupportSQLiteOpenHelper接口的实现类。

class FrameworkSQLiteOpenHelper implements SupportSQLiteOpenHelper {
    private final OpenHelper mDelegate;

    FrameworkSQLiteOpenHelper(Context context, String name,
            Callback callback) {
        mDelegate = createDelegate(context, name, callback);
    }

可以看到在其构造方法中创建了一个代理类OpenHelper

    static class OpenHelper extends SQLiteOpenHelper {......}

OpenHelper继承自系统的SQLiteOpenHelper,它用来监听数据库的创建(onCreate)升级(onUpgrade)等操作。然后回调给RoomOpenHelper来处理。

回到build()方法中,将数据库的配置封装到DatabaseConfiguration中,然后获取我们之前写的抽象类UserRoomDatabase的一个实现类,这个实现类是注解器在编译期间自动创建的。

位置在:build->generated->source->apt->debug->你的包名中找到。最后初始化数据库。怎么创建的可以去查一下编译时注解的原理。

    static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
        //包名
        final String fullPackage = klass.getPackage().getName();
        //全名
        String name = klass.getCanonicalName();
        final String postPackageName = fullPackage.isEmpty()
                ? name
                : (name.substring(fullPackage.length() + 1));
        //拼成UserRoomDatabase_Impl
        final String implName = postPackageName.replace('.', '_') + suffix;
        //noinspection TryWithIdenticalCatches
        try {
        //通过反射找到生成的类,然后实例化
            @SuppressWarnings("unchecked")
            final Class<T> aClass = (Class<T>) Class.forName(
                    fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
            return aClass.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("cannot find implementation for "
                    + klass.getCanonicalName() + ". " + implName + " does not exist");
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot access the constructor"
                    + klass.getCanonicalName());
        } catch (InstantiationException e) {
            throw new RuntimeException("Failed to create an instance of "
                    + klass.getCanonicalName());
        }
    }

上面的方法就是根据我们传入的数据库的类名,拼接出Room编译器给自动生成的实现类的名字,然后通过反射找到这个类并实例化返回。

下面看一下这个自动生成的类UserRoomDatabase_Impl

public final class UserRoomDatabase_Impl extends UserRoomDatabase {
  private volatile UserDao _userDao;

  @Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(2) {
      @Override
      public void createAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`id_` INTEGER NOT NULL, `name` TEXT, `password` TEXT, `school` TEXT, `age` INTEGER NOT NULL, PRIMARY KEY(`id_`))");
        _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
        _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"c59ead1e532b11ea2062c3f5e814a66b\")");
      }

      @Override
      public void dropAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("DROP TABLE IF EXISTS `User`");
      }

      @Override
      protected void onCreate(SupportSQLiteDatabase _db) {
        if (mCallbacks != null) {
          for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
            mCallbacks.get(_i).onCreate(_db);
          }
        }
      }

      @Override
      public void onOpen(SupportSQLiteDatabase _db) {
        mDatabase = _db;
        internalInitInvalidationTracker(_db);
        if (mCallbacks != null) {
          for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
            mCallbacks.get(_i).onOpen(_db);
          }
        }
      }

      @Override
      public void onPreMigrate(SupportSQLiteDatabase _db) {
        DBUtil.dropFtsSyncTriggers(_db);
      }

      @Override
      public void onPostMigrate(SupportSQLiteDatabase _db) {
      }

      @Override
      protected void validateMigration(SupportSQLiteDatabase _db) {
        final HashMap<String, TableInfo.Column> _columnsUser = new HashMap<String, TableInfo.Column>(5);
        _columnsUser.put("id_", new TableInfo.Column("id_", "INTEGER", true, 1));
        _columnsUser.put("name", new TableInfo.Column("name", "TEXT", false, 0));
        _columnsUser.put("password", new TableInfo.Column("password", "TEXT", false, 0));
        _columnsUser.put("school", new TableInfo.Column("school", "TEXT", false, 0));
        _columnsUser.put("age", new TableInfo.Column("age", "INTEGER", true, 0));
        final HashSet<TableInfo.ForeignKey> _foreignKeysUser = new HashSet<TableInfo.ForeignKey>(0);
        final HashSet<TableInfo.Index> _indicesUser = new HashSet<TableInfo.Index>(0);
        final TableInfo _infoUser = new TableInfo("User", _columnsUser, _foreignKeysUser, _indicesUser);
        final TableInfo _existingUser = TableInfo.read(_db, "User");
        if (! _infoUser.equals(_existingUser)) {
          throw new IllegalStateException("Migration didn't properly handle User(com.chs.androiddailytext.jetpack.User).\n"
                  + " Expected:\n" + _infoUser + "\n"
                  + " Found:\n" + _existingUser);
        }
      }
    }, "c59ead1e532b11ea2062c3f5e814a66b", "c0809d515e95fc9eec52ad8880ec6aae");
    final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
        .name(configuration.name)
        .callback(_openCallback)
        .build();
    final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
    return _helper;
  }

  @Override
  protected InvalidationTracker createInvalidationTracker() {
    final HashMap<String, String> _shadowTablesMap = new HashMap<String, String>(0);
    HashMap<String, Set<String>> _viewTables = new HashMap<String, Set<String>>(0);
    return new InvalidationTracker(this, _shadowTablesMap, _viewTables, "User");
  }

  @Override
  public void clearAllTables() {
    super.assertNotMainThread();
    final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase();
    try {
      super.beginTransaction();
      _db.execSQL("DELETE FROM `User`");
      super.setTransactionSuccessful();
    } finally {
      super.endTransaction();
      _db.query("PRAGMA wal_checkpoint(FULL)").close();
      if (!_db.inTransaction()) {
        _db.execSQL("VACUUM");
      }
    }
  }

  @Override
  public UserDao userDao() {
    if (_userDao != null) {
      return _userDao;
    } else {
      synchronized(this) {
        if(_userDao == null) {
          _userDao = new UserDao_Impl(this);
        }
        return _userDao;
      }
    }
  }
}
  • createOpenHelper方法,创建SupportSQLiteOpenHelper的实现类FrameworkSQLiteOpenHelper,前面我们知道他的构造方法中创建了一个代理类OpenHelper它继承自系统的SQLiteOpenHelper,这个就是我们如果不使用Room,而是自己使用系统提供的类操作数据库的时候需要创建的类,这里Room帮我们创建好了,这个方法是在前面build()方法中的 db.init(configuration)中调用的
  • createOpenHelper方法中创建了SupportSQLiteOpenHelper.Callback这个回调,并实现它的回调方法从代码中看到它是一个RoomOpenHelper。
  • OpenHelper监听系统回调,监听到之后会回调RoomOpenHelper的相关方法,RoomOpenHelper又会回调UserRoomDatabase_Impl中的相关方法。
  • onCreate()中Sql语句创建user表和room_master_table表,dropAllTables方法删除数据库,validateMigration方法实现数据库升级
  • userDao()方法创建UserDao_Impl的实例,这个UserDao_Impl也是注解处理器给我们自动生成的,这里面就是我们定义的UserDao中增删改查的真正实现的地方。

然后在看build()方法中的init方法

 public void init(@NonNull DatabaseConfiguration configuration) {
        mOpenHelper = createOpenHelper(configuration);
        boolean wal = false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            wal = configuration.journalMode == JournalMode.WRITE_AHEAD_LOGGING;
            mOpenHelper.setWriteAheadLoggingEnabled(wal);
        }
        mCallbacks = configuration.callbacks;
        mQueryExecutor = configuration.queryExecutor;
        mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
        mAllowMainThreadQueries = configuration.allowMainThreadQueries;
        mWriteAheadLoggingEnabled = wal;
        if (configuration.multiInstanceInvalidation) {
            mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
                    configuration.name);
        }
    }
  • createOpenHelper(configuration)就是调用了UserRoomDatabase_Impl中的createOpenHelper方法。
  • mQueryExecutor 和 mTransactionExecutor 是两个线程池,查询和事物的线程池。这就是为啥前面例子中我们的查询方法不用自己放到非UI线程中执行,而插入和更新方法却需要自己创建子线程执行了。

下面来看看升级的方法,前面我们知道OpenHelper这个代理类继承了系统的SQLiteOpenHelper,会监听系统的数据库相关事件,我们找到它中的升级方法

 public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
            mMigrated = true;
            mCallback.onUpgrade(getWrappedDb(sqLiteDatabase), oldVersion, newVersion);
        }

mCallback就是我们在UserRoomDatabase_Impl中创建的RoomOpenHelper一路传进来的

  public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
        boolean migrated = false;
        if (mConfiguration != null) {
        //根据版本号查找对应的migrations
            List<Migration> migrations = mConfiguration.migrationContainer.findMigrationPath(
                    oldVersion, newVersion);
            if (migrations != null) {
                //迁移之前的初始化工作
                mDelegate.onPreMigrate(db);
                //循环执行我们写的sql
                for (Migration migration : migrations) {
                    migration.migrate(db);
                }
                //验证升级结果
                mDelegate.validateMigration(db);
                mDelegate.onPostMigrate(db);
                updateIdentity(db);
                migrated = true;
            }
        }
        //如果没有执行我们的sql
        if (!migrated) {
            if (mConfiguration != null
                    && !mConfiguration.isMigrationRequired(oldVersion, newVersion)) {
                //删除清空表
                mDelegate.dropAllTables(db);
                mDelegate.createAllTables(db);
            } else {
                throw new IllegalStateException("A migration from " + oldVersion + " to "
                        + newVersion + " was required but not found. Please provide the "
                        + "necessary Migration path via "
                        + "RoomDatabase.Builder.addMigration(Migration ...) or allow for "
                        + "destructive migrations via one of the "
                        + "RoomDatabase.Builder.fallbackToDestructiveMigration* methods.");
            }
        }
    }

这里面的mDelegate就是我们在UserRoomDatabase_Impl中new出来的RoomOpenHelper.Delegate,并实现了它里面的方法,所以上面代码中调用Delegate最终都会到达UserRoomDatabase_Impl中的相关方法执行。

findMigrationPath方法根据版本号找到相应的migrations,前面使用中我们知道migrations中封装了我们升级数据库的sql语句。

循环执行我们的migrations,执行我们写的升级的sql语句,执行完之后验证是否升级成功。

如果没有执行找到需要执行的migrations ,并且mConfiguration.isMigrationRequired(oldVersion, newVersion)为false,就会清空数据库中所有的表。

    public boolean isMigrationRequired(int fromVersion, int toVersion) {
        final boolean isDowngrade = fromVersion > toVersion;
        if (isDowngrade && allowDestructiveMigrationOnDowngrade) {
            return false;
        }
        ......
    }

可以看到当旧版本大于新版本的时候或者allowDestructiveMigrationOnDowngrade为true的时候返回false。

allowDestructiveMigrationOnDowngrade这个标志位可以在数据库创建的时候指定

Room.databaseBuilder(context.getApplicationContext(),UserRoomDatabase.class
                   ,"user_database")
                           .fallbackToDestructiveMigration()
                           .build();

加上它之后升级就会清空数据库中以前的数据。一般情况下我们都是希望保留数据的,所以需要些我们自己的Migration类,定义升级的sql。

OK结束

猜你喜欢

转载自blog.csdn.net/mingyunxiaohai/article/details/89429969