先润润嗓子,听我细细道来:
今天我们来介绍一款性能非常好的orm数据库框架GreenDao,相信大家一定对它有所耳闻,或者已经在项目中在使用它了,虽然我在去年就开始使用这款框架,但是一直没有做过系统的整理和梳理,有些地方也是含糊不清,今天就和大家一起来揭开GreenDao的神秘面纱。
下面是官网对greendao以及其他常见的orm数据库的性能测试图:
通过这张图我们可以看出其性能远远超出其他同类型的数据库框架,greendao执行效率很高,且引入库文件较小,占用更少的内存 ,操作实体灵活,它可以帮助Android开发者快速将Java对象映射到SQLite数据库中,通过使用一个简单的面向对象API,开发者可以对Java对象进行存储、更新、删除和查询。
下面我以eclipse开发工具为例讲解,至于android studio一句话即可搞定:
compile 'org.greenrobot:greendao:2.2.0'
一、android工程
新建android工程,指定自己的包名,此处我的包名为com.greendao.use,在com.greendao.use路径下新建package,com.greendao.use.db.localdb用于存放生成的数据库文件,此处的包名大家可以随意指定,但是在java工程中需要指定对应的包路径,方便引入的时候不会产生包名错误;
二、java工程
首先我们新建java工程,在工程根目录新建libs目录,把需要的包复制到libs下,这两个包在文章结尾的源码里会有,需要的可以去下载:
此处我们准备在数据库中添加一个student表,那么现在我们新建一个java类,并添加如下方法:
/** * <学生表> */
private static void addStudent(Schema schema)
{
// 设置表名,此处的对应android工程中的实体类名称
Entity student = schema.addEntity("Student");
// 设置表名,此处的对应数据库中的表名
student.setTableName("t_student");
// 设置主键为默认增长的id,也可以使用primaryKey指定任意属性为主键
student.addIdProperty().primaryKey().autoincrement();
// 学号,notNull指定为非空
student.addStringProperty("stuNum").columnName("stu_num").notNull();
// 名字
student.addStringProperty("name").columnName("name");
// 性别
student.addStringProperty("sex").columnName("sex");
// 年龄
student.addStringProperty("age").columnName("age");
// 学分score
student.addStringProperty("score").columnName("score");
// school名称
student.addStringProperty("school").columnName("school");
}
然后我们在java类的main方法中添加如下代码:
public static void main(String[] args)
throws IOException, Exception
{
Schema schema = new Schema(1, "com.greendao.use.db.localdb");
addStudent(schema);
new DaoGenerator().generateAll(schema, "./blog");
}
注意:此处的com.greendao.use.db.localdb就是我android工程中用来存放数据库映射实体类的目录!blog是我在java工程中新建的一个用于存放生成实体类的目录:
此时我们运行java类,然后右键工程,Refresh一下工程,在blog目录下就会生成对应的数据库实体类文件:
—–Student.java类就是对应t_student表的实体类文件,
核心类介绍
DaoMaster:
daomaster以一定的模式持有数据库对象(SQLiteDatabase)并管理一些DAO类(而不是对象)。
有一个静态的方法创建和drop数据库表。它的内部类OpenHelper和DevOpenHelper是SQLiteOpenHelper的实现类,用于创建SQLite数据库的模式。
DaoSession:
管理指定模式下所有可用的DAO对象,你可以通过某个get方法获取到。DaoSession提供一些通用的持久化方法,比如对实体进行插入,加载,更新,刷新和删除。最后DaoSession对象会跟踪identity scope,更多细节,可以参看 session文档。
DAOs(Data access objects):
数据访问对象,用于实体的持久化和查询。对于每一个实体,greenDao会生成一个DAO,相对于DaoSession它拥有更多持久化的方法。
当然在DaoMaster类中还有一个比较重要的内部类DevOpenHelper,代码如下:
/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
public DevOpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
}
咦,onUpgrade方法,不是处理数据库升级的嘛,是的,此类就是处理数据库升级的类,但是注意看上面的警告 WARNING: Drops all table on Upgrade! Use only during development.意思就是如果使用默认生成的代码进行greedDao的升级,会删除所有的表之后重新创建,是无法保留老版本数据库中的数据的,所以实际情况下我们需要自己处理升级逻辑;
下面我们把生成的代码复制到android工程下
此处我们简单封装了一个数据库操作类DBController,方便进行数据库操作,代码都很简单,主要是获取DaoSession实例,大家也可以根据自己项目的实际需求封装一些常用的增删查改的方法,DBController.java类代码如下:
/** * 数据库控制类 */
public class DBController {
private static DaoMaster daoMasterEcmc;
private static DaoSession daoSessionEcmc;
/** * 数据库名称:localdata */
public static final String DATABASE_NAME = "localdata.db";
private static DaoMaster obtainMaster(Context context, String dbName)
{
return new DaoMaster(new DaoMaster.DevOpenHelper(context, dbName, null).getWritableDatabase());
}
private static DaoMaster getDaoMaster(Context context, String dbName)
{
if (dbName == null)
return null;
if (daoMasterEcmc == null)
{
daoMasterEcmc = obtainMaster(context, dbName);
}
return daoMasterEcmc;
}
/** * 取得DaoSession * * @return */
public static DaoSession getDaoSession(String dbName)
{
if (daoSessionEcmc == null)
{
daoSessionEcmc = getDaoMaster(MainApplication.getIns(), dbName).newSession();
}
return daoSessionEcmc;
}
/** * 默认操作localdata数据库 */
public static DaoSession getDaoSession()
{
if (daoSessionEcmc == null)
{
daoSessionEcmc = getDaoMaster(MainApplication.getIns(), DATABASE_NAME).newSession();
}
return daoSessionEcmc;
}
}
在libs下添加需要greendao库文件:
下面我们在MainActivity的onCreate中获取StudentDao实例,一句话:
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
然后我们先不进行任何操作,直接运行,然后去DDMS中查看我们的数据库有没有生成成功,路径为data/data/包名/databases下:
我们使用sqllitemanager工具打开localdata.db文件,看看咱们的t_student表有没有创建成功,可以看到表创建成功:
对应的属性:
至此,我们的数据库创建阶段已经完成,下面我们将学习一下基本的增删查改的使用,请各位备好纸巾!
/** * <插入数据> * * @see [类、类#方法、类#成员] */
private void addData()
{
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
//新建一个Student对象就相当于一条数据库的记录
Student student = new Student();
student.setStuNum("1001");
student.setName("刘德华");
student.setAge("22");
student.setSex("男");
student.setScore("3");
student.setSchool("清华大学");
//插入到数据库
studentDao.insert(student);
Student student1 = new Student();
student1.setStuNum("1002");
student1.setName("刘亦菲");
student1.setAge("26");
student1.setSex("女");
student1.setScore("5");
student1.setSchool("北京大学");
// 开启事务插入
studentDao.insertInTx(student1);
Student student2 = new Student();
student2.setStuNum("1003");
student2.setName("黄渤");
student2.setAge("30");
student2.setSex("男");
student2.setScore("12");
student2.setSchool("南京大学");
// 插入,如果存在覆盖之前的记录
studentDao.insertOrReplace(student2);
}
打开db文件看下有么有插入成功:
OK,成功!
至于插入操作,greendao还提供了其他的插入方法,比如批量插入等,大家可以自己去尝试使用下,以便做出更好的选择:
1.查询所有数据库所有记录:
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
//list()方法查询所有记录
List<Student> students = studentDao.queryBuilder().list();
2.查询指定条件(此处为性别为男的记录)的记录:
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryBuilder().where(StudentDao.Properties.Sex.eq("男")).list();
3.使用sql语句进行查询:
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryRaw("where name = ?", "刘德华");
我们再插入两条数据:
4.limit用法是取出查询记录的N条:
//查询前四条limit(4)
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryBuilder().limit(4).list();
4.offset是设置偏移量,下面是从第二个开始,查询三条数据:
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryBuilder().limit(3).offset(1).list();
5.orderAsc是对查询的数据进行升序排序,orderDesc是降序排序,此处根据年龄进行升序排序:
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryBuilder().orderAsc(StudentDao.Properties.Age).list();
关于greendao的查询功能,除了上述查询之外,还有一些其他的功能,大家可以自行尝试:
//把刘德华修改成周星驰,如果有多个,此处只修改第一个
List<Student> students = studentDao.queryBuilder().where(StudentDao.Properties.Name.eq("刘德华")).list();
Student entity = students.get(0);
entity.setName("周星驰");
studentDao.update(entity);
其他的更新方法:
//删除周星驰的第一条记录
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
QueryBuilder<Student> builder = studentDao.queryBuilder().where(StudentDao.Properties.Name.eq("周星驰"));
List<Student> students = builder.list();
studentDao.delete(students.get(0));
其他的删除方法:
升级没有什么好说的,就像前面讲到的那样,现在描述一下基本流程,比如我们添加个表或者修改某些字段,之后需要提高数据库版本,当执行程序的时候一旦发现新版本大于老版本,就会执行数据库更新操作,执行onUpgrade方法,我们的就在此处处理升级逻辑;
比如如果我们要给t_stduent表添加个字段的话,就可以这样来写:
public void onUpgrade(SQLiteDatabase db) {
db.execSQL("ALTER TABLE 't_student' ADD favorite TEXT");
}
运行查看数据表:
添加一张t_school表,我们可以这样写:
db.execSQL("CREATE TABLE 't_school' ('_id' INTEGER PRIMARY KEY AUTOINCREMENT ,'name' TEXT,'address' TEXT)");
查看数据库,添加成功:
虽然这样可以添加成功,别忘记使用java工程生成对应的实体类和Dao类等数据库操作类,不然是无法操作数据表的;写到这,大家说,麻烦死了,还得写sql语句,复杂的话又比较容易出问题,所以下面推荐一种好的方式来进行更新(代码来自:Pedro Okawa):
首先创建一个MyMigrationHelper数据库更新的工具类,具体代码如下:
/** * * please call {@link #migrate(SQLiteDatabase, Class[])} * */
public final class MyMigrationHelper {
private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
private static MyMigrationHelper instance;
public static MyMigrationHelper getInstance() {
if(instance == null) {
instance = new MyMigrationHelper();
}
return instance;
}
public void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
//创建临时表保存数据
generateTempTables(db, daoClasses);
//删除所有
DaoMaster.dropAllTables(db, true);
//创建所有
DaoMaster.createAllTables(db, false);
//恢复数据,删除临时表
restoreData(db, daoClasses);
}
private void generateTempTables(SQLiteDatabase 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) {
// 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(SQLiteDatabase 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(SQLiteDatabase 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;
}
}
使用时我们只需要在onUpgrade方法中调用migrate方法指定数据库中所有的Dao-class即可:
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//注意:class只需要添加已经存在的表,新增的表不需要添加
MyMigrationHelper.getInstance().migrate(db,StudentDao.class,SchoolDao.class);
}
我们现在添加一个t_teacher表,提高数据库版本,把生成的代码复制到android 工程,运行工程,执行后就会发现数据库里t_teacher表已经添加成功了。
而且比较重要的是原来stduent表中的信息依然被保留了下来:
只需简单的一句话即可完成数据库的升级操作;
总结:至此,关于greenDao的基本使用到这里已经完成了,本篇博文主要是记录了数据库的基本使用 ,都是对单表的一些基本操作,如果你仔细看了这篇博文,相信会对你有所帮助,如果你有任何问题,欢迎下方评论讨论,关于数据表的关联关系等操作,下篇会重点介绍;
源码下载:博文工程源码,需要jar包的也可以下载