GreenDAO多表关联

GreenDAO的使用:

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

 repositories {
        google()
        jcenter()
        mavenCentral() // add repository
    }

   dependencies {
        classpath 'com.android.tools.build:gradle:3.5.1'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

2.在module的build.gradle里边添加

apply plugin: 'org.greenrobot.greendao' // apply plugin

android{}节点里边添加

greendao {
        schemaVersion 7 //数据库版本号
        daoPackage 'com.baselibrary.dao.db'
        // 设置DaoMaster、DaoSession、Dao 包名
        targetGenDir 'src/main/java'//设置DaoMaster、DaoSession、Dao目录,请注意,这里路径用/不要用.
        generateTests false //设置为true以自动生成单元测试。
        targetGenDirTests 'src/main/java' //应存储生成的单元测试的基本目录。默认为 src / androidTest / java。
    }

dependencies{}节点里边添加

implementation "org.greenrobot:greendao:3.2.2"

3.创建实体对象类

@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;
}

4.点击Android Studio窗口上的Build , Make Project会自动生成数据库相应的表和实体类中的方法,注意实体类都必须用@Entity注解修饰,主键用@Id

此处只记录下GreenDAO的多表关联,其他的介绍可自行前往了解

必要的一步,在Application中初始化数据库:

 /**
     * 初始化GreenDao,直接在Application中进行初始化操作
     */
    private void initGreenDao() {
       DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "test_dao.db");
//        MyDaoMaster helper=new MyDaoMaster(this, "test_dao.db");
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
    }

    private DaoSession daoSession;

    public DaoSession getDaoSession() {
        return daoSession;
    }

这里补充插入一点:关联表查询,在不设置的前提下,在主动端关联查询,可以查询到被动端的数据。而从被动端查询是不知道自己属于谁和谁有关联的。
一对一 例如:一个人对应一张身份证,关联时会将这个人的ID设置给这张身份证,那么这个人是主动端,身份证是被动端。这个人是知道哪张身份证属于自己,而被动端的身份证是不知道自己属于哪个人。
一对多 例如:一个老师有多个学生,如果我们不给学生设置他对应哪个老师的情况下。外键是设置在学生这个多 的一方(被动的一方),从老师端查询,可以查询到他拥有的学生数据,而从学生端查询是无法知道他对应的老师的信息的,只有给学生设置了对应的老师的信息,那么这个学生才知道自己数据哪个老师。

结论:在不给拥有外键这个多的一方设置的情况下,从拥有外键的一方关联查询只能查询到自己的信息,而从多的一方是可以查询到拥有外键这方的信息。(主动方将自己的主键发给被动方持有,双方由此产生关联)

一对一

一对一实际上是表A中一项元素的主键等于表B中一项元素的主键,那么这两个元素有一对一的关联。一对一要用到@ToOne注解,例如一个人只有一张身份证
用到的实体类就是上面的两个

测试:

private void oneToOne() {
        //存储数据
        TestPerson testPerson = new TestPerson();
        testPerson.setPersonName("李晓明");
        BaseApplication.getDbUtil().insert(testPerson);

        TestIdCard testIdCard = new TestIdCard();
        testIdCard.setCardId(testPerson.getPersonId());
        testIdCard.setCardNo("12334555");
        BaseApplication.getDbUtil().insert(testIdCard);
        //查询数据
        List<TestPerson> testPeople = BaseApplication.getDbUtil().queryAll(TestPerson.class);
        for (TestPerson person : testPeople) {
            LogUtils.d("person  :" + person.toString());
            TestIdCard testIdCard1 = person.getTestIdCard();
            LogUtils.d("card: " + testIdCard1.toString());
        }
    }

一对多

一对多实际上是在多的一方的表中创建一个外键,如果多的一方表中的某个元素的外键等于一的一方表中的主键,那么多的一方表中的这个元素就跟一的一方表中的这个元素产生了一对多的关联。一对多要用到@ToMany注解,例如一个用户可以拥有多张信用卡
新建实体类:

TestUser
@Entity
public class TestUser {

    @Id(autoincrement = true)
    private Long userId;

    private String userName;
    private String age;

    //一对多
    @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

测试

 /**
     * 一对多
     * user and creditCard
     */
    private void oneToMany() {
        //存储数据
        TestUser testUser = new TestUser();
        testUser.setUserName("狗哥");
        testUser.setAge("23");
        BaseApplication.getDbUtil().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");
            BaseApplication.getDbUtil().insert(testCreditCard);
        }
        //查询数据
        List<TestUser> testUsers = BaseApplication.getDbUtil().queryAll(TestUser.class);
        for (TestUser user : testUsers) {
            LogUtils.d("用户:" + user.toString());
            List<TestCreditCard> creditCards = user.getCreditCards();
            for (TestCreditCard creditCard : creditCards) {
                LogUtils.d("信用卡:" + creditCard.toString());
            }
        }
    }

