美文网首页IT@程序员猿媛Java架构技术进阶
Android App开发 之 SQLite 整合 greenD

Android App开发 之 SQLite 整合 greenD

作者: 程就人生 | 来源:发表于2019-07-12 14:37 被阅读2次

    在上一篇Android Studio开发之初入门(一)文章后,一直在研究Android App端的开发,心里一直惦记着一个Android App如何架构,后来干脆不想了。先找到一个点进行突破吧,App本地需要缓存数据吧,那么本地数据如何处理最为方便、简洁、省力。一款app应用,在本地肯定会缓存一些数据,比如用户信息,在Android App端最常用的数据库是sqlite。

    根据网友们的推荐,选择了greenDao,研究一下,果真简便、省代码。接下来本文就从这几个方面,说说greenDao是啥,如何使用的,最重要的,附加代码和配置。

    1.什么是greenDao
    2.greenDao如何配置
    3.greenDao使用,实体类、Dao的生成
    4.Dao层的具体使用,增删改查、条件查询、批量修改
    5.数据库表改动,数据库的升级操作

    一.什么是greenDao

    根据greenDao官网描述,greenDao是针对Android端SQLited数据库的ORM(对象关系映射)型数据库中间件,大家都知道,SQLite并不是关系型数据库,数据类型也有限,但是中间件greenDao可以让我们像操作对象一样,去操作SQLite数据库,是何等的方便和简洁,省去了多少麻烦。

    二.GreenDao如何配置
    第一步,新建一个Android App项目,编程语言选择java语言;
    图-1
    图-2
    图-3
    第二步,添加配置;

    1)首先在项目的build.gradle,dependencies增加配置:

    classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 版本建议最新
    
    图-4

    2)然后在app模块的build.gradle,头部增加配置

    apply plugin: 'org.greenrobot.greendao'
    

    android的配置里增加:

    // greendao 配置
        greendao {
            schemaVersion 1                                     // 数据库版本号,根据需要修改
            daoPackage      'com.example.myapplication.dao'    // greenDao 自动生成的代码保存的包名
            targetGenDir    'src/main/java'                    // 自动生成的代码存储的路径,默认是 build/generated/source/greendao.
        }
    
    图-5

    Dependencies里增加:

    // GreenDao  数据库ORM
        implementation 'org.greenrobot:greendao:3.2.2'
        // GreenDao 生成dao和model的generator的项目 发布时可以去掉
        implementation 'org.greenrobot:greendao-generator:3.2.2'
    
    图-6
    三.GreenDao使用,实体类、Dao的生成

    1)新建实体类User,放在entity文件夹里, User实体类内容如下:

    import org.greenrobot.greendao.annotation.Entity;
    import org.greenrobot.greendao.annotation.Id;
    import org.greenrobot.greendao.annotation.NotNull;
    import org.greenrobot.greendao.annotation.Unique;
    import org.greenrobot.greendao.annotation.Generated;
    
    @Entity(nameInDb = "user")
    public class User {
    
        @Id
        @Unique
        @NotNull
        private String userId;
    
        @Unique
        @NotNull
        private String userName;
    }
    

    2)运行项目,生成了三个文件,UserDao、DaoSession、DaoMaster类


    图-7

    3)打开生成的文件,发现并没有指定数据库名称,新建BaseApplication指明数据库名称;

    import android.app.Application;
    import android.database.sqlite.SQLiteDatabase;
    
    import com.example.myapplication.dao.DaoMaster;
    import com.example.myapplication.dao.DaoSession;
    
    import static com.example.myapplication.dao.DaoMaster.*;
    
    public class BaseApplication extends Application {
    
        private DevOpenHelper mHelper;
        private SQLiteDatabase db;
        private DaoMaster mDaoMaster;
        private DaoSession mDaoSession;
    
        private static DaoSession daoSession;
        public static BaseApplication instances;
    
        @Override
        public void onCreate() {
            super.onCreate();
            //配置数据库
            // 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。
            // 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。
            // 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。
            // 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。
            mHelper = new DevOpenHelper(this.getBaseContext(), "greendao-db", null);
            db = mHelper.getWritableDatabase();
            // 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。
            mDaoMaster = new DaoMaster(db);
            mDaoSession = mDaoMaster.newSession();
    
            instances = this;
        }
    
        public static BaseApplication getInstances(){
            return instances;
        }
    
        public DaoSession getDaoSession() {
            return mDaoSession;
        }
    
        public SQLiteDatabase getDb() {
            return db;
        }
    }
    

    4) 需要在AndroidManifest.xml,增加代码,为application指定名称

    android:name=".dbconfig.BaseApplication"
    
    四.Dao层的具体使用,增删改查、条件查询、批量修改;

    1)在MainActivity添加增删改查的代码:

    public void addOnClick(View view){
            Toast.makeText(this, "add", Toast.LENGTH_SHORT).show();
    
            //新增
            User user = new User();
    //        user.setUserId("12345678");
    //        user.setUserName("bbbbbb");
    //        getUserDao().insert(user);
    //
    //        //根据主键进行查询
    //        User user1 = getUserDao().load("123456");
    //        if(user1 != null ){
    //            Toast.makeText(this, user1.getUserName(), Toast.LENGTH_SHORT).show();
    //        }
    
            //修改
    //        user.setUserName("bbbbbbbb");
    //        getUserDao().update(user);
    //
    //        User user1 = getUserDao().load("123456");
    //        if(user1 != null ){
    //            Toast.makeText(this, user1.getUserName(), Toast.LENGTH_SHORT).show();
    //        }
    
            //删除
    //        getUserDao().delete(user);
    //        User user1 = getUserDao().load("123456");
    //        if(user1 != null ){
    //            Toast.makeText(this, user1.getUserName(), Toast.LENGTH_SHORT).show();
    //        }else{
    //            Toast.makeText(this, "找不到", Toast.LENGTH_SHORT).show();
    //        }
    
            //查询所有
    //        List<User> list = getUserDao().loadAll();
    //        if(list != null && list.size() > 0){
    //            //批量修改
    //            for(User tempUser : list){
    //                tempUser.setUserName(tempUser.getUserId() + tempUser.getUserName());
    //            }
    //            getUserDao().updateInTx(list);
    //            Toast.makeText(this, list.size() + "条", Toast.LENGTH_SHORT).show();
    //        }else{
    //            Toast.makeText(this, "找不到", Toast.LENGTH_SHORT).show();
    //        }
    //
    //
    //
            //条件查询
            String where = " where user_id = ?  and user_name= ? ";
            List<User> list = getUserDao().queryRaw(where, new String[]{"123456","123456aaaaaa"});
            if(list != null && list.size() > 0){
                user = list.get(0);
                Toast.makeText(this, list.size() + "条" + user.getUserName(), Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(this, "找不到", Toast.LENGTH_SHORT).show();
            }
    //
    
        }
    
        /**
         * 获取dao
         * @return
         */
        private UserDao getUserDao(){
            return BaseApplication.getInstances().getDaoSession().getUserDao();
        }
    

    2)并在 xml 页面给按钮增加click事件:

    android:onClick="addOnClick"
    
    图-8

    3) 接下来就可以点击运行进行测试了,经过测试,测试结果是OK的。

    五.数据库表改动,数据库的升级操作;

    如果数据库表中有改动,或者新添加了数据库表,怎么办,继续配置;
    1)新增DbManager类

    package com.example.myapplication.dbconfig;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    
    import com.example.myapplication.dao.DaoMaster;
    import com.example.myapplication.dao.DaoSession;
    
    import static com.example.myapplication.dbconfig.BaseApplication.DB_NAME;
    
    /**
     * 数据库升级管理类
     */
    public class DbManager {
    
        // 是否加密
        public static final boolean ENCRYPTED = false;
    
        private static DbManager mDbManager;
        private static DaoMaster.DevOpenHelper mDevOpenHelper;
        private static DaoMaster mDaoMaster;
        private static DaoSession mDaoSession;
    
        private Context mContext;
    
        private DbManager(Context context) {
            this.mContext = context;
            // 初始化数据库信息
            mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
            getDaoMaster(context);
            getDaoSession(context);
        }
    
        public static DbManager getInstance(Context context) {
            if (null == mDbManager) {
                synchronized (DbManager.class) {
                    if (null == mDbManager) {
                        mDbManager = new DbManager(context);
                    }
                }
            }
            return mDbManager;
        }
    
        /**
         * 获取可读数据库
         *
         * @param context
         * @return
         */
        public static SQLiteDatabase getReadableDatabase(Context context) {
            if (null == mDevOpenHelper) {
                getInstance(context);
            }
            return mDevOpenHelper.getReadableDatabase();
        }
    
        /**
         * 获取可写数据库
         *
         * @param context
         * @return
         */
        public static SQLiteDatabase getWritableDatabase(Context context) {
            if (null == mDevOpenHelper) {
                getInstance(context);
            }
            return mDevOpenHelper.getWritableDatabase();
        }
    
        /**
         * 获取DaoMaster
         *
         * 判断是否存在数据库,如果没有则创建数据库
         * @param context
         * @return
         */
        public static DaoMaster getDaoMaster(Context context) {
            if (null == mDaoMaster) {
                synchronized (DbManager.class) {
                    if (null == mDaoMaster) {
                        MyOpenHelper helper = new MyOpenHelper(context,DB_NAME,null);
                        mDaoMaster = new DaoMaster(helper.getWritableDatabase());
                    }
                }
            }
            return mDaoMaster;
        }
    
        /**
         * 获取DaoMaster
         *
         * @param context
         * @return
         */
    //    public static DaoMaster getDaoMaster(Context context) {
    //        if (null == mDaoMaster) {
    //            synchronized (DbManager.class) {
    //                if (null == mDaoMaster) {
    //
    //                    mDaoMaster = new DaoMaster(getWritableDatabase(context));
    //                }
    //            }
    //        }
    //        return mDaoMaster;
    //    }
    
        /**
         * 获取DaoSession
         *
         * @param context
         * @return
         */
        public static DaoSession getDaoSession(Context context) {
            if (null == mDaoSession) {
                synchronized (DbManager.class) {
                    mDaoSession = getDaoMaster(context).newSession();
                }
            }
    
            return mDaoSession;
        }
    }
    
    

    2)新增MigrationHelper类

    import android.database.Cursor;
    import android.text.TextUtils;
    import android.util.Log;
    
    import com.example.myapplication.dao.DaoMaster;
    
    import org.greenrobot.greendao.AbstractDao;
    import org.greenrobot.greendao.database.Database;
    import org.greenrobot.greendao.internal.DaoConfig;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    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;
        }
    
        @SafeVarargs
        public final void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
            generateTempTables(db, daoClasses);
            DaoMaster.dropAllTables(db, true);
            DaoMaster.createAllTables(db, false);
            restoreData(db, daoClasses);
        }
    
        /**
         * 生成临时表
         * @param db
         * @param daoClasses
         */
        @SafeVarargs
        private final void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
            for (Class<? extends AbstractDao<?, ?>> daoClass : daoClasses) {
                DaoConfig daoConfig = new DaoConfig(db, daoClass);
    
                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());
    
                String insertTableStringBuilder = "INSERT INTO " + tempTableName + " (" +
                        TextUtils.join(",", properties) +
                        ") SELECT " +
                        TextUtils.join(",", properties) +
                        " FROM " + tableName + ";";
    
                db.execSQL(insertTableStringBuilder);
            }
        }
    
        /**
         * 存储新的数据库表以及数据库
         * @param db
         * @param daoClasses
         */
        @SafeVarargs
        private final void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
            for (Class<? extends AbstractDao<?, ?>> daoClass : daoClasses) {
                DaoConfig daoConfig = new DaoConfig(db, daoClass);
    
                String tableName = daoConfig.tablename;
                String tempTableName = daoConfig.tablename.concat("_TEMP");
                List<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);
                    }
                }
    
                String insertTableStringBuilder = "INSERT INTO " + tableName + " (" +
                        TextUtils.join(",", properties) +
                        ") SELECT " +
                        TextUtils.join(",", properties) +
                        " FROM " + tempTableName + ";";
    
                db.execSQL(insertTableStringBuilder);
                db.execSQL("DROP TABLE " + tempTableName);
            }
        }
    
        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";
            }
    
            //        Crashlytics.logException(exception);
            throw new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
        }
    
        private static List<String> getColumns(Database 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;
        }
    }
    

    3)新增MyOpenHelper类

    import com.example.myapplication.dao.DaoMaster;
    import com.example.myapplication.dao.UserDao;
    
    import org.greenrobot.greendao.database.Database;
    
    public class MyOpenHelper extends DaoMaster.OpenHelper {
    
        public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
            super(context, name, factory);
        }
    
        /**
         * 数据库升级
         * @param db
         * @param oldVersion
         * @param newVersion
         */
        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            //操作数据库的更新 有几个表升级都可以传入到下面
            MigrationHelper.getInstance().migrate(db, UserDao.class);
        }
    

    4)在user实体类中增加sex属性

    private String sex;
    

    5)在app的配置文件gradle将schemaVersion改为2,以后每次递增1,然后运行,运行后生成了新增的字段,测试后可见。


    图-9

    总结:
    1)如果表的唯一标识是string类型的,只要增加@Id的,就可以使用load方法进行查询,不一定非得用Long型的id。
    2)数据库表中的数据类型如果是integer时,那么不能储存空,所以字段类型最好用String类型。
    3)表中增加字段或新增表时,配置文件里的版本好必须加1,可以从低版本往高版本升级,不可从高往低降级,如果建立了多个app项目时,每个项目的数据库名称如果是使用的同一个,必须统一版本号,否则会报错。

    本文参考文档:
    https://blog.csdn.net/xy8199/article/details/78690545
    https://www.jianshu.com/p/a490daa82fcb
    https://blog.csdn.net/ITxiaodong/article/details/81570800
    https://www.jianshu.com/p/ec37ea99ef69
    数据库的升级:
    https://blog.csdn.net/pingping_010/article/details/80669282

    相关文章

      网友评论

        本文标题:Android App开发 之 SQLite 整合 greenD

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