Android greenDAO3x数据库框架使用小结

greenDAO是个很好用的Android数据库框架

https://github.com/greenrobot/greenDAO


导入工程

在Project目录的build.gradle文件中添加

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
    }
}

在Module目录中的build.gradle文件中添加

apply plugin: 'org.greenrobot.greendao'

dependencies {
    compile 'org.greenrobot:greendao:3.2.0'
}

完成上述动作后,接着给项目配置自动生成创建db代码文件的相关参数。
在Module中配置build.gradle参数

defaultConfig {
        applicationId "demo.app.android.greendao3xdemo"
        versionCode 1
    }
greendao {
    schemaVersion 1
    daoPackage 'demo.app.android.greendao3xdemo.db'
    targetGenDir 'src/main/java'
}


创建表对应实体类和简单的crud

greenDAO 2.x需要创建一个java项目来自动生成表对应的实体类,而3.x可以通过注解的方式来自动生成对应的实体类。
到目前为止,2.x和3.x的生成方式各有优缺点。3.x自动生成的实体类使用起来比2.x便捷,2.x的可以定制内容不同的数据库(多Schema)
不过个人感觉3.x还是比2.x好用很多


一般常见应用中都有用户模块,那么这里创建一个User类演示

package demo.app.android.greendao3xdemo.db.model;

@Entity
public class User {
    @Id private Long id;

    @Property(nameInDb = "USERNAME") private String username;
}

@Entity 建立表和实体的对应关系
@Id 建立表主键和类成员变量关系
@Property 建立表字段和类成员变量关系

之后Build Moudle,会自动生成构造方法、get、set等


此刻Moudle中应该会产生类似如下的目录结构

Android greenDAO3x数据库框架使用小结_第1张图片



在具体数据的crud之前,应该预先在全局的applicaiton对象中准备好数据库session对象

public class App extends Application {

    private static App instance;

    private DaoMaster.OpenHelper dbHelper;
    private DaoSession dbSession;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }

    public static synchronized App i() {
        return instance;
    }

    public DaoSession getDBSession() {
        if (dbSession != null) {
            dbSession.clear();
        }
        if (dbHelper == null) {
            dbHelper = new DaoMaster.DevOpenHelper(this, "db_user");
            DaoMaster daoMaster = new DaoMaster(dbHelper.getWritableDatabase());
            dbSession = daoMaster.newSession();
        }
        return dbSession;
    }
}


在一切都准备就绪后便可以开始实战。
添加登录界面并且加入登录逻辑(并且将用户保存至数据库)

Android greenDAO3x数据库框架使用小结_第2张图片

简单的保存动作

String sUsername = eUsername.getText().toString().trim();
UserDao userDao = App.i().getDBSession().getUserDao();
User user = new User();
user.setId(System.currentTimeMillis());
user.setUsername(sUsername);
userDao.insertOrReplace(user);

那么实际上,user的数据应用一般会用于各个界面,有必要将user作为成员保留在application对象中

public class App extends Application {

    public void setCurrUser(User user) {
        UserDao userDao = getDBSession().getUserDao();
        userDao.insertOrReplace(user);
        currUser = user;
        // 保存上一次的登录者id至SharedPreferences
        Utils.spWrite("last_login_user_id", currUser.getId());
    }

    public User getCurrUser() {
        if (currUser == null) {
            long lastLoginUserId = Utils.spReadLong("last_login_user_id", 0);
            if (lastLoginUserId > 0) {
                UserDao userDao = getDBSession().getUserDao();
                currUser = userDao.queryBuilder().where(UserDao.Properties.Id.eq(lastLoginUserId)).unique();
            }
        }
        return currUser;
    }
}

多用户数据库之间的切换

支持多用户登录在app中也是一种常态。那么在构建数据库的时候使用"一个用户对应一个库的策略方案"会有很多优势。