多对多

多对多创建中间表,在中间表中分别有两个外键指向一的一方。如果中间表中的某个元素的外键等于一的一方中某个元素的主键,那么这两项元素就产生了一对多的关联。中间表分别指向两边产生一对多的关联,那么两边就产生了多对多的关联。多对多的关系其实也就类似于MySQL中的多对多关系,通过建立中间表来分解为一对多的关系。我们以student和teacher为例,下面创建两个实体类

@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后,我们来进行测试。分为两个方法:insertDataToMany是创建并保存数据;
queryDataToMany是分别从学生的角度查询关联的老师和从老师的角度查询关联的学生

测试

 /**
     * 多对多
     */
    public void insertDataToMany(View view) {
        for (int i = 0; i < 2; i++) {
            if (i == 0) {
                Teacher teacher = new Teacher();
                teacher.setTeacherName("李老师");
                long insert = BaseApplication.getInstance().getDaoSession().insert(teacher);
                Log.i(TAG, "老师存储的结果:" + insert);
                for (int i1 = 0; i1 < 2; i1++) {
                    if (i1 == 0) {
                        Student student = new Student();
                        student.setStudentName("大同学1号");
                        long insertStudent = BaseApplication.getInstance().getDaoSession().insert(student);
                        Log.i(TAG, "学生存储的结果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = BaseApplication.getInstance().getDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存储中间表的结果:" + insertStudentTeacher);
                    }else if (i1==1){}
                    if (i1 == 0) {
                        Student student = new Student();
                        student.setStudentName("大同学2号");
                        long insertStudent = BaseApplication.getInstance().getDaoSession().insert(student);
                        Log.i(TAG, "学生存储的结果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = BaseApplication.getInstance().getDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存储中间表的结果:" + insertStudentTeacher);
                    }
                }
            } else if (i == 1) {
                Teacher teacher = new Teacher();
                teacher.setTeacherName("刘老师");
                long insert = BaseApplication.getInstance().getDaoSession().insert(teacher);
                Log.i(TAG, "老师存储的结果:" + insert);
                for (int i1 = 0; i1 < 2; i1++) {
                    if (i1 == 0) {
                        Student student = new Student();
                        student.setStudentName("小同学1号");
                        long insertStudent = BaseApplication.getInstance().getDaoSession().insert(student);
                        Log.i(TAG, "学生存储的结果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = BaseApplication.getInstance().getDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存储中间表的结果:" + insertStudentTeacher);
                    }else if (i1==1){
                        Student student = new Student();
                        student.setStudentName("小同学2号");
                        long insertStudent = BaseApplication.getInstance().getDaoSession().insert(student);
                        Log.i(TAG, "学生存储的结果:" + insertStudent);

                        StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
                        studentAndTeacherBean.setTId(teacher.getTeacherId());
                        studentAndTeacherBean.setSId(student.getStudentId());
                        long insertStudentTeacher = BaseApplication.getInstance().getDaoSession().insert(studentAndTeacherBean);
                        Log.i(TAG, "存储中间表的结果:" + insertStudentTeacher);
                    }
                }
            }
        }
    }

/**
     * 查询数据
     */
    public void queryDataToMany(View view) {
        List<Teacher> teachers = BaseApplication.getInstance().getDaoSession().loadAll(Teacher.class);
        Log.i(TAG, "所有老师的数量:" + teachers.size());
        for (Teacher teacher : teachers) {
            Log.i(TAG, "老师:" + teacher.toString());
            List<Student> studentList = teacher.getStudentList();
            Log.i(TAG, "学生的数量:" + studentList.size());
            for (Student student : studentList) {
                Log.i(TAG,"学生的信息:"+student.toString());
            }
        }

        List<Student> students = BaseApplication.getInstance().getDaoSession().loadAll(Student.class);
        Log.i(TAG, "所有学生的数量:" + students.size());
        for (Student student : students) {
            Log.i(TAG, "学生:" + student.toString());
            List<Teacher> teacherList = student.getTeacherList();
            Log.i(TAG, "老师的数量:" + teacherList.size());
            for (Teacher teacher : teacherList) {
                Log.i(TAG,"老师的信息:"+teacher.toString());
            }
        }
    }

数据库的升级更新:文章参考https://www.jianshu.com/p/53083f782ea2,在此感谢分享!

我们自定义一个类继承自OpenHelper

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是你项目中的Dao数据类,这里可以传一个Dao类的数组

