greenDAO-orm-320.png
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.png
- 在 根目录/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这个类. 如下图:
SQLiteOpenHelper.png
错误信息.png
②. 再试尝试, 重写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()
//.orderDesc(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 不是空的