例如 添加新功能 用户可以创建的日记便签
表NOTE 字段 CONTENT
如果所有用户使用同一个数据库 
这里将必然添加新字段USER_ID之类的来建立日记便签与用户之间的关联关系,并且在查询数据的时候都必须添加'WHERE USER_ID = ' 之类的条件。
如果使用"一个用户对应一个库的策略方案",由于这个当前库的数据全部都是该用户的,查询效率提高的同时,代码书写也将简化。
那么就需要在用户切换账号登录的同时,程序里需要切换数据库。

public class App extends Application {

    private static App instance;

    private DaoMaster.OpenHelper dbHelper;
    private DaoSession dbSession;

    private User currUser;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }

    public static synchronized App i() {
        return instance;
    }

    public DaoSession getCurrDBSession() {
        getCurrUser();
        return getDBSession(currUser.getId());
    }

    private DaoSession getDBSession(long id) {
        if (dbSession != null) {
            dbSession.clear();
        }

        if (dbHelper != null &&
                (currUser == null || currUser.getId() != id)) {
            dbHelper.close();
            dbHelper = null;
        }

        if (dbHelper == null) {
            dbHelper = new DaoMaster.DevOpenHelper(this, "db_user_" + id);
            DaoMaster daoMaster = new DaoMaster(dbHelper.getWritableDatabase());
            dbSession = daoMaster.newSession();
        }
        return dbSession;
    }

    public void setCurrUser(User user) {
        if (user == null) {
            if (dbSession != null) {
                dbSession.clear();
            }
            if (dbHelper != null) {
                dbHelper.close();
                dbHelper = null;
            }
            currUser = null;
            Utils.spWrite("last_login_user_id", 0);
        } else {
            UserDao userDao = getDBSession(user.getId()).getUserDao();
            userDao.insertOrReplace(user);
            currUser = user;
            // 保存上一次的登录者id至SharedPreferences
            Utils.spWrite("last_login_user_id", currUser.getId());
        }
    }

    public User getCurrUser() {
        if (currUser == null) {
            long lastLoginUserId = Utils.spReadLong("last_login_user_id", 0);
            if (lastLoginUserId > 0) {
                UserDao userDao = getDBSession(lastLoginUserId).getUserDao();
                currUser = userDao.queryBuilder().where(UserDao.Properties.Id.eq(lastLoginUserId)).unique();
            }
        }
        return currUser;
    }
}

之后只需要在登录或者切换的时候调用
App.i().setCurrUser()
在全局任意位置需要获得User信息的时候调用
App.i().getCurrUser()


数据库版本升级

版本发布上架后,下一个版本需要添加新功能给用户设置头像
那么首先

@Entity
public class User {
    @Id private Long id;

    @Property(nameInDb = "USERNAME") private String username;
    @Property(nameInDb = "AVATAR") private String avatar;
}

defaultConfig {
        applicationId "demo.app.android.greendao3xdemo"
        versionCode 2
    }
greendao {
    schemaVersion 2
    daoPackage 'demo.app.android.greendao3xdemo.db'
    targetGenDir 'src/main/java'
}

重新Build项目

将这里的schemaVersion改为2后在dbHelper.getWritableDatabase()时刻满足一定条件下会触发onUpgrade方法。此时可以重写OpenHelper#onUpgrade方法更新数据库结构等
以上使用了DaoMaster.DevOpenHelper,而其中逻辑实现为

  dropAllTables(db, true);
  onCreate(db);
这样的话,之前版本数据都会丢失。

创建支持升级的OpenHelper

package demo.app.android.greendao3xdemo.db.upgrade;

public class UpgradeableOpenHelper extends DaoMaster.OpenHelper {

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

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        Utils.logd("greenDAO Upgrading schema from version %d to %d", oldVersion, newVersion);

        if (oldVersion == 1 && newVersion == 2) {
            final long alterDate = System.currentTimeMillis();
            // step 1 将表名改为临时表
            db.execSQL("ALTER TABLE \"USER\" RENAME TO \"_USER_old_" + alterDate + "\"");
            // step 2 创建新表
            UserDao.createTable(db, false);
            // step 3 导入数据
            final String oldC = "_id, USERNAME";
            final String newC = oldC;
            db.execSQL("INSERT INTO \"USER\" (" + newC + ") SELECT " + oldC + " FROM \"_USER_old_" + alterDate + "\"");
            // step 4 删除临时表
            db.execSQL("DROP TABLE \"_USER_old_" + alterDate + "\"");
        }
    }
}

