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

 

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