簡介
greendao是一個開源的Android開發(fā)ORM使SQLite數(shù)據(jù)庫役拴,可以減輕和節(jié)省開發(fā)時間的同時處理低級別的數(shù)據(jù)庫需求祭隔。SQLite是一個很好的嵌入式關(guān)系數(shù)據(jù)庫妆丘,不過寫SQL解析查詢結(jié)果是相當(dāng)繁瑣和耗時的逝她,greendao可以讓你利用java對象來映射到數(shù)據(jù)庫表便捷的使用Java面相對象的API來處理增兢孝、刪窿凤、改、查的操作跨蟹。
特點
- 性能大(貌似是Android最快的ORM)
- 易用性高
- 內(nèi)存消耗極小
- 精簡的庫(小于100KB)
- 安全性高(支持SQLCipher來保持用戶的數(shù)據(jù)安全)
- 支持RxJava
資料
- 官網(wǎng):<u>http://greenrobot.org/greendao/</u>
- github:<u>https://github.com/greenrobot/greenDAO</u>
配置環(huán)境
在build.gradle中配置
apply plugin: 'com.android.library'
//應(yīng)用greendao插件
apply plugin: 'org.greenrobot.greendao'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
//greendao相關(guān)配置
greendao {
schemaVersion 1 //數(shù)據(jù)庫版本號
daoPackage 'com.greendao.dao' //greendao自動生成的dao文件包名
targetGenDir 'src/main/java' //greendao自動生成的目標(biāo)文件夾
}
//從maven中下載greendao插件
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
}
}
//添加greendao需要的編譯環(huán)境
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:recyclerview-v7:25.0.0'
compile 'org.greenrobot:greendao:3.2.0'
compile 'org.greenrobot:greendao-generator:3.2.0'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
}
然后點擊Sync Project with Gradle Files按鈕下載編譯環(huán)境雳殊,等待下載完成,環(huán)境配置就OK了窗轩。
開始
GreenDao3.0以后開始用注解配置數(shù)據(jù)表夯秃,使用起來非常快捷方便痢艺,下面代碼簡單的配置了User表仓洼,表里面有id和name兩個字段
@Entity
public class User{
@Id
private Long id;
@NotNull
private String name;
}
然后用Build里面的Make Project或Make Module來自動生成相關(guān)DAO類和get set方法,完成之后堤舒,在我們定義好的com.greendao.dao包下也會多出DaoMaster色建、DaoSession、UserDao三個類舌缤,代碼這里就不貼出來了箕戳,User實體會生成如下:
@Entity
public class User{
@Id
private Long id;
@NotNull
private String name;
@Generated(hash = 1709734220)
public User(Long id, @NotNull String name) {
this.id = id;
this.name = name;
}
@Generated(hash = 586692638)
public User() {
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
<b>數(shù)據(jù)表注解@Entity的詳細(xì)配置</b>
@Entity(
// 目前還不是很清楚這個字段的功能和意義
schema = "myschema",
// 是否需要更新、刪除屬性和刷新方法国撵,默認(rèn)為true
active = true,
//指定數(shù)據(jù)庫中的表的名稱陵吸。默認(rèn)是實體類的名稱。
nameInDb = "AWESOME_USERS",
// 定義跨越多個列的索引介牙。
indexes = {
@Index(value = "name DESC", unique = true)
},
// 默認(rèn)為true,創(chuàng)建數(shù)據(jù)表壮虫,如果有多個實體映射到一個表或表創(chuàng)建在greenDao之外,則設(shè)置false
createInDb = false,
// 是否生成一個所有屬性的構(gòu)造函數(shù)耻瑟,無參構(gòu)造函數(shù)默認(rèn)生成
generateConstructors = true,
// 是否自動生成get和set方法
generateGettersSetters = true
)
public class User {
...
}
注意:如果定義了 schema="myschema"旨指,會報如下錯誤赏酥,具體解決方法暫時還沒找到,有知道的小伙伴可以留言告知一下哦
* What went wrong:
Execution failed for task ':greendaolibrary:greendao'.
> Undefined schema \"myschema\" (referenced in entities: User).
Please, define non-default schemas explicitly inside build.gradle
<b>各個字段的詳細(xì)注解</b>
@Id
主鍵ID注解谆构,屬性一般long或Long來定義裸扶,如果要設(shè)該字段為自增屬性,那么可以定義為@Id(autoincrement = true)
@Property
字段名定義注解搬素,@Property(nameInDb = "USER_NAME")
呵晨,默認(rèn)是駝峰法代替下劃線,所有字母大寫熬尺,如 customName 變成 CUSTOM_NAME
@NotNull
字段不能為空注解
@Transient
字段不添加到數(shù)據(jù)表注解
@Index
字段定義為索引注解摸屠,@Index(unique = true)
索引添加唯一約束,@Index(name = "indexName")
定義索引名
@Unique
字段定義唯一約束注解粱哼,并會創(chuàng)建索引
封裝
定義DbCore.java
類來獲取全局唯一的DaoSession
對象季二,代碼如下:
public class DbCore {
private static final String DEFAULT_DB_NAME = "green_dao.db";
private static DaoMaster daoMaster;
private static DaoSession daoSession;
private static Context mContext;
private static String DB_NAME;
public static void init(Context context) {
init(context, DEFAULT_DB_NAME);
}
public static void init(Context context,String dbName) {
if (context == null) {
throw new IllegalArgumentException("context can't be null");
}
mContext = context.getApplicationContext();
DB_NAME = dbName;
}
public static DaoMaster getDaoMaster() {
if (daoMaster == null) {
OnlineOpenHelper helper = new OnlineOpenHelper(mContext, DB_NAME, null);
daoMaster = new DaoMaster(helper.getWritableDatabase());
}
return daoMaster;
}
public static DaoSession getDaoSession() {
if (daoSession == null) {
if (daoMaster == null) {
daoMaster = getDaoMaster();
}
daoSession = daoMaster.newSession();
}
return daoSession;
}
public static void enableQueryBuilderLog(){
QueryBuilder.LOG_SQL = true;
QueryBuilder.LOG_VALUES = true;
}
}
OnlineOpenHelper升級幫助類
public class OnlineOpenHelper extends DaoMaster.OpenHelper{
public OnlineOpenHelper(Context context,String name) {
super(context, name);
}
public OnlineOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(Database db,int oldVersion,int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion );
}
}
需在在application
中初始化
DbCore.init(this); //初始化GreenDao
DbCore.enableQueryBuilderLog(); //打印sql語句日志
封裝所有的增刪改查、獲取Dao揭措、獲取RxDao胯舷、獲取QueryBuilder等方法 到BaseService.java
中
public class BaseService<T, K>{
private AbstractDao<T, K> mDao;
public BaseService(AbstractDao dao) {
mDao = dao;
}
public void save(T item) {
mDao.insert(item);
}
public void save(T... items) {
mDao.insertInTx(items);
}
public void save(List<T> items) {
mDao.insertInTx(items);
}
public void saveOrUpdate(T item) {
mDao.insertOrReplace(item);
}
public void saveOrUpdate(T... items) {
mDao.insertOrReplaceInTx(items);
}
public void saveOrUpdate(List<T> items) {
mDao.insertOrReplaceInTx(items);
}
public void deleteByKey(K key) {
mDao.deleteByKey(key);
}
public void delete(T item) {
mDao.delete(item);
}
public void delete(T... items) {
mDao.deleteInTx(items);
}
public void delete(List<T> items) {
mDao.deleteInTx(items);
}
public void deleteAll() {
mDao.deleteAll();
}
public void update(T item) {
mDao.update(item);
}
public void update(T... items) {
mDao.updateInTx(items);
}
public void update(List<T> items) {
mDao.updateInTx(items);
}
public T query(K key) {
return mDao.load(key);
}
public List<T> queryAll() {
return mDao.loadAll();
}
public List<T> query(String where,String... params) {
return mDao.queryRaw(where, params);
}
public QueryBuilder<T> queryBuilder() {
return mDao.queryBuilder();
}
public long count() {
return mDao.count();
}
public void refresh(T item) {
mDao.refresh(item);
}
public void detach(T item) {
mDao.detach(item);
}
public AbstractDao getDao(){
return mDao;
}
public RxDao<T,K> getRxDao(){
return mDao.rx();
}
public QueryBuilder<T> getQueryBuilder(){
return mDao.queryBuilder();
}
}
封裝User
表的所有操作到UserService.java
類中,如后面有多個表绊含,則只需簡單繼承BaseService
如下即可桑嘶,后面,所有相關(guān)User操作只需通過UserService.getInstance()
來實現(xiàn)所有操作
public class UserService extends BaseService<User,Long>{
public UserService() {
super(DbCore.getDaoSession().getUserDao());
}
public static UserService getInstance(){
return SingleLoader.INSTANCE;
}
private static class SingleLoader{
final static UserService INSTANCE = new UserService();
}
}
使用
<b>增</b>
public void insert(){
String userName = user_name.getText().toString();
if(TextUtils.isEmpty(userName)){
return;
}
User user = new User(null,userName);
//UserService.getInstance().save(user); //方式一
//UserService.getInstance().getDao().save(user); //方式二
UserService.getInstance().getRxDao().insert(user) //方式三 Rx模式
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<User>() {
@Override
public void call(User note) {
search();
}
});
}
<b>刪</b>
public void delete(){
if(mDatas.size()<=0){
return;
}
User user = mDatas.get(0);
//UserService.getInstance().delete(user);
//UserService.getInstance().getDao().delete(user);
UserService.getInstance().getRxDao().delete(user)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Void>(){
@Override
public void call(Void aVoid){
search();
}
});
}
<b>改</b>
public void update(){
String userName = user_name.getText().toString();
if(TextUtils.isEmpty(userName)){
return;
}
User user = mDatas.get(0);
user.setName(userName);
//UserService.getInstance().update(user);
//UserService.getInstance().getDao().update(user);
UserService.getInstance().getRxDao().update(user)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<User>(){
@Override
public void call(User user){
search();
}
});
}
<b>查</b>
ID降序查詢所有數(shù)據(jù)
public void search() {
//List<User> users = UserService.getInstance().getQueryBuilder().orderDesc(UserDao.Properties.Id).build().list();
UserService.getInstance().getQueryBuilder().orderDesc(UserDao.Properties.Id).rx().list()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<User>>(){
@Override
public void call(List<User> users){
mDatas.clear();
for(int i = 0;i<users.size();i++){
User user = users.get(i);
mDatas.add(user);
}
adapter.notifyDataSetChanged();
}
});
}
用Rx模式模糊查詢
public void searchByName() {
String userName = user_name.getText().toString();
if(TextUtils.isEmpty(userName)){
return;
}
//方式一
//List<User> users = UserService.getInstance().getDao().queryBuilder().where(UserDao.Properties.Name.like("%"+userName+"%")).build().list();
//方式二
//List<User> users = UserService.getInstance().getDao().queryBuilder().where(new WhereCondition.PropertyCondition(UserDao.Properties.Name, " LIKE ?", "%"+userName+"%")).build().list();
//方式三
UserService.getInstance().getQueryBuilder().where(new WhereCondition.PropertyCondition(UserDao.Properties.Name, " LIKE ?", "%"+userName+"%")).rx().list()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<User>>(){
@Override
public void call(List<User> users){
mDatas.clear();
for(int i = 0;i<users.size();i++){
User user = users.get(i);
mDatas.add(user);
}
adapter.notifyDataSetChanged();
}
});
}
升級
<b>字段更新</b>
這里借鑒了國外大牛寫的一個工具類躬充,代碼如下:
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);
DaoMaster.createAllTables(db, false);
restoreData(db, 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();
//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());
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());
}
}
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()));
//Crashlytics.logException(exception);
throw exception;
}
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;
}
}
具體應(yīng)用只需要在OnlineOpenHelper
的onUpgrade
方法中如下使用
switch(oldVersion){
//User表添加字段或刪除字段更新
case 1: MigrationHelper.getInstance().migrate(db,UserDao.class);
}
<b>新增數(shù)據(jù)表</b>
@Entity
public class Role{
@Id
private Long id;
@NotNull
private String roleName;
}
在OnlineOpenHelper的onUpgrade方法添加RoleDao.createTable(db,false)
,如下:
switch(oldVersion){
//User表添加字段或刪除字段更新
case 1: MigrationHelper.getInstance().migrate(db,UserDao.class);
//第二個參數(shù)true or false 是否需要檢查是否存在該表
case 2: RoleDao.createTable(db,false);
}
到這里greenDao簡單的操作處理都基本OK了逃顶!
寫在最后的話:很多事情都從小事做起,堅持不懈充甚,那么最終你也會從小牛變成大牛的以政。