GreenDao是一個對象關(guān)系型框架,也就是我們常說的ORM框架,使用它可以通過操作對象的方式去操作數(shù)據(jù)庫绰沥。從而不用手寫sql代碼,簡化開發(fā)贺待。
本文針對GreenDao3.2.2版本進(jìn)行操作徽曲,為什么需要強(qiáng)調(diào)版本號? 因?yàn)镚reenDao2和GreenDao3有比較大的差別,3.0以后相比較之前的版本操作更加的方便麸塞。
添加依賴
1秃臣,在bulid.gradle文件下的dependencies下添加所需依賴
compile 'org.greenrobot:greendao-generator:3.2.2'
compile 'org.greenrobot:greendao:3.2.2' // add library
2,在bulid.gradle下進(jìn)行配置
apply plugin: 'org.greenrobot.greendao'
greendao{
schemaVersion 2 //升級greenDao版本號
daoPackage '生成文件包名' // 一般為app包名+生成文件的文件夾名
targetGenDir 'src/main/java' //生成的路徑
}
3,在project下的build.gradle配置
dependencies {
//其他省略
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
}
上面的配置也就算完了奥此。
我們先明確一下需求:創(chuàng)建一個名為test.db的數(shù)據(jù)庫弧哎,而數(shù)據(jù)庫中有一張表user,需要使用greenDao對user表進(jìn)行CRUD操作稚虎,數(shù)據(jù)庫升級等常用的操作撤嫩。
創(chuàng)建實(shí)體類
先需要定義個User類并標(biāo)明@Entity注解和數(shù)據(jù)庫中的表進(jìn)行對應(yīng)
@Entity
public class User {
@Id(autoincrement = true)
private Long id;
private String name;
private int grade;
@Transient//Transient表示該字段不參與序列化操作,不作為表中列名
private int tempUsageCount;
//…set(),get()方法省略
public Integer age;
@Generated(hash = 1097688115)
public User(Long id, String name, int grade, Integer age) {
this.id = id;
this.name = name;
this.grade = grade;
this.age = age;
}
@Generated(hash = 586692638)
public User() {
}
}
需要注意的是@Id:通過這個注解標(biāo)記的字段必須是包裝類型Long類型蠢终,表示該字段為主鍵序攘,并且它默認(rèn)就是自增。否則在升級/降級數(shù)據(jù)庫的時候蜕径,會報錯两踏。
生成數(shù)據(jù)庫操作類
成功構(gòu)建工程之后,GreenDao會生成UserDao,DaoSession,DaoMaster三個文件兜喻。自動生成的Dao梦染,Session,Master官方是不建議直接修改這些文件的代碼朴皆,因?yàn)楫?dāng)數(shù)據(jù)庫需要升級/降級的時候使用生成器重新生成將會覆蓋之前修改過的代碼
為方便操作帕识,我們先封裝一個DBManager類:
public class DBManager {
private static final String DB_NAME = "test.db";
private static DBManager mDbManager;
private static DaoMaster.DevOpenHelper mDevOpenHelper;
private static volatile DaoMaster mDaoMaster;
private static volatile DaoSession mDaoSession;
private Context mContext;
private DBManager(Context context) {
this.mContext = context;
// 初始化數(shù)據(jù)庫信息
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;
}
/**
* 獲取可讀數(shù)據(jù)庫
*
* @param context
* @return
*/
public static SQLiteDatabase getReadableDatabase(Context context) {
if (null == mDevOpenHelper) {
getInstance(context);
}
return mDevOpenHelper.getReadableDatabase();
}
/**
* 獲取可寫數(shù)據(jù)庫
*
* @param context
* @return
*/
public static SQLiteDatabase getWritableDatabase(Context context) {
if (null == mDevOpenHelper) {
getInstance(context);
}
return mDevOpenHelper.getWritableDatabase();
}
/**
* 獲取DaoMaster
* <p>
* 判斷是否存在數(shù)據(jù)庫,如果沒有則創(chuàng)建數(shù)據(jù)庫
*
* @param context
* @return
*/
public static DaoMaster getDaoMaster(Context context) {
if (null == mDaoMaster) {
synchronized (DbManager.class) {
if (null == mDaoMaster) {
mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
mDaoMaster = new DaoMaster(mDevOpenHelper.getWritableDb());
}
}
}
return mDaoMaster;
}
/**
* 獲取DaoSession
*
* @param context
* @return
*/
public static DaoSession getDaoSession(Context context) {
if (null == mDaoSession) {
synchronized (DBManager.class) {
mDaoSession = getDaoMaster(context).newSession();
}
}
return mDaoSession;
}
}
對數(shù)據(jù)表的進(jìn)行操作
添加User表數(shù)據(jù)
/**
* 向User表中添加一條記錄
*/
private void addData() {
DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
mUser = new User();
mUser.setName("Hubery" + System.currentTimeMillis());
mUser.setAge(15);
userDao.insert(mUser);
}
/**
* 更新一條記錄
* @param user
*/
public void updateUser(User user) {
DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.update(user);
}
/**
* 刪除一條記錄
*
* @param user
*/
public void deleteUser(User user) {
DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.delete(user);
}
/**
* 查詢用戶列表
*/
public User queryUserModel(String curName) {
DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
QueryBuilder<User> qb = userDao.queryBuilder();
qb.where(UserDao.Properties.Name.like("%" + curName + "%")).build();
List<User> list = qb.list();
if (list != null && list.size() > 0) {
Toast.makeText(mContext, list.get(0).getName(), Toast.LENGTH_LONG).show();
return list.get(0);
} else {
return null;
}
}
/**
* 查詢用戶列表
*/
public List<User> queryUserList() {
DaoMaster daoMaster = DBManager.getDaoMaster(mContext);
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
QueryBuilder<User> qb = userDao.queryBuilder();
List<User> list = qb.list();
return list;
}
當(dāng)然GreenDao為我們封裝的對表的操作還不僅僅是這些遂铡,包括表一對多等復(fù)雜操作肮疗,感興趣的可以自行去查閱相關(guān)文檔。
數(shù)據(jù)庫中表升級/降級
說完GreenDao對數(shù)據(jù)表的基本操作之后扒接,再來看看數(shù)據(jù)庫的升級伪货,數(shù)據(jù)庫的升級需要繼承android.database.sqlite包中的SQLiteOpenHelper類,升級/降級的操作需要在onUpgrade()方法中執(zhí)行钾怔。
/**
* 數(shù)據(jù)庫升級
* Created by hubery on 2018/2/9.
*/
public class OpenHelper extends DaoMaster.OpenHelper {
public OpenHelper(Context context, String name) {
super(context, name);
}
public OpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
//判斷條件
//操作數(shù)據(jù)庫的更新 有幾個表升級都可以傳入到下面
MigrationHelper.getInstance().migrate(db,UserDao.class);
}
}
當(dāng)然在這里我們只是做一個功能的演示碱呼,并沒有拿oldVersion和newVersion版本號去控制升級/降級。現(xiàn)在我們把升級/降級的操作都放到MigrationHelper中去宗侦,先來看看這個類:
**
* Created by hubery on 2018/2/8.
*/
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(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateTempTables(db, daoClasses);
DaoMaster.dropAllTables(db, true);// 重新創(chuàng)建新的數(shù)據(jù)庫表
DaoMaster.createAllTables(db, false);
restoreData(db, daoClasses);
}
/**
* 生成臨時列表
*
* @param db
* @param daoClasses
*/
private void generateTempTables(Database 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) {
exception.printStackTrace();
}
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());
}
}
/**
* 存儲新的數(shù)據(jù)庫表 以及數(shù)據(jù)
*
* @param db
* @param daoClasses
*/
private void restoreData(Database 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()));
exception.printStackTrace();
throw exception;
}
private 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;
}
}
public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateTempTables(db, daoClasses);
restoreData(db, daoClasses);
}
這里升級的步驟:先創(chuàng)建一張臨時表愚臀,把表中數(shù)據(jù)插入到臨時表中,然后新建一個新的表矾利,把臨時表的數(shù)據(jù)拷貝到新表里面去姑裂,最后刪除臨時表。
性能效率對比
因?yàn)镚reenDao框架對表進(jìn)行CRU操作的時候男旗,由于其本身底層使用的不是反射舶斧,而是編譯時期生成的代碼,而不是反射的機(jī)制察皇,所以相對比其他的ORM關(guān)系型數(shù)據(jù)庫而言捧毛,效率會高很多。
這是官網(wǎng)的一張對比截圖以及官網(wǎng)的一段描述:
所有的ORM關(guān)系型數(shù)據(jù)庫中,greendao是最快的呀忧。greendao不作任何妥協(xié)的性能师痕。數(shù)據(jù)庫非常適合存儲大量數(shù)據(jù),因此速度很快而账。使用greendao胰坟,大多數(shù)實(shí)體可以插入,更新泞辐,以每秒幾千單位率加載笔横。
另外提一下ObjectBox:ObjectBox是移動端數(shù)據(jù)庫框架,靈感來自于NoSql咐吼,速度非炒档蓿快,號稱是市面上最快的移動端數(shù)據(jù)庫框架锯茄。目前非關(guān)系行數(shù)據(jù)庫也就只有Realm 能與之相比厢塘。
本文主要講解了一下greendao的基本使用,包括引入greendao,greendao對于單表的CRUD操作肌幽,以及數(shù)據(jù)庫升級晚碾,性能對比等。