简介
GreenDao可以说是当今最流行,最高效而且还在迭代的关系型数据库。
关系型数据库:ORM(Object Relation Mapping 即 对象关系映射),就是将面向对象编程语言里的对象与数据库关联起来的一种技术,greenDao其实就是一种将java object 与SQLite Database关联起来的桥梁
配置不介绍了,参考官网即可。
优点
1.存取速度快
2.支持数据库加密
支持android原生的数据库SQLite,也支持SQLCipher(在SQLite基础上加密型数据库)。
3.轻量级
greenDao的代码库仅仅100k大小
4.激活实体
处于激活状态下的实体可以有更多操作方法
5.支持缓存
能够将使用的过的实体存在缓存中,下次使用时可以直接从缓存中取,这样可以使性能提高N个数量级
6.代码自动生成
DaoMaster
DaoMaster 是入口类,每一个DaoMaster持有一个数据库连接,通过DaoMaster#newSession()方法可以实例化多个Session,这些Session对应同一个数据库连接,但是系统会为每一个Session分配内存,在这片内存中会为实体进行缓存。每一个Session对应一个Identity .
DaoSession
从DaoSession中可以获取各实体类的对应DAO,然后就可以进行增删改查的操作了,对于每一个Session中的查询操作都会对查到的实体类做缓存操作,所以对应同一个Session的多次查询操作,如果entity的对象在该Session中有对应缓存则直接使用,而不再从数据库中读取数据并构建新的实体类对象。
常用注解介绍
@Entity
该实例是类的注解,告诉greenDAO这个类是需要持久化的实体类。下面是其内部注解
@Entity(
// If you want to have more than one schema, you can tell greenDAO
// to which schema an entity belongs (pick any string as a name).
schema = "myschema",
// Flag to make an entity "active": Active entities have update,
// delete, and refresh methods.
active = true,
// Specifies the name of the table in the database.
// By default, the name is based on the entities class name.
nameInDb = "AWESOME_USERS",
// Define indexes spanning multiple columns here.
indexes = {
@Index(value = "name DESC", unique = true)
},
// Flag if the DAO should create the database table (default is true).
// Set this to false, if you have multiple entities mapping to one table,
// or the table creation is done outside of greenDAO.
createInDb = false
)
@ID, field注解
表示选择一个long或Long类型的属性作为该实体所对应数据库中数据表的主键,参数可以设置(autoincreament=true),其实其他类型高版本也是可以的,比如String 不过应该不能自增长了。
@Property field 注解
可以自定义该属性在数据表的列名,默认的列名为属性名大写,并由下划线隔开多个单词,@Property(nameInDb="XXXX")可以自定义列名。
@NotNull
对应着数据表中该列不能为空。
@Transient
与Java中的Transient关键字类似,指不对该属性持久化,即不为该属性在数据表中创建相应列。
@Index
使用@Index 可以将一个属性变为数据库索引;其有俩个参数
name :不使用默认名称,自定义索引名称
unique : 给索引增加一个唯一约束,迫使该值唯一
@Entity
public class User {
@Id
private Long id;
@Index(unique = true)
private String name;
}
@Unique
含义与数据表中列的unique一致, 这种情况下,SQLite会自动为该列建立索引。
@Generated greenDAO
会根据开发者定义的实体类定义schema,并生成DAOs,而在生成过程中会为开发者编写的实体类的一些方法和域上添加
@Generated注解
提示开发者该属性不能被修改;并且实体类的方法,属性,构造器一旦被@Generated注释就不能被再次修改,否则或报错
此外还有@ToOne, @ToMany, @JoinEntity与多个数据表的关系有关,后面再对此详细介绍。
@Entity
public class User {
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "USERNAME")
private String name;
@NotNull
private int repos;
@Transient
private int tempUsageCount;
...}
查询list
1.list()
缓存查询结果;list()类型一般为ArrayList
2.listLazy();
懒查询,只有当调用list()中的实体对象时才会执行查询操作并且只缓存第一次被查询的结果,需要关闭
3.listlazyUncached() 懒查询,只有当调用list()中的实体对象时才会执行查询操作并且不缓存;
4.listIterator() 对查询结果进行遍历,不缓存,需要关闭;
List cityList =EntityManager.getInstance().getCityDao().queryBuilder().list();
LazyList cityList2 =EntityManager.getInstance().getCityDao().queryBuilder().listLazy();
cityList2.close();
List cityList3 =EntityManager.getInstance().getCityDao().queryBuilder().listLazyUncached();
CloseableListIterator cityList4 =EntityManager.getInstance().getCityDao().queryBuilder().listIterator();
cityList4.close();//需要try
分页查询
limit(int): 限制查询的数量;
offset(int): 每次返回的数量; offset不能单独使用;
多次查询:
即 第一次查询返回的 query 可以再次设置条件;
// fetch users with Joe as a first name born in 1970
Query query = userDao.queryBuilder().where(
Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)
).build();
List joesOf1970 = query.list();
// using the same Query object, we can change the parameters
// to search for Marias born in 1977 later:
query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List mariasOf1977 = query.list();
关联查询 joinI();
@Entity
public class City {
@Id
private Long id;
private String name;
private int population;
private Long countryId;
}
@Entity
public class Country {
@Id
private Long id;
private String name;
}
city 表示持有country 表的id
QueryBuilder qb=EntityManager.getInstance().getCountryDao().queryBuilder();
Join cities= qb.join(City.class,CityDao.Properties.CountryId)
.where(CityDao.Properties.Population.gt(1000));
List countries =qb.list();
就是 join on 只不过这里它替你写了 on 的部分,应该只是 id 才可以。
多线程查询
多条线程执行查询语句时需要调用forCurrentThread()方法将query对象与当前线程进行绑定,如果其他线程修改该Query对象,greenDao将会抛出一个异常;forCurrentThread()方法通过将Query创建时的时间作为 query标识;
先看一段代码
final Query query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
new Thread(new Runnable() {
@Override
public void run() {
List countries =query.list();
for (Country c:countries){
Log.e("返回",c.getName()+Thread.currentThread().getName());
}
}
}).start();
执行后会报错,当前的query 已经绑定到主线程,这时候在子线程中调用query 会报错
08-08 15:56:44.040 4587-4614/com.green.dao E/AndroidRuntime: FATAL EXCEPTION: Thread-314
Process: com.green.dao, PID: 4587
org.greenrobot.greendao.DaoException: Method may be called only in owner thread, use forCurrentThread to get an instance for this thread
at org.greenrobot.greendao.query.AbstractQuery.checkThread(AbstractQuery.java:99)
at org.greenrobot.greendao.query.Query.list(Query.java:87)
at com.green.dao.MainActivity$1.run(MainActivity.java:98)
at java.lang.Thread.run(Thread.java:818)
这个错误是在哪里抛出的呢?看下list()源码
public List list() {
checkThread();
Cursor cursor = dao.getDatabase().rawQuery(sql, parameters);
return daoAccess.loadAllAndCloseCursor(cursor);
}
protected void checkThread() {
if (Thread.currentThread() != ownerThread) {
throw new DaoException(//就是这里抛出了异常
"Method may be called only in owner thread, use forCurrentThread to get an instance for this thread");
}
}
啥意思?list()只能在它自己的线程中调用。 错误提示很清晰了,用 forCurrentThread 给当前线程一个 query
final Query query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
new Thread(new Runnable() {
@Override
public void run() {
List countries =query.forCurrentThread().list();
for (Country c:countries){
Log.e("返回",c.getName());
}
}
}).start();
这样就ok 了。大概就是每个线程要绑定自己的query
所以下面这张写法肯定没有问题啦
new Thread(new Runnable() {
@Override
public void run() {
Query query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
List countries =query.list();
for (Country c:countries){
Log.e("返回",c.getName());
}
}
}).start();
Sql语句查询
方式1
Query query =EntityManager.getInstance().getCityDao().queryBuilder().where(new WhereCondition.StringCondition("_ID IN"+"(SELECT _ID FROM CITY WHERE _ID=1)")).build();
City city =query.unique();
StringCondition()中的内容:_ID 可以换成任何字段,但是需要跟后面的查询内容保持一致, 虽然后面的sql 语句是如果放到数据库里查询是只能返回id 但是这里不是 而是返回所有值。注意 IN 还有sql 语句的括号。
方式2:
Query query =EntityManager.getInstance().getCityDao().queryRawCreate("where _ID=?","1");
City city=query.unique();
Log.e("返回值",city.getPopulation()+"~~~"+city.getName()+"~~~"+city.getId());
方式3
List citiess=EntityManager.getInstance().getCityDao().queryRaw("where _ID=?","1");
for (City city:citiess){
Log.e("返回值",city.getPopulation()+"~~~"+city.getName()+"~~~"+city.getId());
}