IT@程序员猿媛Java架构技术进阶

Android App开发 之 SQLite 整合 greenD

2019-07-12  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读