/**
 * 数据库升级的思路:
 *    1.先创建当前已存在的所有表,对应临时的表
 *    2.然后删除当前已存在的所有的表
 *    3.创建数据库升级后的所有新的表
 *
 */
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";

    /**
     * 重新创建所有表格监听的弱引用
     */
    private static WeakReference<ReCreateAllTableListener> weakListener;

    /**
     * 重新创建所有表格的监听
     */
    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) {
        //旧的数据库版本
        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);//生成临时的表
        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);
            }
        }
    }

    /**
     *
     * @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();
        }
    }

    /**
     * 恢复数据
     * @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;
        }
    }
}

数据库的文件有变动,记得修改build.gradle中的数据库版本号,当新的数据库版本号大于旧的版本号时才会进行数据库的更新升级!

多表关联:一对多和多对多时,把一的一方的外键给多的一方存储;一对一中,把被动一方的外键给主动一方存储

补充上DBUtils工具类

package com.baselibrary.dao.db;

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

import com.orhanobut.logger.Logger;

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.async.AsyncOperation;
import org.greenrobot.greendao.async.AsyncOperationListener;
import org.greenrobot.greendao.async.AsyncSession;
import org.greenrobot.greendao.query.Query;
import org.greenrobot.greendao.query.QueryBuilder;
import org.greenrobot.greendao.query.WhereCondition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created By pq
 * on 2019/9/9
 */
public class DBUtil {

    private static final String DB_NAME = "UE875_DB.db";
    private static final String DEFAULT_DB_PASSWORD = "ue875ue";

    private DaoSession daoSession;
    private static DBUtil dbUtil = null;
    private MyGreenDaoDbHelper devOpenHelper;
    private AbstractDao mAbstractDao;
    private DbCallBack mCallBack;

    public DBUtil() {
    }

    public static DBUtil instance(Application application) {
        if (dbUtil == null) {
            synchronized (DBUtil.class) {
                if (dbUtil == null) {
                    dbUtil = new DBUtil(application, DB_NAME, DEFAULT_DB_PASSWORD);
//                    dbUtil = new DBUtil();
                }
            }
        }
        return dbUtil;
    }

    //    初始化数据库
    public void initDB(Context context) {
        devOpenHelper = new MyGreenDaoDbHelper(context, DB_NAME);
//        devOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
        SQLiteDatabase database = devOpenHelper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(database);
        daoSession = daoMaster.newSession();
    }

    public synchronized DaoSession getDaoSession() {
        return daoSession;
    }

    //初始化数据库
    private DBUtil(Application application, String dbName, String passWord) {
        if (devOpenHelper == null) {
            devOpenHelper = new MyGreenDaoDbHelper(application, dbName);
//            devOpenHelper = new DaoMaster.DevOpenHelper(application, dbName);
        }
        DaoMaster daoMaster;
        if (passWord == null || passWord.isEmpty()) {
            daoMaster = new DaoMaster(devOpenHelper.getWritableDb());
        } else {
            daoMaster = new DaoMaster(devOpenHelper.getEncryptedReadableDb(passWord));
        }
        daoSession = daoMaster.newSession();

    }

    public static DBUtil getInstance(Application application) {
        return getInstance(application, DB_NAME);
    }

    public static DBUtil getInstance(Application application, String dbName) {
        return getInstance(application, dbName, DEFAULT_DB_PASSWORD);
    }

    public static DBUtil getInstance(Application application, String dbName, String passWord) {
        if (dbUtil == null) {
            synchronized (DBUtil.class) {
                if (dbUtil == null) {
                    dbUtil = new DBUtil(application, dbName, "");
                }
            }
        }
        return dbUtil;
    }

    /**
     * 数据库不加密
     *
     * @param entityClass 根据 entityClass 获取相应的 xxDao
     * @return mDbUtils
     */
    @Deprecated
    public DBUtil create(Class<?> entityClass) {
        if (devOpenHelper == null) {
            throw new NullPointerException("You need to init mHelper first!");
        }
        mAbstractDao = daoSession.getDao(entityClass);
        return dbUtil;
    }

    private <T> void setCurrentDao(Class<T> entityClass) {
        if (devOpenHelper == null) {
            throw new NullPointerException("You need to init mHelper first!");
        }
        mAbstractDao = daoSession.getDao(entityClass);
    }

    private <T> void setCurrentDaoOfList(Collection<T> entities) {
        if (entities != null && entities.size() > 1) {
            Iterator<T> iterator = entities.iterator();
            T next = iterator.next();
            setCurrentDao(next.getClass());
        }
    }

