Android笔记之greenDao3.0学习总结
Posted mictoy_朱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android笔记之greenDao3.0学习总结相关的知识,希望对你有一定的参考价值。
前言
本博文旨在记录greenDao3.0的基本用法以及基于greenDao3.0的数据库的升级策略。本文基于Window10下android Studio环境操作使用。废话不多说,直入正题。
关于greenDao3.0
greenDao是一个对象关系映射(ORM)的框架,能够提供一个接口通过操作对象的方式去操作关系型数据库,它能够让你操作数据库时更简单、更方便。如下图来之其官网的图片:
官网地址:http://greenrobot.org/greendao/
github地址:https://github.com/greenrobot/greenDAO
关于greenDao3.0之前的2.0、1.0版本,本人没有接触(现在似乎也没必要再去了解)过。据网上大神介绍,3.0之前需要通过新建GreenDaoGenerator工程生成Java数据对象(实体)和DAO对象,非常的繁琐而且也加大了使用成本。而GreenDao3.0最大的变化就是采用注解的方式通过编译方式生成Java数据对象和DAO对象。
greenDao3.0特点
greenDao3.0主要有以下特点:
· 1.性能高,号称Android最快的关系型数据库
2.内存占用小
· 3.库文件比较小,小于100K,编译时间低,而且可以避免65K方法限制
· 4.支持数据库加密 greendao支持SQLCipher进行数据库加密
5.简洁易用的API
greenDao3.0使用
greenDao3.0基本配置:
在项目的build.gradle添加如下配置:
dependencies
classpath 'org.greenrobot:greendao-gradle-plugin:3.0.0'
这里添加的是greenDao插件路径。
在主工程下的build.gradle下添加如下配置:
apply plugin: 'org.greenrobot.greendao'
dependencies
compile 'org.greenrobot:greendao:3.0.1'
如以上配置所示,greenDao3.0使用需要依赖greenDao插件配合使用。完成这些基本配置后我们同步编译(Sync Project),没毛病,接着往下。
greenDao基本使用:
现在我们就可以使用greenDao框架了,新建实体类:
@Entity
public class User
@Id(autoincrement = true)
private Long id;
private String name;
private int age;
...
注意:因为我们要使用greenDao3.0框架,所以我们的实体类要添加greenDao3.0的注解@Entity,表示该实体类的相关数据要添加到数据库中。此外,我们还对实体类中id字段添加了注解@Id(autoincrement = true) 表示将该字段设为主键并自动增长,需要注意的是,主键是Long型,不是long。
此外,定义实体类时对于字段还可以添加以下注解:
- @Id :主键 Long型,可以通过@Id(autoincrement = true)设置自增长
- @Property:设置一个非默认关系映射所对应的列名,默认是的使用字段名 举例:@Property (nameInDb="name")
- @NotNul:设置数据库表当前列不能为空
- @Transient :添加次标记之后不会生成数据库表的列
再次同步编译,编译完成后,我们可以发现,User类中已经自动给我们添加了字段对应的setter、getter方法以及构造函数:
public int getAge()
return this.age;
public void setAge(int age)
this.age = age;
public String getName()
return this.name;
public void setName(String name)
this.name = name;
public Long getId()
return this.id;
public void setId(Long id)
this.id = id;
@Generated(hash = 1309193360)
public User(Long id, String name, int age)
this.id = id;
this.name = name;
this.age = age;
@Generated(hash = 586692638)
public User()
这些都是greenDao3.0插件为我们自动生成的代码。此外,我们还可以在app/build/generated/source目录下新增了一个greendao目录,里面有三个java文件分别是DaoMaster、DaoSession、UserDao。没错,这些文件也是greenDao3.0插件为我们自动生成的。这些类就是我们后面操作数据库主要依赖的类。
那么问题来了,如果我想让这些文件放到其它目录下该怎么办?good question!
这时我们就可以在主工程下的build.gradle添加相应的辅助配置(比如我想将这些文件放在src/mian/java目录下):
greendao
targetGenDir 'src/main/java'
这时,我们重新编译,就会发现这些文件已经转移到这个目录下了。
这里的greendao,我们还可以添加下面一些数据库的基本属性配置
- schemaVersion: 数据库schema版本,也可以理解为数据库版本号
- daoPackage:设置DaoMaster 、DaoSession、Dao包名
- targetGenDir:设置DaoMaster 、DaoSession、Dao目录
- targetGenDirTest:设置生成单元测试目录
- generateTests:设置自动生成单元测试用例
greenDao简单操作
借助上面生成的DaoMaster、DaoSession、UserDao三个类,我们可以创建一个数据库工具类DBManager
-------------------------------------------------------明天接着写
。。。一天后,继续……
-------------------------------------------------------
首先明确我们这个工具类的功能。很明显,对于数据库的操作,无非就是增、删、改、查,也就是说,我们要在DBManager这个类里实现实现数据库增删改查的功能。
1.声明一个数据库管理者DBManager单例
public class DBManager
private static final String dbName = "my_test_db";
private static DBManager mInstance;
private DaoMaster.DevOpenHelper mOpenHelper;
private Context mContext;
public DBManager(Context context)
this.mContext = context;
mOpenHelper = new DaoMaster.DevOpenHelper(mContext,dbName,null);
/**
* 获取单例引用
*
* @param context
* @return
*/
public static DBManager getInstance(Context context)
if (mInstance == null)
synchronized (DBManager.class)
if (mInstance == null)
mInstance = new DBManager(context);
return mInstance;
2.获取可写、可读数据库
/**
* 获取可读数据库
* @return
*/
private SQLiteDatabase getReadableDatabase()
if (mOpenHelper == null)
mOpenHelper = new DaoMaster.DevOpenHelper(mContext,dbName,null);
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
return db;
/**
* 获取可写数据库
* @return
*/
private SQLiteDatabase getWritableDatabase()
if (mOpenHelper == null)
mOpenHelper = new DaoMaster.DevOpenHelper(mContext,dbName,null);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
return db;
3.增删改查系列方法
通过greenDao插件给我们自动生成的这三个工具类,我们可以很轻易的实现增删改查系列方法
/**
* 插入一条记录
* @param user
*/
public void insertUser(User user)
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.insert(user);
/**
* 插入用户集合
*
* @param users
*/
public void insertUserList(List<User> users)
if (users == null || users.isEmpty())
return;
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.insertInTx(users);
/**
* 删除一条记录
*
* @param user
*/
public void deleteUser(User user)
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.delete(user);
/**
* 清空所有记录
*/
public void clearUser()
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.deleteAll();
/**
* 批量删除
* @param list
*/
public void deleteUsers(List<User> list)
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.deleteInTx(list);
/**
* 更新一条记录
*
* @param user
*/
public void updateUser(User user)
DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
userDao.update(user);
/**
* 查询用户列表
*/
public List<User> queryUserList()
DaoMaster daoMaster = new DaoMaster(getReadableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
QueryBuilder<User> qb = userDao.queryBuilder();
List<User> list = qb.list();
return list;
/**
* 根据条件查询用户列表
*/
public List<User> queryUserList(int age)
DaoMaster daoMaster = new DaoMaster(getReadableDatabase());
DaoSession daoSession = daoMaster.newSession();
UserDao userDao = daoSession.getUserDao();
QueryBuilder<User> qb = userDao.queryBuilder();
qb.where(UserDao.Properties.Age.gt(age)).orderAsc(UserDao.Properties.Age);
List<User> list = qb.list();
return list;
至此,我们可以真正测试操作数据库了
public class MainActivity extends AppCompatActivity
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
private void initData()
DBManager dbManager = DBManager.getInstance(this);
for (int i = 0;i<6;i++)
User user = new User("测试数据"+i,10+i);
dbManager.insertUser(user);
List<User> users = dbManager.queryUserList();
for (User user : users)
Log.i(TAG,user.toString()+"\\n");
运行结果:
初步测试,没毛病。其它增删改查功能,大家自己可以测试下,这里就不做示例了。接下来,我们讨论下一个问题:数据库升级。
数据库升级
应用上线之后,app的更新或多或少涉及到数据库的版本升级。可能要给某张表添加一个字段,也可能要新增一张表等等此类情况。那么,这里数据库更新可以有两种方案:
方案一:删除旧的数据库,创建新的数据库和数据库表
这种方案,简单粗暴,一般也不会出什么Bug,但也出在一个很明显的问题,就是在已经上线的应用,更新app升级数据库时删除了用户原有的数据,对于用户体验来说是很不友好的。
方案二:保留原有数据库数据,并在此基础上添加新的字段或新表。这种方案也就是为了解决方案一存在的问题。
那么,greenDao3.0在升级数据库上是怎么做的呢?
通过调查源码发现,我们操作的对象mOpenHelper对应的类DevOpenHelper重写了一个方法onUpgrade(),没错,这就是数据库版本升级时要调用的方法,看内容:
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion)
Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
通过源码我们发现:
dropAllTables(db,true);//删除当前数据库所有表单;
onCreate(db);//新建当前所有表
即greenDao默认的升级方法就是我们的方案一,简单粗暴,问题也突出。然而,我们还是先来调查一下它什么时候会调用onUpgrade()方法。
查看源码,发现在执行mOpenHelper.getWritableDatabase()和mOpenHelper.gerReadableDatabase()的时候会比较数据库的版本号和app设置的数据库版本号(即所谓的数据库的旧版本号和新版本号),如果不同者会执行升级(onUpgrade())或降级(onDowngrde())数据库操作,看源码:
final int version = db.getVersion();
if (version != mNewVersion)
if (db.isReadOnly())
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
db.beginTransaction();
try
if (version == 0)
onCreate(db);
else
if (version > mNewVersion)
onDowngrade(db, version, mNewVersion);
else
onUpgrade(db, version, mNewVersion);
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
finally
db.endTransaction();
很明显的,这里的version就是从当前数据库中获取的版本号(即旧版本号),而mNewVersion,我们通过追踪,发现它是从DaoMaster类里传过来的,也就是SCHEMA_VERSION
public class DaoMaster extends AbstractDaoMaster
public static final int SCHEMA_VERSION = 1;
...
换言之,我们要升级数据库,就要改变这个值,但是我们都知道这是greenDao框架自动生成的,不能手动修改:
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
是的,即使我们非要手动修改,你会发现编译之后它又还原了。这是,我们就要用到上面提到的greendao辅助配置
我们添加配置schemaVersion,这就是设置新数据库的版本号:
greendao
schemaVersion 2
...
编译,我们发现,DaoMaster的变成了SCHEMA_VERSION我们设置的值了
/**
* Master of DAO (schema version 2): knows all DAOs.
*/
public class DaoMaster extends AbstractDaoMaster
public static final int SCHEMA_VERSION = 2;
。。。
Ok,现在就可以升级数据库了,运行:
查看打印信息,我们知道,它确实执行了onUpgrade()方法,也就是完成了数据库的升级
知道了如何执行数据库升级,现在就还剩下最后一个问题,如何实现方案二?
这里我通过网上查阅资料,看到了某大神提供的方法,合并(MigrationHelper),其核心思路如下:
1 把旧表改为临时表
2 建立新表
3 临时表数据写入新表,删除临时表
看MigrationHelper.java核心方法:public class MigrationHelper
/**
* 调用升级方法
* @param db
* @param daoClasses 一系列dao.class
*/
public static void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses)
//1 新建临时表
generateTempTables(db, daoClasses);
//2 创建新表
createAllTables(db, false, daoClasses);
//3 临时表数据写入新表,删除临时表
restoreData(db, daoClasses);
...
所以我们需要新建一个类MyDBOpenHelper继承DaoMaster.DevOpenHelper,重写onUpgrade()方法,里面调用MigrationHelper.migrate()方法就行了。
public class MyDbOpenHelper extends DaoMaster.DevOpenHelper
public MyDbOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory)
super(context, name, factory);
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion)
MigrationHelper.migrate(db, UserDao.class);
现在,我们修改SCHEMA_VERSION为3,给User添加新字段number,将DBManager中的DaoMaster.DevOpenHelper替换成MyDBOpenHelper,以及修改其他对应代码,编译运行:
我们发现数据库确实保存了原来的数据,并添加了新的字段number。至此,我们终于实现了方案二,完美
BUT,我们新增表能行吗?这里,我们测试下添加一个实体类News
@Entity
public class News
private int id;
private String newsInfo;
...
同步编译,同样在DBManager.java里添加增删改查方法,并在onUpgrade()方法里添加NewsDao.class
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion)
MigrationHelper.migrate(db, UserDao.class, NewsDao.class);
在MainActivity里添加代码
private void initData()
...
for (int i = 0;i<6;i++)
News user = new News(i,"测试数据"+i);
dbManager.insertNews(user);
List<News> newsList = dbManager.queryNewsList();
for (News user : newsList)
Log.i(TAG,user.toString()+"\\n");
修改SCHEMA_VERSION为4,运行,发现提示错误 E/SQLiteLog: (1) no such table: NEWS即没有创建NEWS表
所以大神的MigrationHelper这个类的功能似乎也稍有欠缺,呐,现在我们又有了新的目标,当旧数据库里不存在某表时,我们创建该表。
怎么做呢?是的,greenDao框架怎么做,我们就怎么做。
greenDao是怎么做的呢,通过研究代码发现,greenDao是通过DaoMaster调用createAllTables(Database db, boolean ifNotExists)方法创建表的
public class DaoMaster extends AbstractDaoMaster
public static final int SCHEMA_VERSION = 4;
/** Creates underlying database table using DAOs. */
public static void createAllTables(Database db, boolean ifNotExists)
UserDao.createTable(db, ifNotExists);
NewsDao.createTable(db, ifNotExists);
...
/** Creates the underlying database table. */
public static void createTable(Database db, boolean ifNotExists)
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\\"NEWS\\" (" + //
"\\"ID\\" INTEGER NOT NULL ," + // 0: id
"\\"NEWS_INFO\\" TEXT);"); // 1: newsInfo
这就好办了,我们直接使用该方法,研究里面方法里面的sql语句发现,我们要将ifNotExists设为true,即表不存在才创建。
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion)
DaoMaster.createAllTables(db,true);//如果表不存在,则创建
MigrationHelper.migrate(db, UserDao.class,NewsDao.class);
重新编译运行
是的,成功,没毛病!
至此,我们基于greenDao的数据库升级就基本搞定啦。
以上是关于Android笔记之greenDao3.0学习总结的主要内容,如果未能解决你的问题,请参考以下文章