GreenDao3+ 介绍

简介
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<City>  cityList =EntityManager.getInstance().getCityDao().queryBuilder().list();
    LazyList<City>  cityList2 =EntityManager.getInstance().getCityDao().queryBuilder().listLazy();
    cityList2.close();
    List<City>  cityList3 =EntityManager.getInstance().getCityDao().queryBuilder().listLazyUncached();
    CloseableListIterator<City> 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<User> query = userDao.queryBuilder().where(
    Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)
).build();
List<User> 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<User> 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<Country> qb=EntityManager.getInstance().getCountryDao().queryBuilder();
              Join cities= qb.join(City.class,CityDao.Properties.CountryId)
                      .where(CityDao.Properties.Population.gt(1000));
               List<Country> countries =qb.list();

就是 join on 只不过这里它替你写了 on 的部分,应该只是 id 才可以。

多线程查询

多条线程执行查询语句时需要调用forCurrentThread()方法将query对象与当前线程进行绑定,如果其他线程修改该Query对象,greenDao将会抛出一个异常;forCurrentThread()方法通过将Query创建时的时间作为 query标识;
先看一段代码

 final   Query<Country> query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        List<Country> 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<T> 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<Country> query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        List<Country> countries =query.forCurrentThread().list();
                        for (Country c:countries){
                            Log.e("返回",c.getName());
                        }
                    }
                }).start();

这样就ok 了。大概就是每个线程要绑定自己的query

所以下面这张写法肯定没有问题啦

 new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Query<Country> query =EntityManager.getInstance().getCountryDao().queryBuilder().build();
                        List<Country> countries =query.list();
                        for (Country c:countries){
                            Log.e("返回",c.getName());
                        }
                    }
                }).start();
Sql语句查询

方式1

 Query<City> 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<City> query =EntityManager.getInstance().getCityDao().queryRawCreate("where _ID=?","1");
                City city=query.unique();
                    Log.e("返回值",city.getPopulation()+"~~~"+city.getName()+"~~~"+city.getId());

方式3

  List<City> citiess=EntityManager.getInstance().getCityDao().queryRaw("where _ID=?","1");
                for (City city:citiess){
                    Log.e("返回值",city.getPopulation()+"~~~"+city.getName()+"~~~"+city.getId());
                }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270