    /**
     * 设置DeBug的模式
     *
     * @param flag
     */
    public void setDebug(boolean flag) {
        QueryBuilder.LOG_SQL = flag;
        QueryBuilder.LOG_VALUES = flag;
    }

    private void closeDaoSession() {
        if (daoSession != null) {
            daoSession.clear();
            daoSession = null;
        }
    }

    private void closeHelper() {
        if (devOpenHelper != null) {
            devOpenHelper.close();
            devOpenHelper = null;
        }
    }

    public void closeData() {
        closeHelper();
        closeDaoSession();
    }

    public <T> Query<T> getQuery(Class<T> claz) {
        return getQueryBuilder(claz).build();
    }

    public <T> QueryBuilder<T> getQueryBuilder(Class<T> claz) {
        return daoSession.queryBuilder(claz);
    }

    /**
     * 获取数据库Item的数量
     *
     * @return
     */
    public <T> Long count(Class<T> entityClaz) {
        setCurrentDao(entityClaz);
        return mAbstractDao.count();
    }

    /**
     * 插入一条数据
     *
     * @param dbEntity
     */
    public <T> void insert(T dbEntity) {
        setCurrentDao(dbEntity.getClass());
        mAbstractDao.insert(dbEntity);
    }

    /**
     * 插入一条数据
     *
     * @param dbEntity
     */
    public <T> void insertOrReplace(T dbEntity) {
        setCurrentDao(dbEntity.getClass());
        mAbstractDao.insertOrReplace(dbEntity);
    }

    /**
     * 插入多条数据
     *
     * @param entities
     */
    public <T> void insertTx(List<T> entities) {

        if (entities == null || entities.size() < 1) {
            return;
        }
        setCurrentDaoOfList(entities);
        mAbstractDao.insertInTx(entities);
    }

    public <T> void insertOrReplaceInTx(List<T> entities) {
        if (entities == null || entities.size() < 1) {
            return;
        }
        setCurrentDaoOfList(entities);
        mAbstractDao.insertOrReplaceInTx(entities);
    }

    /**
     * 删除单条数据
     *
     * @param entity
     */
    public <T> void delete(T entity) {
        setCurrentDao(entity.getClass());
        mAbstractDao.delete(entity);
    }

    /**
     * 删除特定ID的数据
     *
     * @param id
     */
    public <T> void deleteById(Class<T> entityClaz, long id) {
        setCurrentDao(entityClaz);
        mAbstractDao.deleteByKey(id);
    }

    /**
     * 删除多条数据
     *
     * @param entities
     */
    public <T> void deleteList(List<T> entities) {
        setCurrentDaoOfList(entities);
        mAbstractDao.deleteInTx(entities);
    }

    /**
     * 全部删除
     */
    public <T> void deleteAll(Class<T> claz) {
        setCurrentDao(claz);
        mAbstractDao.deleteAll();
    }

    /**
     * 更新单条数据
     *
     * @param entity
     */
    public <T> void updateData(final T entity) {
        setCurrentDao(entity.getClass());
        mAbstractDao.update(entity);
    }

    /**
     * 更新多条数据
     *
     * @param entities
     */
    public <T> void updateListData(Collection<T> entities) {
        setCurrentDaoOfList(entities);
        mAbstractDao.updateInTx(entities);
    }

    /**
     * 查询特定ID的数据
     *
     * @param id
     * @return
     */
    public <T> T queryById(Class<T> claz, long id) {
        setCurrentDao(claz);
        return (T) mAbstractDao.load(id);
    }

    /**
     * 查询全部数据
     *
     * @return
     */
    public <T> List<T> queryAll(Class<T> claz) {
        setCurrentDao(claz);
        return mAbstractDao.loadAll();
    }

    public <T> List<T> queryAll(Class<T> claz, WhereCondition whereCondition) {
        setCurrentDao(claz);
        return daoSession.queryBuilder(claz)
                .where(whereCondition)
                .list();
    }

    public <T> List<T> queryAll(Class<T> claz, QueryBuilder<T> queryBuilder) {
        setCurrentDao(claz);
        return daoSession.queryBuilder(claz).list();
    }

    public <T> List<T> queryAll(Class<T> claz, Query<T> query) {
        setCurrentDao(claz);
        return query.list();
    }

    /**
     * 原生查询
     *
     * @param claz
     * @param whereString
     * @param params
     * @param <T>
     * @return
     */
    public <T> List<T> queryRaw(Class<T> claz, String whereString, String[] params) {
        setCurrentDao(claz);
        return mAbstractDao.queryRaw(whereString, params);
    }

