Android GreenDao数据库—升级

Android GreenDao数据库—基础详解 

 

 

 Android GreenDao数据库—高级详解

 

 

本章节讲述GreenDao数据库升级

 

 

在版本迭代时,我们经常需要对数据库进行升级,而GreenDAO默认的DaoMaster.DevOpenHelper在进行数据升级时,会把旧表删除,然后创建新表,并没有迁移旧数据到新表中,从而造成数据丢失。 

 

 

1.代码说明

 

获取DevOpenHelper帮助类

//创建数据库shop.db 创建SQLite数据库的SQLiteOpenHelper的具体实现
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "greendaodemo.db", null);

 

DevOpenHelper 部分源码

    public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name) {
            super(context, name);
        }

        public DevOpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory);
        }

        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
            dropAllTables(db, true);
            onCreate(db);
        }
    }

 

由上述部分源码可以看出 在onUpgrade方法中 默认的DevOpenHelper 类是直接将所有的表都删除了然后重新执行onCreate(db)方法创建新表。这在实际中是不可取的,因为我们在绝大数情况下是需要保留原始数据的。所以需要解决这一问题。

 

 

 

 

2.代码举例

 

版本为1时

greendao {
        // 版本号
        schemaVersion 1
        //greendao输出dao的数据库操作实体类文件夹
        daoPackage 'com.wjn.androiddbdemo.greendao'
        //greenDao实体类包文件夹
        targetGenDir 'src/main/java'
    }

 

 

插入数据+查询数据

package com.wjn.androiddbdemo.activity.greendao;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

import com.wjn.androiddbdemo.MyApplication;
import com.wjn.androiddbdemo.R;
import com.wjn.androiddbdemo.greendao.UserInfo;
import com.wjn.androiddbdemo.greendao.UserInfoDao;
import com.wjn.androiddbdemo.utils.ui.StatusBarUtil;

import org.greenrobot.greendao.query.QueryBuilder;

import java.util.ArrayList;
import java.util.List;

public class GreenDaoUpdateActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView textView1;
    private TextView textView2;
    private TextView textView3;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_greendaoupdate);
        initView();
    }

    /**
     * 初始化各种View
     */

    private void initView() {
        //根据状态栏颜色来决定 状态栏背景 用黑色还是白色 true:是否修改状态栏字体颜色
        StatusBarUtil.setStatusBarMode(this, false, false, R.color.colorPrimary);
        textView1 = findViewById(R.id.activity_greendaoupdate_textview1);
        textView2 = findViewById(R.id.activity_greendaoupdate_textview2);
        textView3 = findViewById(R.id.activity_greendaoupdate_textview3);
        textView = findViewById(R.id.activity_greendaoupdate_textview);
        textView1.setOnClickListener(this);
        textView2.setOnClickListener(this);
        textView3.setOnClickListener(this);
    }

    /**
     * 各种点击事件的方法
     */

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.activity_greendaoupdate_textview1://插入数据
                insertUser();
                break;
            case R.id.activity_greendaoupdate_textview2://查询数据
                List<UserInfo> list=queryUserList();
                StringBuilder sbBuilder = new StringBuilder();
                for(int i=0;i<list.size();i++){
                    UserInfo userInfo=list.get(i);
                    Long id=userInfo.getId();
                    String name=userInfo.getName();
                    String age=userInfo.getAge();
                    sbBuilder.append("ID:" + id + "\n");
                    sbBuilder.append("姓名:" + name + "\n\n");
                    sbBuilder.append("年龄:" + age + "\n\n");
                }
                textView.setText(sbBuilder.toString());
                break;
            case R.id.activity_greendaoupdate_textview3://更新后查询数据
                
                break;
            default:
                break;
        }
    }

    /**
     * 插入多条条记录
     */

    public void insertUser() {
        UserInfo userInfo=new UserInfo();
        userInfo.setName("张三");
        userInfo.setAge("29");

        UserInfo userInfo1=new UserInfo();
        userInfo1.setName("李四");
        userInfo1.setAge("39");

        UserInfo userInfo2=new UserInfo();
        userInfo2.setName("旺旺");
        userInfo2.setAge("19");

        UserInfo userInfo3=new UserInfo();
        userInfo3.setName("王伟");
        userInfo3.setAge("59");

        List<UserInfo> list=new ArrayList<>();
        list.add(userInfo);
        list.add(userInfo1);
        list.add(userInfo2);
        list.add(userInfo3);

        UserInfoDao userInfoDao= MyApplication.getDaoInstant().getUserInfoDao();
        userInfoDao.insertInTx(list);
    }

    /**
     * 查询数据列表 姓名=“张三”
     */

    public List<UserInfo> queryUserList() {
        UserInfoDao userInfoDao= MyApplication.getDaoInstant().getUserInfoDao();
        QueryBuilder<UserInfo> qb = userInfoDao.queryBuilder();
        List<UserInfo> list = qb.list();
        return list;
    }

}

 

 

