greenDAO是一款輕鬆快速的Android ORM解決方案,可將對象映射到SQLite數據庫.
這裏只是介紹其中的一部分功能, 想了解更多還是得去官網看看.
github地址: https://github.com/greenrobot/greenDAO
提示:使用前需要申請讀寫權限
一.導入greenDAO
1. 項目根目錄下, build.gradle,如下圖:
mavenCentral()
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' //greendao
- 在 根目錄/app/build.gradle文件,添加
apply plugin: 'org.greenrobot.greendao'
android 節點下:
android{
greendao{
schemaVersion 1 //數據庫版本號
daoPackage'greendao' //編譯時生成文件 所在的包名
targetGenDir'src/main/java' //編譯時生成文件 所在的目錄
}
}
implementation 'org.greenrobot:greendao:3.2.2' //數據庫
2. 建立數據庫實體模型
@Entity
public class User{
@Id
String name= "";
String age= "0.00";
String lunch = "0.00";
}
@Entity 標記該類爲數據庫實體模型,指示GreendDao生成代碼
@Id 主鍵
三. 使用
1.默認方式
DbOpenHelper helper = new DaoMaster.DevOpenHelper(context, "數據庫文件名", null);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
//然後就可以用userDao 操作數據庫了.
2. 自定義保存路徑
發現問題: 數據庫文件保存在哪兒? 可以自定義路徑嗎?
網上搜到, 默認保存的目錄是 /data/data/Package Name/database. 可是卻不知道爲什麼?
最終還是得跑去看看源碼. 這裏簡單說一下:
①. 我們一開始就new DaoMaster.DevOpenHelper這個對象, 我們對着它的構造函數一層一層進去看, 發現最終調用的是android系統自帶的 SQLiteOpenHelper.java這個類. 如下圖:
②. 再試嘗試, 重寫Context中的openOrCreateDatabase, 結果成功了.
③. 看過源碼的應該知道,我們默認傳進去的Context實際上就是ContextWrapper, 於是有了以下寫法:
最終解決方法:
public class DatabaseContext extends ContextWrapper {
public static String dbPath = "";
public DatabaseContext(Context base, String dbPath) {
super(base);
if(!TextUtils.isEmpty(dbPath)){
this.dbPath = dbPath;
}
}
@Override
public File getDatabasePath(String name){
File dbDir = new File(dbPath);
if(!dbDir.exists()){
dbDir.mkdir();
}
File dbFile = new File(dbPath, name);
if(!dbFile.exists()){
try {
dbFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return dbFile;
}
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
return result;
}
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), null);
return result;
}
}
使用
DbOpenHelper helper = new DaoMaster.DevOpenHelper(new DatabaseContext(getApplication(), "數據庫目錄"), "數據庫文件名", null);
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao()
3. 數據升級出現的問題: 源數據會被覆蓋
解決辦法: 複製一份數據備份, 被覆蓋後重新保存
具體做法: 新增兩個輔助類: MigrationHelper.java 和 DbOpenHelper.java
/**
* @Description: 數據庫升級輔助
* @Author: liys
* @CreateDate: 2019/6/17 14:15
* @UpdateUser: 更新者
* @UpdateDate: 2019/6/17 14:15
* @UpdateRemark: 更新說明
* @Version: 1.0
*/
public class MigrationHelper {
public static void migrate(SQLiteDatabase sqliteDatabase, Class<? extends AbstractDao<?, ?>>... daoClasses) {
StandardDatabase db = new StandardDatabase(sqliteDatabase);
generateNewTablesIfNotExists(db, daoClasses);
generateTempTables(db, daoClasses);
dropAllTables(db, true, daoClasses);
createAllTables(db, false, daoClasses);
restoreData(db, daoClasses);
}
public static void migrate(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateNewTablesIfNotExists(db, daoClasses);
generateTempTables(db, daoClasses);
dropAllTables(db, true, daoClasses);
createAllTables(db, false, daoClasses);
restoreData(db, daoClasses);
}
private static void generateNewTablesIfNotExists(StandardDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "createTable", true, daoClasses);
}
private static void generateTempTables(StandardDatabase 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");
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("CREATE TEMP TABLE ").append(tempTableName);
insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
}
}
private static void dropAllTables(StandardDatabase db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "dropTable", ifExists, daoClasses);
}
private static void createAllTables(StandardDatabase db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "createTable", ifNotExists, daoClasses);
}
/**
* dao class already define the sql exec method, so just invoke it
*/
private static void reflectMethod(StandardDatabase db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
if (daoClasses.length < 1) {
return;
}
try {
for (Class cls : daoClasses) {
Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
method.invoke(null, db, isExists);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private static void restoreData(StandardDatabase 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");
// get all columns from tempTable, take careful to use the columns list
List<String> columns = getColumns(db, tempTableName);
ArrayList<String> properties = new ArrayList<>(columns.size());
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (columns.contains(columnName)) {
properties.add(columnName);
}
}
if (properties.size() > 0) {
final String columnSQL = TextUtils.join(",", properties);
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
}
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(dropTableStringBuilder.toString());
}
}
private static List<String> getColumns(StandardDatabase db, String tableName) {
List<String> columns = null;
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
if (null != cursor && cursor.getColumnCount() > 0) {
columns = Arrays.asList(cursor.getColumnNames());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
if (null == columns)
columns = new ArrayList<>();
}
return columns;
}
}
/**
* @Description: 數據庫升級輔助
* @Author: liys
* @CreateDate: 2019/6/17 14:11
* @UpdateUser: 更新者
* @UpdateDate: 2019/6/17 14:11
* @UpdateRemark: 更新說明
* @Version: 1.0
*/
public class DbOpenHelper extends DaoMaster.DevOpenHelper {
public DbOpenHelper(Context context, String name) {
super(context, name);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
//切記不要調用super.onUpgrade(db,oldVersion,newVersion)
if (oldVersion < newVersion) {
MigrationHelper.migrate((StandardDatabase) db,
SpendMoneyBeanDao.class //具體操作數據庫的dao類, 如果需要多個,用逗號隔開
//TestDao1.class,
//TestDao2.class
);
}
}
}
使用:
DbOpenHelper helper = new DbOpenHelper(new DatabaseContext(context, "數據庫目錄"), "數據庫文件名");
DaoMaster daoMaster = new DaoMaster(helper.getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao()
4. 增刪改查
UserDao userDao = daoSession.getUserDao()
//增
userDao.insert(userBean);
//刪
userDao.delete(userBean);
//改
userDao.update(userBean)
//查
//1. 查詢所有
userDao.queryBuilder().list();
//2. 單條件查詢(名字=liys)
userDao.queryBuilder()
.where(UserDaoDao.Properties.Name.eq("liys"))
.list();
//3. 多條件查詢, 同時滿足(name=liys && age = 18)
userDao.queryBuilder()
.where(UserDaoDao.Properties.Name.eq("liys"),
UserDaoDao.Properties.Age.eq("18"))
.list();
//4. 多條件查詢(或)
userDao.queryBuilder()
.whereOr(UserDaoDao.Properties.Name.eq("liys"),
UserDaoDao.Properties.Age.eq("18"))
//5. 查出來排序
serDao.queryBuilder()
//.orderAsc(UserDaoDao.Properties.Age) //升序
.orderAsc(UserDaoDao.Properties.Age) //升序
.list();
5. 查詢條件總結
eq : 等於
notEq : 不等於
like: 模糊查詢 記住模糊查詢,string要用夾在%key%中間。
//查詢Name包含liys的人。
xxDao.queryBuilder().where(Properties.Name.like("%liys%")).list();
IN(..., ..., ...) 在給出的value的範圍內的符合項
gt: 大於
ge 大於等於
lt 小於
le 小於等於
isNull 不是空的