GreenDao 配置及使用心得

作者: 热血沸腾 | 来源:发表于2017-12-20 11:42 被阅读311次

    说明

    仅仅记录自己学习及使用该框架的过程和代码
    框架版本大致可以分为

    • 1.x
    • 2.x 增加build.gradle插件
    • 3.x 增加注解,build.gradle插件,加密

    为什么用GreenDao

    没有该框架之前,其实本人一直在使用手动封装的ORM。该ORM是我看起来融合我现有掌握的知识很自然的框架。因为它仅仅做的事情是。 编译时生产出数据库业务相关的代码,这样简单理解为它就是一个工具。仅仅而已。
    The best way to access the SQLite就像它宣传的含义一样。

    简单概念

    想玩转数据库,再我看来,必须要懂概念性东西。比如关系型数据库的基础。

    1. Master
      抽象基类,定义Session生成等等
    2. Schema
      如果数据库比作图书馆,那么每个藏书的房间就是Schema,它描述了这个房间的基本信息,如几个书架,藏书什么类型,借阅的开放时间等等。 那么表就代表书架,那么字段就是书架的分类签。行内容就是一本本书了。
    3. Session
      每一次数据库链接的会话,记得有缓存功能!。可以理解为你打算今天去图书馆看书一天。
      4.Dao
      相当于你获得了借阅哪个书架的资格

    1.x配置

    我的工程目录如图,
    生成数据库相关代码如下:


    image.png
    package com.example.ltx.orm_greendao.db.generator;
    
    import de.greenrobot.daogenerator.DaoGenerator;
    import de.greenrobot.daogenerator.Entity;
    import de.greenrobot.daogenerator.Schema;
    
    /**
     * Created by ltx on 2017/12/19.
     */
    
    public class GreenDaoGenerator {
    
        private static void addTaskDetail(Schema schema) {
            //创建User表
            Entity entityUser = schema.addEntity("User");
            entityUser.addIdProperty().primaryKey().autoincrement();;
            entityUser.addStringProperty("userId").notNull();
            entityUser.addStringProperty("userName");
            entityUser.addIntProperty("age");
            entityUser.addStringProperty("phone");
            entityUser.addDateProperty("createTime");
            entityUser.addDateProperty("modifyTime");
    
            //创建NetCache表
            Entity entityNetCache = schema.addEntity("NetCache");
            entityNetCache.addIdProperty().primaryKey().autoincrement();;
            entityNetCache.addLongProperty("netId").notNull();
            entityNetCache.addStringProperty("url");
            entityNetCache.addStringProperty("json");
            entityNetCache.addDateProperty("createTime");
            entityNetCache.addDateProperty("modifyTime");
        }
    
        public static void main(String[] args) throws Exception {
            //定义Scheme,可以有多个,通常一个就可以了。Scheme相当于房间,表相当于书架子。
            //参数二代表包的路径,生成文件所在位置。
            //需要注意的是,Scheme版本更新,DaoSession
            Schema schema = new Schema(1, "com.example.ltx.orm_greendao.db.entity");
            addTaskDetail(schema);
            try {
                new DaoGenerator().generateAll(schema, "../ORM-GreenDao/app/src/main/java/");
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    

    接下来,为了方便使用。单例来管理Session。我们知道,一般IO操作,包括网络,数据库的读写,文件读写都是IO,那么IO就最好严格执行异步,因此这里还会有GreenDao提供的AsyncSession对象


    image.png
    package com.example.ltx.orm_greendao.db;
    
    import android.app.Application;
    import android.content.Context;
    
    import com.example.ltx.orm_greendao.db.entity.DaoMaster;
    import com.example.ltx.orm_greendao.db.entity.DaoSession;
    
    import de.greenrobot.dao.async.AsyncSession;
    
    /**
     * Created by ltx on 2017/12/20.
     */
    
    public class DBHelper {
        private static String DB_NAME = "test";
    
        private static DaoSession session  = null;
        private static AsyncSession asyncSession  = null;
    
        /**
         * DaoSession单利
         * @return
         */
        public static DaoSession getSessionInstance(){
            if(session==null)
                synchronized (DBHelper.class) {
                    if(session==null){
                        Context appContext = new Application().getApplicationContext();
                        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(appContext, DB_NAME, null);
                        DaoMaster master = new DaoMaster(helper.getWritableDatabase());
                        session = master.newSession();
                    }
                }
            return session;
        }
    
        /**
         * AsyncSession单利
         * @return
         */
        public static AsyncSession getAsyncSessionInstance(){
            if(asyncSession==null)
                synchronized (DBHelper.class) {
                    if(asyncSession==null){
                        if(session==null){
                            session = getSessionInstance();
                        }
                        asyncSession = session.startAsyncSession();
    
                    }
                }
            return asyncSession;
        }
    
    
    
    }
    
    

    具体使用,代码:

    package com.example.ltx.orm_greendao;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    import com.example.ltx.orm_greendao.db.DBHelper;
    import com.example.ltx.orm_greendao.db.entity.DaoSession;
    import com.example.ltx.orm_greendao.db.entity.User;
    import com.example.ltx.orm_greendao.db.entity.UserDao;
    
    import de.greenrobot.dao.async.AsyncOperation;
    import de.greenrobot.dao.async.AsyncOperationListener;
    import de.greenrobot.dao.async.AsyncSession;
    
    public class MainActivity extends Activity implements View.OnClickListener {
        private TextView mTvShow;
        private Button mBtnAdd,mBtnDel,mBtnUpdate,mBtnSelect;
        private AsyncSession mAsyncSession;
        private DaoSession mDaoSession;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTvShow = findViewById(R.id.db_show);
            mBtnAdd = findViewById(R.id.db_add);
            mBtnDel = findViewById(R.id.db_delete);
            mBtnUpdate = findViewById(R.id.db_update);
            mBtnSelect = findViewById(R.id.db_select);
    
    
            mBtnAdd.setOnClickListener(this);
            mBtnDel.setOnClickListener(this);
            mBtnUpdate.setOnClickListener(this);
            mBtnSelect.setOnClickListener(this);
    
            mAsyncSession = DBHelper.getAsyncSessionInstance();
            mDaoSession = DBHelper.getSessionInstance();
        }
    
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.db_add:
                    User u = new User();
                    u.setUserName("ltx");
                    u.setAge(4);
    
    
                    //异步操作
                    mAsyncSession.insert(u);
                    mAsyncSession.setListenerMainThread(new AsyncOperationListener() {
                        @Override
                        public void onAsyncOperationCompleted(AsyncOperation operation) {
                            operation.getResult();
                        }
                    });
    
    
    
                    mAsyncSession.runInTx(new Runnable() {
                        @Override
                        public void run() {
                            //不关心结果,且有多个顺序执行的操作
                        }
                    });
    
                    
                    UserDao userDao = mDaoSession.getUserDao();
                    //同步操作
                    userDao.insert(u);
    
    
                    break;
    
                case R.id.db_delete:
    
    
                    break;
    
                case R.id.db_update:
                    break;
    
                case R.id.db_select:
                    break;
            }
        }
    }
    
    

    数据库升级

    • 原表数据保留
    • 表字段有增加(记得,表字段不要修改,不要删除,有新业务增加就行了)
    • 有些表数据,数据库升级后不需要保留
      要知道一点,GreenDao在数据库升级的回调方法中的操作是
    DropAllTable();
    CreateAllTable();
    

    因此,默认全部表的数据是无法保留的。
    若想升级时保留某个表数据。需要使用一个帮助类,帮助类代码

    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.text.TextUtils;
    import android.util.Log;
    
    import com.crashlytics.android.Crashlytics;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import de.greenrobot.dao.AbstractDao;
    import de.greenrobot.dao.internal.DaoConfig;
    import greendao.DaoMaster;
    
    
    /**
     * Created by pokawa on 18/05/15.
     */
    public class MigrationHelper {
    
        private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
        private static MigrationHelper instance;
    
        public static MigrationHelper getInstance() {
            if(instance == null) {
                instance = new MigrationHelper();
            }
            return instance;
        }
    
        public void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
            generateTempTables(db, daoClasses);
            DaoMaster.dropAllTables(db, true);
            DaoMaster.createAllTables(db, false);
            restoreData(db, daoClasses);
        }
    
        private void generateTempTables(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
            for(int i = 0; i < daoClasses.length; i++) {
                DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
    
                String divider = "";
                String tableName = daoConfig.tablename;
                String tempTableName = daoConfig.tablename.concat("_TEMP");
                ArrayList<String> properties = new ArrayList<>();
    
                StringBuilder createTableStringBuilder = new StringBuilder();
    
                createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");
    
                for(int j = 0; j < daoConfig.properties.length; j++) {
                    String columnName = daoConfig.properties[j].columnName;
    
                    if(getColumns(db, tableName).contains(columnName)) {
                        properties.add(columnName);
    
                        String type = null;
    
                        try {
                            type = getTypeByClass(daoConfig.properties[j].type);
                        } catch (Exception exception) {
                            Crashlytics.logException(exception);
                        }
    
                        createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);
    
                        if(daoConfig.properties[j].primaryKey) {
                            createTableStringBuilder.append(" PRIMARY KEY");
                        }
    
                        divider = ",";
                    }
                }
                createTableStringBuilder.append(");");
    
                db.execSQL(createTableStringBuilder.toString());
    
                StringBuilder insertTableStringBuilder = new StringBuilder();
    
                insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
                insertTableStringBuilder.append(TextUtils.join(",", properties));
                insertTableStringBuilder.append(") SELECT ");
                insertTableStringBuilder.append(TextUtils.join(",", properties));
                insertTableStringBuilder.append(" FROM ").append(tableName).append(";");
    
                db.execSQL(insertTableStringBuilder.toString());
            }
        }
    
        private void restoreData(SQLiteDatabase 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");
                ArrayList<String> properties = new ArrayList();
    
                for (int j = 0; j < daoConfig.properties.length; j++) {
                    String columnName = daoConfig.properties[j].columnName;
    
                    if(getColumns(db, tempTableName).contains(columnName)) {
                        properties.add(columnName);
                    }
                }
    
                StringBuilder insertTableStringBuilder = new StringBuilder();
    
                insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
                insertTableStringBuilder.append(TextUtils.join(",", properties));
                insertTableStringBuilder.append(") SELECT ");
                insertTableStringBuilder.append(TextUtils.join(",", properties));
                insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
    
                StringBuilder dropTableStringBuilder = new StringBuilder();
    
                dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
    
                db.execSQL(insertTableStringBuilder.toString());
                db.execSQL(dropTableStringBuilder.toString());
            }
        }
    
        private String getTypeByClass(Class<?> type) throws Exception {
            if(type.equals(String.class)) {
                return "TEXT";
            }
            if(type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
                return "INTEGER";
            }
            if(type.equals(Boolean.class)) {
                return "BOOLEAN";
            }
    
            Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
            Crashlytics.logException(exception);
            throw exception;
        }
    
        private static List<String> getColumns(SQLiteDatabase db, String tableName) {
            List<String> columns = new ArrayList<>();
            Cursor cursor = null;
            try {
                cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
                if (cursor != null) {
                    columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
                }
            } catch (Exception e) {
                Log.v(tableName, e.getMessage(), e);
                e.printStackTrace();
            } finally {
                if (cursor != null)
                    cursor.close();
            }
            return columns;
        }
    }
    

    DaoMaster中使用:

    @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by migrating all tables data");
    
            MigrationHelper.getInstance().migrate(db,
                    UserDao.class,
                    ItemDao.class);
        }
    

    只要把想保留数据的表所对应的Dao.class 写在参数里就可以。

    数据加密

    GreenDao3.0 后直接支持SQLCipher的。SQLCipher是一个自定义使用256位的AES加密的SQLite.
    依赖

    compile 'net.zetetic:android-database-sqlcipher:3.5.2'
    

    在Session创建时调用加密数据库

    daoSession = new DaoMaster(dbHelper.getEncryptedWritableDb(DB_SCRET_KEY)).newSession();
    

    完结

    相关文章

      网友评论

        本文标题:GreenDao 配置及使用心得

        本文链接:https://www.haomeiwen.com/subject/rfnwwxtx.html