GreenDAO數(shù)據(jù)庫(kù)的使用 - 多表關(guān)聯(lián)

GreenDAO的使用:

1.在project的build.gradle里邊添加:

buildscript {
    repositories {
        maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
        maven { url 'https://jitpack.io' }
        // greenDao數(shù)據(jù)庫(kù)
        mavenCentral()
        google()
        jcenter()
    }
    dependencies {
        //Gradle編譯版本
        classpath "com.android.tools.build:gradle:4.1.2"
        // greenDao數(shù)據(jù)庫(kù)
        classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
    }
}

allprojects {
    repositories {
        // 阿里云云效倉(cāng)庫(kù)菇民,提升編譯速度
        maven { url 'https://maven.aliyun.com/repository/public' }
        maven { url 'https://maven.aliyun.com/repository/jcenter' }
        maven { url 'https://maven.aliyun.com/repository/central' }
        maven { url 'https://maven.aliyun.com/repository/google' }
        maven { url 'https://jitpack.io' }
        jcenter()
        google()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

在app模塊的build.gradle里的android{}節(jié)點(diǎn)里添加:

    greendao {
        schemaVersion 1                     // 數(shù)據(jù)庫(kù)版本號(hào)
        daoPackage 'com.my.dao.greendao'    // 設(shè)置DaoMaster、DaoSession、Dao包名痢缎,也就是要放置這些類的包的全路徑叹侄。
        targetGenDir 'src/main/java'        // 設(shè)置 DaoMaster梁只、DaoSession绝淡、Dao 目錄
        //generateTests false //設(shè)置為true以自動(dòng)生成單元測(cè)試昌抠。
       //targetGenDirTests 'src/main/java' //應(yīng)存儲(chǔ)生成的單元測(cè)試的基本目錄。默認(rèn)為 src / androidTest / java轨功。
    }

dependencies{}節(jié)點(diǎn)里邊添加

    // greenDao數(shù)據(jù)庫(kù)
    implementation 'org.greenrobot:greendao:3.3.0'

3.創(chuàng)建實(shí)體對(duì)象類

@Entity
public class TestIdCard {
    @Id
    private Long cardId;
    private String cardNo;
}

@Entity
public class TestPerson {
    @Id(autoincrement = true)
    private Long personId;
    private String personName;
    @ToOne(joinProperty = "personId")
    private TestIdCard testIdCard;
}

這個(gè)類用不著為了看他的注解方法 忽略這個(gè)

//@Entity
//public class Student {
//    //@Id:主鍵旭斥,通過(guò)這個(gè)注解標(biāo)記的字段必須是Long類型的,這個(gè)字段在數(shù)據(jù)庫(kù)中表示它就是主鍵古涧,并且它默認(rèn)就是自增的
//    @Id(autoincrement = true)
//    private long id;
//
//    //姓名
//    @NotNull // @NotNull 設(shè)置數(shù)據(jù)庫(kù)表當(dāng)前列不能為空
//    @Unique  //唯一
//    private String name;
//
//    //年齡
//    //@Property:設(shè)置一個(gè)非默認(rèn)關(guān)系映射所對(duì)應(yīng)的列名垂券,默認(rèn)是使用字段名,例如:@Property(nameInDb = "name")
//    @Property(nameInDb = "userage")
//    private int age;
//}

4.點(diǎn)擊Android Studio窗口上的Build , Make Project會(huì)自動(dòng)生成數(shù)據(jù)庫(kù)相應(yīng)的表和實(shí)體類中的方法羡滑,注意實(shí)體類都必須用@Entity注解修飾菇爪,主鍵用@Id

此處只記錄下GreenDAO的多表關(guān)聯(lián),其他的介紹可自行前往了解

必要的一步啄栓,在Application中初始化數(shù)據(jù)庫(kù):

import android.app.Application;
import android.database.sqlite.SQLiteDatabase;

import com.my.dao.greendao.DaoMaster;
import com.my.dao.greendao.DaoSession;

/**
 * Author : i小灰
 * time   : 2021/3/11 14:52
 * desc   : 程序入口
 */
public class App extends Application {

    //數(shù)據(jù)庫(kù)名稱
    private static final String DB_NAME = "hui_dao.db";
    private static DaoSession mDaoSession;

    //單例模式-懶漢式
    private static App app;

    public App() {
        App.app = this;
    }

    public static App getInstance() {
        if (app == null) {
            //雙重校驗(yàn)鎖
            synchronized (App.class) {
                if (app == null) {
                    app = new App();
                }
            }
        }
        return app;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化GreenDao數(shù)據(jù)庫(kù)
        initGreenDao();
    }

    private void initGreenDao() {
        //生成數(shù)據(jù)庫(kù)文件娄帖,名為 students-db
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, DB_NAME, null);
        //獲取可寫(xiě)數(shù)據(jù)庫(kù)
        SQLiteDatabase db = helper.getWritableDatabase();
        //建立特定模式下的所有的 DAO 對(duì)象和數(shù)據(jù) db 對(duì)象的映射
        DaoMaster daoMaster = new DaoMaster(db);
        //管理特定模式下的所有 DAO 對(duì)象尿褪,并提供一些通用的 CRUD 持久化方法
        mDaoSession = daoMaster.newSession();
    }

    //供外部調(diào)用操作數(shù)據(jù)庫(kù)
    public static DaoSession getmDaoSession() {
        return mDaoSession;
    }
}

這里補(bǔ)充插入一點(diǎn):關(guān)聯(lián)表查詢舔株,在不設(shè)置的前提下缀蹄,在主動(dòng)端關(guān)聯(lián)查詢允粤,可以查詢到被動(dòng)端的數(shù)據(jù)孽拷。而從被動(dòng)端查詢是不知道自己屬于誰(shuí)和誰(shuí)有關(guān)聯(lián)的音五。
一對(duì)一 例如:一個(gè)人對(duì)應(yīng)一張身份證料滥,關(guān)聯(lián)時(shí)會(huì)將這個(gè)人的ID設(shè)置給這張身份證徘跪,那么這個(gè)人是主動(dòng)端淳梦,身份證是被動(dòng)端析砸。這個(gè)人是知道哪張身份證屬于自己,而被動(dòng)端的身份證是不知道自己屬于哪個(gè)人爆袍。
一對(duì)多 例如:一個(gè)老師有多個(gè)學(xué)生首繁,如果我們不給學(xué)生設(shè)置他對(duì)應(yīng)哪個(gè)老師的情況下。外鍵是設(shè)置在學(xué)生這個(gè)多 的一方(被動(dòng)的一方)陨囊,從老師端查詢弦疮,可以查詢到他擁有的學(xué)生數(shù)據(jù),而從學(xué)生端查詢是無(wú)法知道他對(duì)應(yīng)的老師的信息的蜘醋,只有給學(xué)生設(shè)置了對(duì)應(yīng)的老師的信息胁塞,那么這個(gè)學(xué)生才知道自己數(shù)據(jù)哪個(gè)老師。

結(jié)論:在不給擁有外鍵這個(gè)多的一方設(shè)置的情況下压语,從擁有外鍵的一方關(guān)聯(lián)查詢只能查詢到自己的信息啸罢,而從多的一方是可以查詢到擁有外鍵這方的信息。(主動(dòng)方將自己的主鍵發(fā)給被動(dòng)方持有胎食,雙方由此產(chǎn)生關(guān)聯(lián))

一對(duì)一

一對(duì)一實(shí)際上是表A中一項(xiàng)元素的主鍵等于表B中一項(xiàng)元素的主鍵扰才,那么這兩個(gè)元素有一對(duì)一的關(guān)聯(lián)。一對(duì)一要用到@ToOne注解斥季,例如一個(gè)人只有一張身份證
用到的實(shí)體類就是上面的兩個(gè)

測(cè)試:

private void oneToOne() {
        //存儲(chǔ)數(shù)據(jù)
        TestPerson testPerson = new TestPerson();
        testPerson.setPersonName("i小灰");
        App.getmDaoSession().insert(testPerson);

        TestIdCard testIdCard = new TestIdCard();
        testIdCard.setCardId(testPerson.getPersonId());
        testIdCard.setCardNo("12334555");
        App.getmDaoSession().insert(testIdCard);
        //查詢數(shù)據(jù)
        List<TestPerson> testPeople = App.getmDaoSession().queryAll(TestPerson.class);
        for (TestPerson person : testPeople) {
            LogUtils.d("person  :" + person.toString());
            TestIdCard testIdCard1 = person.getTestIdCard();
            LogUtils.d("card: " + testIdCard1.toString());
        }
    }

一對(duì)多

一對(duì)多實(shí)際上是在多的一方的表中創(chuàng)建一個(gè)外鍵训桶,如果多的一方表中的某個(gè)元素的外鍵等于一的一方表中的主鍵累驮,那么多的一方表中的這個(gè)元素就跟一的一方表中的這個(gè)元素產(chǎn)生了一對(duì)多的關(guān)聯(lián)酣倾。一對(duì)多要用到@ToMany注解舵揭,例如一個(gè)用戶可以擁有多張信用卡
新建實(shí)體類:

TestUser
@Entity
public class TestUser {

    @Id(autoincrement = true)
    private Long userId;

    private String userName;
    private String age;

    //一對(duì)多
    @ToMany(referencedJoinProperty = "credit_user_id")
    private List<TestCreditCard> creditCards;
}

TestCreditCard
@Entity
public class TestCreditCard {
    //主鍵必須是唯一的
    @Id
    private Long cId;

    private String creditCardNo;
    private String creditCardName;

    //外鍵必須是唯一的
    @Unique
    private Long credit_user_id;
}

同樣記得build Make Project

測(cè)試

 /**
     * 一對(duì)多
     * user and creditCard
     */
    private void oneToMany() {
        //存儲(chǔ)數(shù)據(jù)
        TestUser testUser = new TestUser();
        testUser.setUserName("狗哥");
        testUser.setAge("23");
        App.getmDaoSession().insert(testUser);
        for (int i = 3; i < 6; i++) {
            TestCreditCard testCreditCard = new TestCreditCard();
            testCreditCard.setCId((i + 1L));
            testCreditCard.setCredit_user_id((i + 1L));
            testCreditCard.setCreditCardName("招商信用卡_"+i);
            testCreditCard.setCreditCardNo("12345678");
            App.getmDaoSession().insert(testCreditCard);
        }
        //查詢數(shù)據(jù)
        List<TestUser> testUsers = App.getmDaoSession().queryAll(TestUser.class);
        for (TestUser user : testUsers) {
            LogUtils.d("用戶:" + user.toString());
            List<TestCreditCard> creditCards = user.getCreditCards();
            for (TestCreditCard creditCard : creditCards) {
                LogUtils.d("信用卡:" + creditCard.toString());
            }
        }
    }

多對(duì)多

多對(duì)多創(chuàng)建中間表,在中間表中分別有兩個(gè)外鍵指向一的一方躁锡。如果中間表中的某個(gè)元素的外鍵等于一的一方中某個(gè)元素的主鍵午绳,那么這兩項(xiàng)元素就產(chǎn)生了一對(duì)多的關(guān)聯(lián)。中間表分別指向兩邊產(chǎn)生一對(duì)多的關(guān)聯(lián)映之,那么兩邊就產(chǎn)生了多對(duì)多的關(guān)聯(lián)拦焚。多對(duì)多的關(guān)系其實(shí)也就類似于MySQL中的多對(duì)多關(guān)系,通過(guò)建立中間表來(lái)分解為一對(duì)多的關(guān)系杠输。我們以student和teacher為例赎败,下面創(chuàng)建兩個(gè)實(shí)體類

@Entity
public class Student {
    @Id(autoincrement = true)
    Long studentId;
    String studentName;
    @ToMany
    @JoinEntity(entity = StudentAndTeacherBean.class, sourceProperty = "sId", targetProperty = "tId")
    List<Teacher> teacherList;
...setter and getter
}

@Entity
public class Teacher {
    @Id(autoincrement = true)
    Long teacherId;
    String teacherName;
    @ToMany
    @JoinEntity(entity = StudentAndTeacherBean.class, sourceProperty = "tId", targetProperty = "sId")
    List<Student> studentList;
...setter and getter
}

build Make Project后,我們來(lái)進(jìn)行測(cè)試蠢甲。分為兩個(gè)方法:insertDataToMany是創(chuàng)建并保存數(shù)據(jù)僵刮;
queryDataToMany是分別從學(xué)生的角度查詢關(guān)聯(lián)的老師和從老師的角度查詢關(guān)聯(lián)的學(xué)生

測(cè)試

 /**
     * 多對(duì)多
     */
    public void insertDataToMany(View view) {
        for (int i = 0; i < 2; i++) {
            if (i == 0) {
                Teacher teacher = new Teacher();
                teacher.setTeacherName("李老師");
                long insert = App.getmDaoSession().insert(teacher);
                Log.i(TAG, "老師存儲(chǔ)的結(jié)果:" + insert);
                for (int i1 = 0; i1 < 2; i1++) {
                    if (i1 == 0) {
                        Student student = new Student();
                        student.setStudentName("大同學(xué)1號(hào)");
                        long insertStudent = App.getmDaoSession().insert(student);
                        Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
                    }else if (i1==1){}
                    if (i1 == 0) {
                        Student student = new Student();
                        student.setStudentName("大同學(xué)2號(hào)");
                        long insertStudent = App.getmDaoSession().insert(student);
                        Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
                    }
                }
            } else if (i == 1) {
                Teacher teacher = new Teacher();
                teacher.setTeacherName("劉老師");
                long insert = App.getmDaoSession().insert(teacher);
                Log.i(TAG, "老師存儲(chǔ)的結(jié)果:" + insert);
                for (int i1 = 0; i1 < 2; i1++) {
                    if (i1 == 0) {
                        Student student = new Student();
                        student.setStudentName("小同學(xué)1號(hào)");
                        long insertStudent = App.getmDaoSession().insert(student);
                        Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
                    }else if (i1==1){
                        Student student = new Student();
                        student.setStudentName("小同學(xué)2號(hào)");
                        long insertStudent = App.getmDaoSession().insert(student);
                        Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
                    }
                }
            }
        }
    }

/**
     * 查詢數(shù)據(jù)
     */
    public void queryDataToMany(View view) {
        List<Teacher> teachers = App.getmDaoSession().loadAll(Teacher.class);
        Log.i(TAG, "所有老師的數(shù)量:" + teachers.size());
        for (Teacher teacher : teachers) {
            Log.i(TAG, "老師:" + teacher.toString());
            List<Student> studentList = teacher.getStudentList();
            Log.i(TAG, "學(xué)生的數(shù)量:" + studentList.size());
            for (Student student : studentList) {
                Log.i(TAG,"學(xué)生的信息:"+student.toString());
            }
        }

        List<Student> students = App.getmDaoSession().loadAll(Student.class);
        Log.i(TAG, "所有學(xué)生的數(shù)量:" + students.size());
        for (Student student : students) {
            Log.i(TAG, "學(xué)生:" + student.toString());
            List<Teacher> teacherList = student.getTeacherList();
            Log.i(TAG, "老師的數(shù)量:" + teacherList.size());
            for (Teacher teacher : teacherList) {
                Log.i(TAG,"老師的信息:"+teacher.toString());
            }
        }
    }

數(shù)據(jù)庫(kù)的升級(jí)更新:文章參考http://www.reibang.com/p/53083f782ea2,在此感謝分享!

我們自定義一個(gè)類繼承自O(shè)penHelper

public class MyDaoMaster extends DaoMaster.OpenHelper {
    private static final String TAG = "MyDaoMaster";

    public MyDaoMaster(Context context, String name) {
        super(context, name);
    }

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

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        super.onUpgrade(db, oldVersion, newVersion);
        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);
            }
        },YouThingDao.class);
        Log.e(TAG, "onUpgrade: " + oldVersion + " newVersion = " + newVersion);
    }
}