结果

 

 

版本为2时

greendao {
        // 版本号
        schemaVersion 2
        //greendao输出dao的数据库操作实体类文件夹
        daoPackage 'com.wjn.androiddbdemo.greendao'
        //greenDao实体类包文件夹
        targetGenDir 'src/main/java'
    }

 

 

再次查询数据结果

 

这就证明默认情况下,GreenDao数据库升级时会把原始数据删除。

 

 

 

 

 

解决方案

 

 

解决方案1:自己手动修改实体类和相应的DaoMaster.OpenHelper(DaoMaster.DevOpenHelper)类

 

 

由上可知,在GreenDao数据库升级时,要想保留原始数据要操作DaoMaster.OpenHelper(DaoMaster.DevOpenHelper)类的onUpgrade方法。由于GreenDao数据库的建表是按实体类创建的所以也要修改实体类。

 

 

修改前的实体类

package com.wjn.androiddbdemo.greendao;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;

@Entity
public class UserInfo {

    @Id(autoincrement = true)
    private Long id;//主键 Long型,可以通过@Id(autoincrement = true)设置自增长
    private String name;
    private String age;
    @Generated(hash = 752529704)
    public UserInfo(Long id, String name, String age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Generated(hash = 1279772520)
    public UserInfo() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return this.age;
    }
    public void setAge(String age) {
        this.age = age;
    }

}

 

添加或删除完实体类属性后 将原本自动生成的构造方法以及getter/setter方法删除,重新Build—>Make Project进行生成。

 

修改后的实体类

package com.wjn.androiddbdemo.greendao;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;

@Entity
public class UserInfo {

    @Id(autoincrement = true)
    private Long id;//主键 Long型,可以通过@Id(autoincrement = true)设置自增长
    private String name;
    private String age;
    private String describe;
    @Generated(hash = 819903449)
    public UserInfo(Long id, String name, String age, String describe) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.describe = describe;
    }
    @Generated(hash = 1279772520)
    public UserInfo() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return this.age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public String getDescribe() {
        return this.describe;
    }
    public void setDescribe(String describe) {
        this.describe = describe;
    }

}

 

即 添加了一个describe字段。

 

在表实体中,调整其中的变量(表字段),一般就是新增/删除/修改字段。注意: 
 

新增的字段或修改的字段,其变量类型应使用基础数据类型的包装类,如使用Integer而不是int,避免升级过程中报错。

 

 

自定义DaoMaster.OpenHelper

package com.wjn.androiddbdemo.utils.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;

import com.wjn.androiddbdemo.greendao.DaoMaster;

import org.greenrobot.greendao.database.Database;

public class MyGreenDaoOpenHelper  extends DaoMaster.OpenHelper {

    /**
     * 构造方法
     * */

    public MyGreenDaoOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }

    /**
     * onUpgrade方法
     * 将老表的数据复制到新表
     * */

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        super.onUpgrade(db, oldVersion, newVersion);
        //1.将旧表改名成临时表
        String sql1 = "ALTER TABLE USER_INFO RENAME TO _USER_INFO";
        //2.创建新表
        String sql2=" CREATE TABLE USER_INFO (_id INTEGER PRIMARY KEY AUTOINCREMENT,NAME TEXT,AGE TEXT,DESCRIBE TEXT);";
        //3.将临时表的数据导入到新表 原表中没有的要自己设个默认值
        String sql3="INSERT INTO USER_INFO SELECT _id,NAME,AGE,\"这是描述\" FROM _USER_INFO";
        //删除临时表
        String sql4="DROP TABLE _USER_INFO";
        //执行SQL语句
        db.execSQL(sql1);
        db.execSQL(sql2);
        db.execSQL(sql3);
        db.execSQL(sql4);
    }
}

 

注意:

这里和SQLite数据库升级一样,要判断版本问题(比如从版本1直接到版本3,中间没有版本2的某个实体类)。详见

 

Android SQLite数据库升级 

 

 

修改程序入口初始化Helper类的方式

/**
     * 配置数据库
     */

    private void setupDatabase() {
        //创建数据库shop.db 创建SQLite数据库的SQLiteOpenHelper的具体实现
//        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "greendaodemo.db", null);
        MyGreenDaoOpenHelper helper=new MyGreenDaoOpenHelper(this,"greendaodemo.db",null);
        //获取SQLiteDatabase对象
        SQLiteDatabase db = helper.getReadableDatabase();
        //获取数据库对象
        DaoMaster daoMaster = new DaoMaster(db);
        //获取dao对象管理者
        daoSession = daoMaster.newSession();
    }

 

 

DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "greendaodemo.db", null);

 

换成自定义的

MyGreenDaoOpenHelper helper=new MyGreenDaoOpenHelper(this,"greendaodemo.db",null);

 

 

然后再将版本从1——>2 再次查询

package com.wjn.androiddbdemo.activity.greendao;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

import com.wjn.androiddbdemo.MyApplication;
import com.wjn.androiddbdemo.R;
import com.wjn.androiddbdemo.greendao.UserInfo;
import com.wjn.androiddbdemo.greendao.UserInfoDao;
import com.wjn.androiddbdemo.utils.ui.StatusBarUtil;

import org.greenrobot.greendao.query.QueryBuilder;

import java.util.ArrayList;
import java.util.List;

public class GreenDaoUpdateActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView textView1;
    private TextView textView2;
    private TextView textView3;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_greendaoupdate);
        initView();
    }

    /**
     * 初始化各种View
     */

    private void initView() {
        //根据状态栏颜色来决定 状态栏背景 用黑色还是白色 true:是否修改状态栏字体颜色
        StatusBarUtil.setStatusBarMode(this, false, false, R.color.colorPrimary);
        textView1 = findViewById(R.id.activity_greendaoupdate_textview1);
        textView2 = findViewById(R.id.activity_greendaoupdate_textview2);
        textView3 = findViewById(R.id.activity_greendaoupdate_textview3);
        textView = findViewById(R.id.activity_greendaoupdate_textview);
        textView1.setOnClickListener(this);
        textView2.setOnClickListener(this);
        textView3.setOnClickListener(this);
    }

    /**
     * 各种点击事件的方法
     */

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.activity_greendaoupdate_textview1://插入数据
                insertUser();
                break;
            case R.id.activity_greendaoupdate_textview2://查询数据
                List<UserInfo> list=queryUserList();
                StringBuilder sbBuilder = new StringBuilder();
                for(int i=0;i<list.size();i++){
                    UserInfo userInfo=list.get(i);
                    Long id=userInfo.getId();
                    String name=userInfo.getName();
                    String age=userInfo.getAge();
                    sbBuilder.append("ID:" + id + "\n");
                    sbBuilder.append("姓名:" + name + "\n\n");
                    sbBuilder.append("年龄:" + age + "\n\n");
                }
                textView.setText(sbBuilder.toString());
                break;
            case R.id.activity_greendaoupdate_textview3://更新后查询数据
                List<UserInfo> lists=queryUserList();
                StringBuilder sbBuilders = new StringBuilder();
                for(int i=0;i<lists.size();i++){
                    UserInfo userInfo=lists.get(i);
                    Long id=userInfo.getId();
                    String name=userInfo.getName();
                    String age=userInfo.getAge();
                    String describe=userInfo.getDescribe();
                    sbBuilders.append("ID:" + id + "\n");
                    sbBuilders.append("姓名:" + name + "\n");
                    sbBuilders.append("年龄:" + age + "\n");
                    sbBuilders.append("描述:" + describe + "\n\n");
                }
                textView.setText(sbBuilders.toString());
                break;
            default:
                break;
        }
    }

    /**
     * 插入多条条记录
     */

    public void insertUser() {
        UserInfo userInfo=new UserInfo();
        userInfo.setName("张三");
        userInfo.setAge("29");

        UserInfo userInfo1=new UserInfo();
        userInfo1.setName("李四");
        userInfo1.setAge("39");

        UserInfo userInfo2=new UserInfo();
        userInfo2.setName("旺旺");
        userInfo2.setAge("19");

        UserInfo userInfo3=new UserInfo();
        userInfo3.setName("王伟");
        userInfo3.setAge("59");

        List<UserInfo> list=new ArrayList<>();
        list.add(userInfo);
        list.add(userInfo1);
        list.add(userInfo2);
        list.add(userInfo3);

        UserInfoDao userInfoDao= MyApplication.getDaoInstant().getUserInfoDao();
        userInfoDao.insertInTx(list);
    }

    /**
     * 查询数据列表 姓名=“张三”
     */

    public List<UserInfo> queryUserList() {
        UserInfoDao userInfoDao= MyApplication.getDaoInstant().getUserInfoDao();
        QueryBuilder<UserInfo> qb = userInfoDao.queryBuilder();
        List<UserInfo> list = qb.list();
        return list;
    }

}

 

