现在程序中对于数据库的操作是必不可少的,而且占据整个程序的重要一个部分。而程序员掌握数据库的操作是很重要的一项技能,但是数据库的代码往往重复性很高,一遍遍的重复着相同的操作步骤并不是很明智的选择。这时候一个ORM框架就显得极为重要了,一个好的ORM框架可以为程序的开发节省大量的时间,并且好的框架还能够发挥出更大的性能和效率,所以如果并没有过硬的技术,去选择一个良好的ORM框架那会给发开和学习带来巨大的优势。
ORM:对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
而对于在Android系统中内置的sqlite数据库,我们可以选择的ORM框架也是有很多的,GreenDao就是其中的一种。其他的还有OrmLite、Realm、Sugar等。
GREENDAO 设计的主要目标
一个精简的库
性能最大化
内存开销最小化
易于使用的 APIs
对 Android 进行高度优化
核心类
一旦生成了指定的代码,就可以在你的android工程中使用greenDao了。别忘记在你的android工程中引入greenDao的核心jar包:greenDao.jar。以下是GreenDao的一些必要接口。DaoMaster:
daomaster以一定的模式持有数据库对象(SQLiteDatabase)并管理一些DAO类(而不是对象)。
有一个静态的方法创建和drop数据库表。它的内部类OpenHelper和DevOpenHelper是SQLiteOpenHelper的实现类,用于创建SQLite数据库的模式。DaoSession:
管理指定模式下所有可用的DAO对象,你可以通过某个get方法获取到。DaoSession提供一些通用的持久化方法,比如对实体进行插入,加载,更新,刷新和删除。最后DaoSession对象会跟踪identity scope,更多细节,可以参看 session文档。DAOs(Data access objects):
数据访问对象,用于实体的持久化和查询。对于每一个实体,greenDao会生成一个DAO,相对于DaoSession它拥有更多持久化的方法,比如:加载全部,插入(insertInTx,语境不明了,暂且简单的翻译成插入)。
最重要的一点就是greenDAO3中使用了java中的注解来完成的,通过注解GreenDao可以快速的生成我们所需要的Entity,而且3版本与之前的版本使用的方法是有不小的差别的。
Android Sutio是个好东西,在AS中想要添加GreenDao的话,只用在Dependency中找到该项目添加即可,看清楚样子不要添加错了。
或者直接在build.gradle的dependencies中添加compile ‘org.greenrobot:greendao:3.2.2’。注意版本号可能发生改变。
然后在对应的build.gradle文件中添加相对应的信息。
官方描述:
// In your root build.gradle file:
buildscript {
repositories {
jcenter()
mavenCentral() // add repository
}
dependencies {
classpath ‘com.android.tools.build:gradle:2.3.0’
classpath ‘org.greenrobot:greendao-gradle-plugin:3.2.2’ // add plugin
}
}// In your app projects build.gradle file:
apply plugin: ‘com.android.application’
apply plugin: ‘org.greenrobot.greendao’ // apply plugindependencies {
compile ‘org.greenrobot:greendao:3.2.2’ // add library
}
在root build.gradle中添加的有如下两样:
在app build.gradle中添加的有三样:
3.
greendao配置介绍
其中参数介绍:
schemaVersion: The current version of the database schema. This is used by the *OpenHelpers classes to migrate between schema versions. If you change your entity/database schema, this value has to be increased. Defaults to 1.(数据库版本,默认为1,如果更改数据库必须增加该值)
daoPackage: The package name for generated DAOs, DaoMaster, and DaoSession. Defaults to the package name of your source entities.(生成核心类DaoMaster、DaoSession、Dao的包目录)
targetGenDir: The location where generated sources should be stored at. Defaults to the generated source folder inside the build directory ( build/generated/source/greendao).(生成的源文件储存目录)
generateTests: Set to true to automatically generate unit tests.(是否生成单元测试,true生成)
如果什么都不指定的话,单单的指定一个数据库版本,那么默认生成的核心类位置如下:
如果指定为图中所述的位置的话,生成的核心类位置如下:
要说明的是,单单完成上述步骤并不会生成图中看到的三个核心类:DaoMaster、DaoSession、Daos(这里是UserDao,这里的App并不是自动生成的,后面有介绍)。
想要生成上述的核心类还必须先定义好我们所需要的Entity类:User。
新建一个User.java:
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.NotNull;
/**
* Created by leafage on 2017/4/10.
*/
@Entity
public class User {
@Id(autoincrement = true)
private Long id;
@NotNull
private String name;
}
然后进行Make Project(ctrl + F9 或者 Build下),会发现所写的User类不仅仅补全了seeter和getter方法,还多了两个构造方法。
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.NotNull;
import org.greenrobot.greendao.annotation.Generated;
/**
* Created by leafage on 2017/4/10.
*/
@Entity
public class User {
@Id(autoincrement = true)
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;
}
}
这就是通过注解所产生的内容,而且此时也会同时产生三个核心类。Entity中所具有的属性很多,这里只是进行测试,详细属性:Entity属性介绍
要想使用GreenDao来操纵数据库的话,核心类的使用是必不可少的,我们先看一下这三个核心类里面有什么东西。
这个类继承了AbstractDaoMaster,API只有一句话:The master of dao will guide you: start dao sessions with the master.
DaoMaster通过调用下面两个方法实现了对数据库的创建和删除。
public static void createAllTables(Database db, boolean ifNotExists) {
UserDao.createTable(db, ifNotExists);
}
/** Drops underlying database table using DAOs. */
public static void dropAllTables(Database db, boolean ifExists) {
UserDao.dropTable(db, ifExists);
}
可以通过调用以下该方法得到对应的DaoSession对象。
public DaoSession newSession() {
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
public DaoSession newSession(IdentityScopeType type) {
return new DaoSession(db, type, daoConfigMap);
}
还有DevOpenHelper、OpenHelper可以帮助我们使用数据库,比如可以通过DevOpenHelper得到一个Database对象,但是相对应的代码是在它们的父类中实现的。
其实可以发现,在这个类最开始的时候有一行注释:// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
所以我们还是不要轻易编辑它了。
这个类继承了:AbstractDaoSession
官方API:
DaoSession gives you access to your DAOs, offers convenient persistence methods, and also serves as a session cache.
To access the DAOs, call the get{entity}Dao methods by the generated DaoSession sub class.
DaoSession offers many of the available persistence operations on entities as a convenience. Consider using DAOs directly to access all available operations, especially if you call a lot of operations on a single entity type to avoid the overhead imposed by DaoSession (the overhead is small, but it may add up).
By default, the DaoSession has a session cache (IdentityScopeType.Session). The session cache is not just a plain data cache to improve performance, but also manages object identities. For example, if you load the same entity twice in a query, you will get a single Java object instead of two when using a session cache. This is particular useful for relations pointing to a common set of entities. This class is thread-safe.
上谷歌翻译:
DaoSession可以访问您的DAO,提供方便的持久性方法,还可以作为会话缓存。
要访问DAO,请通过生成的DaoSession子类调用get {entity} Dao方法。
DaoSession为方便起见,为实体提供了许多可用的持久性操作。 考虑直接使用DAO来访问所有可用的操作,特别是如果您在单个实体类型上调用大量操作以避免由DaoSession强加的开销(开销较小,但可能相加)。
默认情况下,DaoSession具有会话缓存(IdentityScopeType.Session)。 会话缓存不仅仅是提高性能的普通数据缓存,还可以管理对象标识。 例如,如果在查询中加载相同的实体两次,那么在使用会话缓存时,您将获得单个Java对象而不是两个。 这对于指向一组共同实体的关系特别有用。 这个类是线程安全的。
所以我们最常用的是通过DaoSession来得到Dao,每当我们创建一个新的Entity之后一单make project之后,就会在这个类里面自动产生新的getDao方法,如下所示可以得到UserDao。
public UserDao getUserDao() {
return userDao;
}
这个类继承了Class AbstractDao,官方API:Base class for all DAOs: Implements entity operations like insert, load, delete, and query.
很显然,我们都是通过这个对象来对数据库进行增添查改的,它会有两个参数,一个Entity类型,一个主键类型:
Type Parameters:
T - Entity type
K - Primary key (PK) type; use Void if entity does not have exactly one PK
其中的下面两个方法完成了数据库表结构的定义和创建以及删除,上述的DaoMater也是通过调用该方法完成创建数据库表的:
public static void createTable(Database db, boolean ifNotExists) {
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\"USER\" (" + //
"\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id
"\"NAME\" TEXT NOT NULL );"); // 1: name
}
/** Drops the underlying database table. */
public static void dropTable(Database db, boolean ifExists) {
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"USER\"";
db.execSQL(sql);
}
官方API
我们是无法在该类中查找到增添改查的代码的,它们都在父类中,我们只要使用就好了。
说了这么多,我们最主要还是想要使用GreenDao来完成我们对数据库的操作,增添改查无非是重中之重。
上述我们已经说到,想要完成对数据库的增添改查必须要有获取到Dao对象,官方文档中给的解释是:
You already saw DAOs, but how do you initialize greenDAO and the underlying database? Usually you need to init a DaoSession, which is typically done once for the whole app inside the Application class(您已经看到DAO,但是如何初始化greenDAO和底层数据库? 通常你需要初始化一个DaoSession,这通常在Application类中的整个应用程序中完成一次):
DevOpenHelper helper = new DevOpenHelper(this, "notes-db");
Database db = helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
The database is created with the helper class DevOpenHelper, provided by the generated DaoMaster class. It is an implementation of the OpenHelper class in DaoMaster, which does all database set up for you. No need to write “CREATE TABLE” statements.(数据库由生成的DaoMaster类提供的助手类DevOpenHelper创建。 它是DaoMaster中OpenHelper类的一个实现,它为您设置了所有数据库。 无需编写“CREATE TABLE”语句)
Activities and fragments may then call getDaoSession() to get access to all entity DAOs, as seen above when inserting and deleting notes.(然后,活动和片段可以调用getDaoSession()来访问所有实体DAO,如上所述插入和删除笔记时。)
说的很明白了,我们不必每次都初始化一个DaoSession,我们可以在Application类中初始化一次,然后接下来直接在其他Activity或者Fragment中通过方法调用得到DaoSession和对应的Dao即可。
还是直接看官方给的example.官方example
官方是将DaoSession的初始化放在了一个名为App的类中,然后其中在onCreate中完成了数据库的初始化创建,我们在其他地方可以直接通过getDaoSession()得到我们所需要的DaoSession对象。
import android.app.Application;
import org.greenrobot.greendao.database.Database;
public class App extends Application {
/** A flag to show how easily you can switch from standard SQLite to the encrypted SQLCipher. */
public static final boolean ENCRYPTED = false;//是否创建加密数据库
private DaoSession daoSession;
@Override
public void onCreate() {
super.onCreate();
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, ENCRYPTED ? "notes-db-encrypted" : "user-db");
Database db = ENCRYPTED ? helper.getEncryptedWritableDb("super-secret") : helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
}
public DaoSession getDaoSession() {
return daoSession;
}
}
上面的那个boolean需要注意下,如果为true的话将会得到加密型数据库,刚开始官方的例子中默认为true,然后在运行的时候我就一直出错有BUG,最后发现是这个地方的问题,我们将其改为false就能够普通的运行了,至于加密数据库还没有了解过,所以在此不作讨论。
然后在AndroidMainfest.xml为application添加name属性,指定该类即可,至于为什么可以查一下。
然后在Activity或者Fragment中通过以下代码即可得到想要的DaoSession和Dao对象:
mDaoSession = ((App)getApplication()).getDaoSession();
mUserDao = mDaoSession.getUserDao();
为了测试,我设置了如下一个页面布局:
1.增加实体:
private void InsertUser() {
mInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Long id = Long.valueOf(mUserId.getText().toString());
String name = mUserName.getText().toString();
User user = new User(id,name);
mUserDao.insert(user);
UpdateList();
}
});
}
不能再简单了,得到用户输入的内容转换成对应的类型,然后new出一个Entiry对象(User)设置完对应的属性之后,使用刚才得到的D奥Session对象进行insert操作即可,最后更新一下界面内容。
但是这个有个问题,不能添加相同的主键,如果我所要添加的主键已经存在了话,那么就会报错了。
当我连续添加两次相同的内容时就会出错(虽然设置了自增但是却是根据输入来进行赋值的),对此我们可以再添加时先判断是否存在,这就需要使用到查询语句。
其实这里不输入name值也是可以的,但我明明在Name中使用了@NotNull注解来强制了啊,对此可以参考这篇文章来看看到底有什么区别:null和空字符串的区别
2.查询实体。
private void QueryUser() {
mQuery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Long id = Long.valueOf(mUserId.getText().toString());
User user = mUserDao.queryBuilder().where(UserDao.Properties.Id.eq(id)).build().unique();
Toast.makeText(MainActivity.this, "User Name:" + user.getName(), Toast.LENGTH_SHORT).show();
}
});
}
可以看到上述查询主键不存在的话,竟然也报错了!
不要误会,这并不是数据库的问题,DeBug一下就会发现问题所在:
是因为数据库中没有对应的主键内容,所以返回了一个null,这才导致了程序的崩溃。
但是我们可以通过这个得知,如果查询没有的话就会得到一个null对象。这样的话,就可以在添加前面查询一下来判断是否存在相同的主键,如果没有的话就会得到非null,再进行添加操作,否则是一个null那么不操作即可。
3.删除实体。
private void DeleteUser() {
mDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Long id = Long.valueOf(mUserId.getText().toString());
mUserDao.deleteByKey(id);
UpdateList();
}
});
}
可以看到对于删除的话,如果所要删除的主键不存在并不会报错。
4.修改实体。
private void AlterUser() {
mAlter.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Long id = Long.valueOf(mUserId.getText().toString());
User user = mUserDao.queryBuilder().where(UserDao.Properties.Id.eq(id)).build().unique();
user.setName(mUserName.getText().toString());
mUserDao.update(user);
UpdateList();
}
});
}
修改的话,也是先根据查询得到一个Entity,然后在进行相对应的setter操作更新数据,最后DaoSession进行更新。
这里也出错了,跟上面的原因一样:没有查询到实体,所以得到了一个null对象,然后试图对null进行set操作,自然就出错了。
主要是介绍了GreenDao3的用法,最开始查找的资料有很多都是之前版本的介绍和步骤,看了半天一脸懵,后来看看官方的example,再结合greendao使用的博客,折腾了半天才入了门。写博客也是方便记忆,正所谓眼过千遍不如手过一遍。
其实最初的时候,我的例子中并没有考虑出错的问题,像上述的因为Toast中使用了null对象导致出错,我最初就没有发现,而且三个核心类没有进行了解就直接使用了。但是写博客的时候就发现了这些相关性的问题,然后不断的摸索、总结。所以写博客不仅仅是加强记忆,同时也能够发现最初自己没有发现或者考虑不够周到的一些地方。本篇并没有讲解很高深的内容,如果想要了解更多的话,官方文档是一个不错的选择。