在MyMasterDao中YouThingDao.class是你項(xiàng)目中的Dao數(shù)據(jù)類,這里可以傳一個(gè)Dao類的數(shù)組

/**
 * 數(shù)據(jù)庫(kù)升級(jí)的思路:
 *    1.先創(chuàng)建當(dāng)前已存在的所有表鹦牛,對(duì)應(yīng)臨時(shí)的表
 *    2.然后刪除當(dāng)前已存在的所有的表
 *    3.創(chuàng)建數(shù)據(jù)庫(kù)升級(jí)后的所有新的表
 *
 */
public final class MigrationHelper {

    public static boolean DEBUG = false;
    private static String TAG = "MigrationHelper";
    private static final String SQLITE_MASTER = "sqlite_master";
    private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master";

    /**
     * 重新創(chuàng)建所有表格監(jiān)聽(tīng)的弱引用
     */
    private static WeakReference<ReCreateAllTableListener> weakListener;

    /**
     * 重新創(chuàng)建所有表格的監(jiān)聽(tīng)
     */
    public interface ReCreateAllTableListener{
        void onCreateAllTables(Database db, boolean ifNotExists);
        void onDropAllTables(Database db, boolean ifExists);
    }

    /**
     * 遷移
     * @param db
     * @param daoClasses
     */
    public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        //舊的數(shù)據(jù)庫(kù)版本
        printLog("【The Old Database Version】" + db.getVersion());
        Database database = new StandardDatabase(db);
        migrate(database, daoClasses);
    }

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

    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);//生成臨時(shí)的表
        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);
        }
        //回復(fù)原來(lái)表的數(shù)據(jù)
        printLog("【Restore data】start");
        restoreData(database, daoClasses);
        printLog("【Restore data】complete");
    }

    /**
     * 聲場(chǎng)新的表
     */
    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);
            }
        }
    }

    /**
     *
     * @param db
     * @param isTemp
     * @param tableName
     * @return
     */
    private static boolean isTableExists(Database db, boolean isTemp, String tableName) {
        if (db == null || TextUtils.isEmpty(tableName)) {
            return false;
        }
        String dbName = isTemp ? SQLITE_TEMP_MASTER : SQLITE_MASTER;
        String sql = "SELECT COUNT(*) FROM " + dbName + " WHERE type = ? AND name = ?";
        Cursor cursor=null;
        int count = 0;
        try {
            cursor = db.rawQuery(sql, new String[]{"table", tableName});
            if (cursor == null || !cursor.moveToFirst()) {
                return false;
            }
            count = cursor.getInt(0);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return count > 0;
    }

    private static String getColumnsStr(DaoConfig daoConfig) {
        if (daoConfig == null) {
            return "no columns";
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < daoConfig.allColumns.length; i++) {
            builder.append(daoConfig.allColumns[i]);
            builder.append(",");
        }
        if (builder.length() > 0) {
            builder.deleteCharAt(builder.length() - 1);
        }
        return builder.toString();
    }

    private static void dropAllTables(Database db, boolean ifExists,
                                      @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        reflectMethod(db, "dropTable", ifExists, daoClasses);
        printLog("【Drop all table by reflect】");
    }

    private static void createAllTables(Database db, boolean ifNotExists,
                                        @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        reflectMethod(db, "createTable", ifNotExists, daoClasses);
        printLog("【Create all table by reflect】");
    }

    /**
     * dao class already define the sql exec method, so just invoke it
     */
    private static void reflectMethod(Database db, String methodName, boolean isExists,
                                      @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
        if (daoClasses.length < 1) {
            return;
        }
        try {
            for (Class cls : daoClasses) {
                Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
                method.invoke(null, db, isExists);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 恢復(fù)數(shù)據(jù)
     * @param db
     * @param daoClasses
     */
    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);
            }
        }
    }

    private static List<String> getColumns(Database db, String tableName) {
        List<String> columns = null;
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
            if (null != cursor && cursor.getColumnCount() > 0) {
                columns = Arrays.asList(cursor.getColumnNames());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
            if (null == columns)
                columns = new ArrayList<>();
        }
        return columns;
    }

    private static void printLog(String info){
        if(DEBUG){
            Log.d(TAG, info);
        }
    }

    private static class TableInfo {
        int cid;
        String name;
        String type;
        boolean notnull;
        String dfltValue;
        boolean pk;

        @Override
        public boolean equals(Object o) {
            return this == o
                    || o != null
                    && getClass() == o.getClass()
                    && name.equals(((TableInfo) o).name);
        }

        @Override
        public String toString() {
            return "TableInfo{" +
                    "cid=" + cid +
                    ", name='" + name + '\'' +
                    ", type='" + type + '\'' +
                    ", notnull=" + notnull +
                    ", dfltValue='" + dfltValue + '\'' +
                    ", pk=" + pk +
                    '}';
        }

        private static List<TableInfo> getTableInfo(Database db, String tableName) {
            String sql = "PRAGMA table_info(" + tableName + ")";
            printLog(sql);
            Cursor cursor = db.rawQuery(sql, null);
            if (cursor == null)
                return new ArrayList<>();
            TableInfo tableInfo;
            List<TableInfo> tableInfos = new ArrayList<>();
            while (cursor.moveToNext()) {
                tableInfo = new TableInfo();
                tableInfo.cid = cursor.getInt(0);
                tableInfo.name = cursor.getString(1);
                tableInfo.type = cursor.getString(2);
                tableInfo.notnull = cursor.getInt(3) == 1;
                tableInfo.dfltValue = cursor.getString(4);
                tableInfo.pk = cursor.getInt(5) == 1;
                tableInfos.add(tableInfo);
                // printLog(tableName + ":" + tableInfo);
            }
            cursor.close();
            return tableInfos;
        }
    }
}