if (dbHelper == null) {
            dbHelper = new UpgradeableOpenHelper(this, "db_user_" + id);
            DaoMaster daoMaster = new DaoMaster(dbHelper.getWritableDatabase());
            dbSession = daoMaster.newSession();
        }

随着应用日渐更新,必然的之后将会出现版本3,4,5...
那么这里有必要对UpgradeableOpenHelper的代码进一步优化

public class UpgradeableOpenHelper extends DaoMaster.OpenHelper {

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

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        Utils.logd("greenDAO Upgrading schema from version %d to %d", oldVersion, newVersion);

        for (int i = oldVersion; i < newVersion; i++) {
            try {
                AbstractDBMigratorHelper migratorHelper =
                        (AbstractDBMigratorHelper) Class.forName(
                                AbstractDBMigratorHelper.UPGRADE_PACKAGE_NAME + ".DBMigrationHelper" + (i + 1)).newInstance();
                if (migratorHelper != null) {
                    migratorHelper.onUpgrade(db);
                }
            } catch (Exception e) {
                e.printStackTrace();
                Utils.loge("greenDAO Could not migrate from schema from schema: %d to %d", i, i + 1);
                break;
            }
        }
    }
}

package demo.app.android.greendao3xdemo.db.upgrade;

import org.greenrobot.greendao.database.Database;

import java.util.Date;

public abstract class AbstractDBMigratorHelper {
    public static final String UPGRADE_PACKAGE_NAME = "demo.app.android.greendao3xdemo.db.upgrade";

    public abstract void onUpgrade(Database db);

    public static final void createTemporaryTable(Database db, String tableName, Date createDate) {
        final String ALTER_TABLE_RENAME = "ALTER TABLE \"%s\" RENAME TO \"_%s_old_%ts\"";
        db.execSQL(String.format(ALTER_TABLE_RENAME, tableName, tableName, createDate));
    }

    public static final void dataTransferFromTemporary(Database db, String tableName, Date tempDate, String oldC, String newC) {
        final String DATA_TRANSFER = "INSERT INTO \"%s\" (%s) SELECT %s FROM \"_%s_old_%ts\"";
        db.execSQL(String.format(DATA_TRANSFER, tableName, newC, oldC, tableName, tempDate));
    }

    public static final void dropTemporaryTable(Database db, String tableName, Date createDate) {
        final String DROP_TABLE = "DROP TABLE \"_%s_old_%ts\"";
        db.execSQL(String.format(DROP_TABLE, tableName, createDate));
    }
}

Android greenDAO3x数据库框架使用小结_第3张图片

利用Class.forName("").newInstance()和递归机制 将数据库迭代升级的逻辑转移至DBMigrationHelper中
例如刚才的version1升级到version2的逻辑可以简化为

public class DBMigrationHelper2 extends AbstractDBMigratorHelper {

    @Override
    public void onUpgrade(Database db) {
        Date alterDate = new Date();
        // step 1 将表名改为临时表
        createTemporaryTable(db, UserDao.TABLENAME, alterDate);
        // step 2 创建新表
        UserDao.createTable(db, false);
        // step 3 导入数据
        final String oldC = "_id, USERNAME";
        final String newC = oldC;
        dataTransferFromTemporary(db, UserDao.TABLENAME, alterDate, oldC, newC);
        // step 4 删除临时表
        dropTemporaryTable(db, UserDao.TABLENAME, alterDate);
    }
}

数据库内容加密

http://greenrobot.org/greendao/documentation/database-encryption/



混淆规则 引用自‘http://www.jianshu.com/p/cb4d346d7e2a’

### greenDAO 3
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties

# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use RxJava:
-dontwarn rx.**

### greenDAO 2
-keepclassmembers class * extends de.greenrobot.dao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
-keep class com.cn.daqi.otv.db.*{ *; } (实体类所在的包)

你可能感兴趣的:(Android,数据库,greenDAO)