https://github.com/greenrobot/greenDAO
导入工程
在Project目录的build.gradle文件中添加
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
}
}
apply plugin: 'org.greenrobot.greendao'
dependencies {
compile 'org.greenrobot:greendao:3.2.0'
}
defaultConfig {
applicationId "demo.app.android.greendao3xdemo"
versionCode 1
}
greendao {
schemaVersion 1
daoPackage 'demo.app.android.greendao3xdemo.db'
targetGenDir 'src/main/java'
}
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;
}
之后Build Moudle,会自动生成构造方法、get、set等
此刻Moudle中应该会产生类似如下的目录结构
在具体数据的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;
}
}
简单的保存动作
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);
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;
}
}
数据库版本升级
版本发布上架后,下一个版本需要添加新功能给用户设置头像
那么首先
@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();
}
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));
}
}
利用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.*{ *; } (实体类所在的包)