數(shù)據(jù)庫(kù)的文件有變動(dòng)搞糕,記得修改build.gradle中的數(shù)據(jù)庫(kù)版本號(hào),當(dāng)新的數(shù)據(jù)庫(kù)版本號(hào)大于舊的版本號(hào)時(shí)才會(huì)進(jìn)行數(shù)據(jù)庫(kù)的更新升級(jí)!

多表關(guān)聯(lián):一對(duì)多和多對(duì)多時(shí)曼追,把一的一方的外鍵給多的一方存儲(chǔ);一對(duì)一中窍仰,把被動(dòng)一方的外鍵給主動(dòng)一方存儲(chǔ)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市礼殊,隨后出現(xiàn)的幾起案子驹吮,更是在濱河造成了極大的恐慌,老刑警劉巖晶伦,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碟狞,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡坝辫,警方通過(guò)查閱死者的電腦和手機(jī)篷就,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)近忙,“玉大人竭业,你說(shuō)我怎么就攤上這事〖吧幔” “怎么了未辆?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)锯玛。 經(jīng)常有香客問(wèn)我咐柜,道長(zhǎng)兼蜈,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任拙友,我火速辦了婚禮为狸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘遗契。我一直安慰自己辐棒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布牍蜂。 她就那樣靜靜地躺著漾根,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鲫竞。 梳的紋絲不亂的頭發(fā)上辐怕,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音从绘,去河邊找鬼寄疏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛顶考,可吹牛的內(nèi)容都是我干的赁还。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼驹沿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼艘策!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起渊季,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤朋蔫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后却汉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體驯妄,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年合砂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了青扔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翩伪,死狀恐怖微猖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缘屹,我是刑警寧澤凛剥,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站轻姿,受9級(jí)特大地震影響犁珠,放射性物質(zhì)發(fā)生泄漏逻炊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一犁享、第九天 我趴在偏房一處隱蔽的房頂上張望余素。 院中可真熱鬧,春花似錦饼疙、人聲如沸溺森。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至医窿,卻和暖如春磅甩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姥卢。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工卷要, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人独榴。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓僧叉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親棺榔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓶堕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容