结果

 

 

 

 

 

 

解决方案2:GitHub上的帮助类

 

Gradle添加依赖

 

根Gradle

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }//GreenDao数据库升级
    }
}

 

APP Gradle

//GreenDao数据库升级
implementation 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.1.0'

 

 

帮助类

package com.wjn.androiddbdemo.utils.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;

import com.github.yuweiguocn.library.greendao.MigrationHelper;
import com.wjn.androiddbdemo.greendao.DaoMaster;
import com.wjn.androiddbdemo.greendao.UserInfoDao;

import org.greenrobot.greendao.database.Database;

public class MyGitHubGreenDaoOpenHelper  extends DaoMaster.OpenHelper{

    /**
     * 构造方法
     * */

    public MyGitHubGreenDaoOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }

    /**
     * onUpgrade方法
     * 使用GitHub MigrationHelper 将老表的数据复制到新表
     * */

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        super.onUpgrade(db, oldVersion, newVersion);
        //把需要管理的数据库表DAO作为最后一个参数传入到方法中
        MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {

            @Override
            public void onCreateAllTables(Database db, boolean ifNotExists) {
                DaoMaster.createAllTables(db, ifNotExists);
            }

            @Override
            public void onDropAllTables(Database db, boolean ifExists) {
                DaoMaster.dropAllTables(db, ifExists);
            }
        },  UserInfoDao.class);
    }
}

注意:

在表实体中,调整其中的变量(表字段),一般就是新增/删除/修改字段。注意: 


1)新增的字段或修改的字段,其变量类型应使用基础数据类型的包装类,如使用Integer而不是int,避免升级过程中报错。

 
2)根据MigrationHelper中的代码,升级后,新增的字段和修改的字段,都会默认被赋予null值。

 

 

APP入口替换

 /**
     * 配置数据库
     */

    private void setupDatabase() {
        //创建数据库shop.db 创建SQLite数据库的SQLiteOpenHelper的具体实现
//        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "greendaodemo.db", null);
//        MyGreenDaoOpenHelper helper=new MyGreenDaoOpenHelper(this,"greendaodemo.db",null);
        MyGitHubGreenDaoOpenHelper helper=new MyGitHubGreenDaoOpenHelper(this,"greendaodemo.db",null);
        //获取SQLiteDatabase对象
        SQLiteDatabase db = helper.getReadableDatabase();
        //获取数据库对象
        DaoMaster daoMaster = new DaoMaster(db);
        //获取dao对象管理者
        daoSession = daoMaster.newSession();
    }

 

 

实体类添加字段

 

将原本自动生成的构造方法以及getter/setter方法删除,重新Build—>Make Project进行生成。

package com.wjn.androiddbdemo.greendao;

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.annotation.Id;

@Entity
public class UserInfo {

    @Id(autoincrement = true)
    private Long id;//主键 Long型,可以通过@Id(autoincrement = true)设置自增长
    private String name;
    private String age;
    private String githubdes;
    @Generated(hash = 466573838)
    public UserInfo(Long id, String name, String age, String githubdes) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.githubdes = githubdes;
    }
    @Generated(hash = 1279772520)
    public UserInfo() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAge() {
        return this.age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public String getGithubdes() {
        return this.githubdes;
    }
    public void setGithubdes(String githubdes) {
        this.githubdes = githubdes;
    }
    
    
}

 

 

 

然后再将版本从1——>2 再次查询

package com.wjn.androiddbdemo.activity.greendao;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