    /**
     * 异步操作的回调设置
     *
     * @param callBack
     * @param <T>
     * @return
     */
    public <T> DBUtil setDbCallBack(DbCallBack<T> callBack) {
        mCallBack = callBack;
        return this;
    }

    /**
     * 条件查询数据
     *
     * @param cls
     * @return
     */
    public <T> void queryAsync(Class<T> cls, WhereCondition whereCondition) {
        setCurrentDao(cls);
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    //      List<T> list = new ArrayList<>();
                    //     list.add(((T) operation.getResult()));
                    mCallBack.onSuccess((List) operation.getResult());
                } else if (operation.isFailed()) {
                    mCallBack.onFailed();
                }
            }
        });
        Query query = daoSession.queryBuilder(cls).where(whereCondition).build();
        asyncSession.queryList(query);
    }

    public <T> void queryAsync(Class<T> cls, WhereCondition cond1, WhereCondition cond2,
                               WhereCondition... condMore) {
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onSuccess((List) operation.getResult());
                } else if (operation.isFailed()) {
                    mCallBack.onFailed();
                }
            }
        });
        Query query = daoSession.queryBuilder(cls).whereOr(cond1, cond2, condMore).build();
        asyncSession.queryList(query);
    }


    /**
     * 异步条件查询,通过使用 QueryBuilder 构造 Query
     *
     * @param claz
     * @param builder
     * @param <T>
     */
    public   <T>  void  queryAsyncAll(Class<T> claz, QueryBuilder<T> builder) {
        setCurrentDao(claz);
        String name = claz.getName();
        Logger.d("  类名 :" + name);
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompleted() && mCallBack != null) {
                    List<T> result = (List<T>) operation.getResult();
                    mCallBack.onSuccess(result);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onFailed();
                }
            }
        });
        if (builder == null || builder.build() == null) {
            asyncSession.loadAll(claz);
        } else {
            asyncSession.queryList(builder.build());
        }
    }

    /**
     * 删除
     */
    public <T> void deleteAsyncSingle(T entry) {
        setCurrentDao(entry.getClass());
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.delete(entry);
    }

    /**
     * 批量删除
     */
    public <T> void deleteAsyncBatch(Class<T> cls, final List<T> list) {
        setCurrentDao(cls);
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.deleteInTx(cls, list);
    }


    /**
     * 根据Id批量删除
     */
    public <T> void deleteByIdBatch(Class<T> claz, List<Long> longList) {
        setCurrentDao(claz);
        mAbstractDao.deleteByKeyInTx(longList);
    }

    /**
     * 删除所有数据
     */
    public <T> void deleteAsyncAll(Class<T> cls) {
        setCurrentDao(cls);
        final AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                    mCallBack.onSuccess(null);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                    mCallBack.onFailed();
                }
            }
        });
        asyncSession.deleteAll(cls);
    }

    /**
     * 插入一条数据
     */
    public <T> void insertAsyncSingle(final T entity) {
        setCurrentDao(entity.getClass());
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                    mCallBack.onSuccess(entity);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                    mCallBack.onFailed();
                }
            }
        });
        asyncSession.runInTx(new Runnable() {
            @Override
            public void run() {
                daoSession.insertOrReplace(entity);
            }
        });
    }

    /**
     * 批量插入
     */
    public <T> void insertAsyncBatch(final Class<T> cls, final List<T> userList) {
        setCurrentDao(cls);
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                    mCallBack.onSuccess(userList);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                    mCallBack.onFailed();
                }
            }
        });
        asyncSession.runInTx(new Runnable() {
            @Override
            public void run() {
                for (T object : userList) {
                    daoSession.insertOrReplace(object);
                }
            }
        });
    }

    /**
     * 更新一个数据
     */
    public <T> void updateAsyncSingle(Class<T> cls, T entry) {
        setCurrentDao(cls);
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                    mCallBack.onSuccess(entry);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                    mCallBack.onFailed();
                }
            }
        });
        asyncSession.update(entry);
    }

    /**
     * 批量更新数据
     */
    public <T> void updateAsyncBatch(final Class<T> cls, final List<T> tList) {
        setCurrentDao(cls);
        AsyncSession asyncSession = daoSession.startAsyncSession();
        asyncSession.setListenerMainThread(new AsyncOperationListener() {
            @Override
            public void onAsyncOperationCompleted(AsyncOperation operation) {
                if (operation.isCompletedSucessfully() && mCallBack != null) {
                    mCallBack.onNotification(true);
                } else if (operation.isFailed() && mCallBack != null) {
                    mCallBack.onNotification(false);
                }
            }
        });
        asyncSession.updateInTx(cls, tList);
    }
}

附带一个LiteORM框架的博文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274