import com.wjn.androiddbdemo.MyApplication;
import com.wjn.androiddbdemo.R;
import com.wjn.androiddbdemo.greendao.UserInfo;
import com.wjn.androiddbdemo.greendao.UserInfoDao;
import com.wjn.androiddbdemo.utils.ui.StatusBarUtil;

import org.greenrobot.greendao.query.QueryBuilder;

import java.util.ArrayList;
import java.util.List;

public class GreenDaoUpdateActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView textView1;
    private TextView textView2;
    private TextView textView3;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_greendaoupdate);
        initView();
    }

    /**
     * 初始化各种View
     */

    private void initView() {
        //根据状态栏颜色来决定 状态栏背景 用黑色还是白色 true:是否修改状态栏字体颜色
        StatusBarUtil.setStatusBarMode(this, false, false, R.color.colorPrimary);
        textView1 = findViewById(R.id.activity_greendaoupdate_textview1);
        textView2 = findViewById(R.id.activity_greendaoupdate_textview2);
        textView3 = findViewById(R.id.activity_greendaoupdate_textview3);
        textView = findViewById(R.id.activity_greendaoupdate_textview);
        textView1.setOnClickListener(this);
        textView2.setOnClickListener(this);
        textView3.setOnClickListener(this);
    }

    /**
     * 各种点击事件的方法
     */

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.activity_greendaoupdate_textview1://插入数据
                insertUser();
                break;
            case R.id.activity_greendaoupdate_textview2://查询数据
                List<UserInfo> list=queryUserList();
                StringBuilder sbBuilder = new StringBuilder();
                for(int i=0;i<list.size();i++){
                    UserInfo userInfo=list.get(i);
                    Long id=userInfo.getId();
                    String name=userInfo.getName();
                    String age=userInfo.getAge();
                    sbBuilder.append("ID:" + id + "\n");
                    sbBuilder.append("姓名:" + name + "\n\n");
                    sbBuilder.append("年龄:" + age + "\n\n");
                }
                textView.setText(sbBuilder.toString());
                break;
            case R.id.activity_greendaoupdate_textview3://更新后查询数据
                List<UserInfo> lists=queryUserList();
                StringBuilder sbBuilders = new StringBuilder();
                for(int i=0;i<lists.size();i++){
                    UserInfo userInfo=lists.get(i);
                    Long id=userInfo.getId();
                    String name=userInfo.getName();
                    String age=userInfo.getAge();
                    String describe=userInfo.getGithubdes();
                    sbBuilders.append("ID:" + id + "\n");
                    sbBuilders.append("姓名:" + name + "\n");
                    sbBuilders.append("年龄:" + age + "\n");
                    sbBuilders.append("GitHub描述:" + describe + "\n\n");
                }
                textView.setText(sbBuilders.toString());
                break;
            default:
                break;
        }
    }

    /**
     * 插入多条条记录
     */

    public void insertUser() {
        UserInfo userInfo=new UserInfo();
        userInfo.setName("张三");
        userInfo.setAge("29");

        UserInfo userInfo1=new UserInfo();
        userInfo1.setName("李四");
        userInfo1.setAge("39");

        UserInfo userInfo2=new UserInfo();
        userInfo2.setName("旺旺");
        userInfo2.setAge("19");

        UserInfo userInfo3=new UserInfo();
        userInfo3.setName("王伟");
        userInfo3.setAge("59");

        List<UserInfo> list=new ArrayList<>();
        list.add(userInfo);
        list.add(userInfo1);
        list.add(userInfo2);
        list.add(userInfo3);

        UserInfoDao userInfoDao= MyApplication.getDaoInstant().getUserInfoDao();
        userInfoDao.insertInTx(list);
    }

    /**
     * 查询数据列表 姓名=“张三”
     */

    public List<UserInfo> queryUserList() {
        UserInfoDao userInfoDao= MyApplication.getDaoInstant().getUserInfoDao();
        QueryBuilder<UserInfo> qb = userInfoDao.queryBuilder();
        List<UserInfo> list = qb.list();
        return list;
    }

}

 

结果

 

 

 

 

MigrationHelper 源码解析

 

由上可知 在onUpgrade方法中调用了MigrationHelper.migrate()方法

MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {

            @Override
            public void onCreateAllTables(Database db, boolean ifNotExists) {
                DaoMaster.createAllTables(db, ifNotExists);
            }

            @Override
            public void onDropAllTables(Database db, boolean ifExists) {
                DaoMaster.dropAllTables(db, ifExists);
            }
        },  UserInfoDao.class);

 

 

也就是说 升级数据库的操作是在这个方法中。

 

    public static void migrate(Database database, ReCreateAllTableListener listener, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        weakListener = new WeakReference<>(listener);
        migrate(database, daoClasses);
    }

 

public static void migrate(Database database, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        printLog("【Generate temp table】start");
        generateTempTables(database, daoClasses);
        printLog("【Generate temp table】complete");

        ReCreateAllTableListener listener = null;
        if (weakListener != null) {
            listener = weakListener.get();
        }

        if (listener != null) {
            listener.onDropAllTables(database, true);
            printLog("【Drop all table by listener】");
            listener.onCreateAllTables(database, false);
            printLog("【Create all table by listener】");
        } else {
            dropAllTables(database, true, daoClasses);
            createAllTables(database, false, daoClasses);
        }
        printLog("【Restore data】start");
        restoreData(database, daoClasses);
        printLog("【Restore data】complete");
    }

 

 

private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            String tempTableName = null;

            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
            String tableName = daoConfig.tablename;
            if (!isTableExists(db, false, tableName)) {
                printLog("【New Table】" + tableName);
                continue;
            }
            try {
                tempTableName = daoConfig.tablename.concat("_TEMP");
                StringBuilder dropTableStringBuilder = new StringBuilder();
                dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");
                db.execSQL(dropTableStringBuilder.toString());

                StringBuilder insertTableStringBuilder = new StringBuilder();
                insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);
                insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
                db.execSQL(insertTableStringBuilder.toString());
                printLog("【Table】" + tableName +"\n ---Columns-->"+getColumnsStr(daoConfig));
                printLog("【Generate temp table】" + tempTableName);
            } catch (SQLException e) {
                Log.e(TAG, "【Failed to generate temp table】" + tempTableName, e);
            }
        }
    }

 

private static void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");

            if (!isTableExists(db, true, tempTableName)) {
                continue;
            }

            try {
                // get all columns from tempTable, take careful to use the columns list
                List<TableInfo> newTableInfos = TableInfo.getTableInfo(db, tableName);
                List<TableInfo> tempTableInfos = TableInfo.getTableInfo(db, tempTableName);
                ArrayList<String> selectColumns = new ArrayList<>(newTableInfos.size());
                ArrayList<String> intoColumns = new ArrayList<>(newTableInfos.size());
                for (TableInfo tableInfo : tempTableInfos) {
                    if (newTableInfos.contains(tableInfo)) {
                        String column = '`' + tableInfo.name + '`';
                        intoColumns.add(column);
                        selectColumns.add(column);
                    }
                }
                // NOT NULL columns list
                for (TableInfo tableInfo : newTableInfos) {
                    if (tableInfo.notnull && !tempTableInfos.contains(tableInfo)) {
                        String column = '`' + tableInfo.name + '`';
                        intoColumns.add(column);

                        String value;
                        if (tableInfo.dfltValue != null) {
                            value = "'" + tableInfo.dfltValue + "' AS ";
                        } else {
                            value = "'' AS ";
                        }
                        selectColumns.add(value + column);
                    }
                }

                if (intoColumns.size() != 0) {
                    StringBuilder insertTableStringBuilder = new StringBuilder();
                    insertTableStringBuilder.append("REPLACE INTO ").append(tableName).append(" (");
                    insertTableStringBuilder.append(TextUtils.join(",", intoColumns));
                    insertTableStringBuilder.append(") SELECT ");
                    insertTableStringBuilder.append(TextUtils.join(",", selectColumns));
                    insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
                    db.execSQL(insertTableStringBuilder.toString());
                    printLog("【Restore data】 to " + tableName);
                }
                StringBuilder dropTableStringBuilder = new StringBuilder();
                dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
                db.execSQL(dropTableStringBuilder.toString());
                printLog("【Drop temp table】" + tempTableName);
            } catch (SQLException e) {
                Log.e(TAG, "【Failed to restore data from temp table 】" + tempTableName, e);
            }
        }
    }

 

可以看出,其实它也是创建临时表,然后创建新表,再将临时表中数据拷贝到新表。最后删除临时表。

 

 

代码链接:https://github.com/wujianning/AndroidDBDemo

 

 

附:

GitHub链接:

https://github.com/yuweiguocn/GreenDaoUpgradeHelper

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章