GreenDao讲义3:带你了解查询生成器和更加复杂的查询

1. QueryBuilder

QueryBuilder能够让你在不涉及SQL语句的情况下查询实体。写SQL有几个缺点,首先是易错的,其次是要在运行时才知道有没有问题(假如属性名是pid,你写成了id,也要到运营时才会崩溃),QueryBuilder能够在编译时检查错误(如属性的引用是否错误)。

例子:查询first name是"Joe"的人,得到的结果按照last name排序:

List <User> items = userDao.queryBuilder()
.where(Properties.FirstName.eq("Joe"))
.orderAsc(Properties.LastName)
.list();
例子:查询first name是"Joe"并且出生在1970年10月份或之后的人:

也就是FirstName.eq("Joe") AND ( YearOfBirth.gt(1970) OR ( YearOfBirth.eq(1970) AND MonthOBirth.ge(10) ) )

QueryBuilder qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq("Joe"),
qb.or(Properties.YearOfBirth.gt(1970),
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10))));
List youngJoes = qb.list();
请仔细观察括号的匹配。

我们看一下QueryBuilder.where函数的参数会发现,是一个变参函数(arg0, arg1...),这些参数是and关系

例子:加入购物车的生成如下:

Entity Cart = schema.addEntity("Cart");
Cart.addIdProperty();
Cart.addStringProperty("pid").notNull();
Cart.addStringProperty("pcount").notNull();
每个商品的pid都是不同的。

购物车中商品prd的数量增加x:

List<Cart> items = DbService.getInstance(getActivity()).queryCart(Properties.Pid.eq(prd));
if (items.size() == 0) {
	DbService.getInstance(getActivity()).addToCart(new Cart(null,prd,x));
	Log.d("debug","success to add to cart, new item create");
} else if (items.size() == 1) {
	Cart item = items.get(0);
	int c1 = Integer.valueOf(item.getPcount()).intValue();
	int c2 = Integer.valueOf(x).intValue();
	item.setPcount(Integer.toString(c1+c2));
	DbService.getInstance(getActivity()).updateCart(item);
	Log.d("debug","success to add to cart, item count modified");
} else {
	Log.d("error","error to add to cart");
}


2. Query (我理解为相当于SQL中的Prepare)

Query类是一个能够执行多次的查询语句,我理解为Prepare。

先查询1970年出生的Joe:

Query query = userDao.queryBuilder().where(Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)).build();
List <User> joesOf1970 = query.list();
然后查询1977年出生的Maria:

query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List <User> mariasOf1977 = query.list();




3. LazyList

greenDao支持唯一结果和结果链表。如果你只想获取唯一的结果,那么可以使用unique(),它要么给你唯一一个结果,要么null,如果你不想获得null,那么你可以使用uniqueOrThrow(),它会在匹配结果为空时抛出DaoException的异常。

如果你想获取多个结果,以下有几个方案:

list():所有结果都会载入内存,结果会死一个ArrayList。

listLazy():结果会按需载入内存,一旦其中一个元素被要求了,那么就会载入内存并且进行chache,必须手动关闭。

还有listLazyUncached()和listIterator(),也需要手动通过调用close()来关闭。





4. 多线程执行查询

如果一个现成正在执行查询,另一个现成试图修改参数,会抛出异常。不要使用自己定义锁的机制,不然可能出现死锁。

尽量避免多线程执行查询,如果实在需要就使用forCurrentThread()来执行。



下面的示例使用Query对象来获取用户  生于1970年第一个名字:


Query query = userDao.queryBuilder().where(
Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970))
.build();
List joesOf1970 = query.list();


使用此查询的对象,我们可以搜索玛丽亚出生在1977年以后:
query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List mariasOf1977 = query.list();


如果QueryBuilder的不提供你所需要的功能,原始查询是您回落的解决方案。有执行导致实体原始的


SQL两种方法。的首选方法是使用的QueryBuilder和WhereCondition.StringCondition。使用这个你


可以通过任何SQL碎片的WHERE子句查询生成器。下面的代码是一个假设的例子,你如何可以运行一个


子选择(使用连接将是更好的解决方案)


Query query = userDao.queryBuilder().where(
new StringCondition("_ID IN " +
"(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)")).build();


让我们来看看另一个例子,包括三个实体:城市,国家和大陆。如果我们要查询欧洲所有城市中至少


有一百万人口,它是这样的:
QueryBuilder qb = cityDao.queryBuilder().where(Properties.Population.ge(1000000));
Join country = qb.join(Properties.CountryId, Country.class);
Join continent = qb.join(country, CountryDao.Properties.ContinentId, Continent.class, 


ContinentDao.Properties.Id);
continent.where(ContinentDao.Properties.Name.eq("Europe"));
List<City> bigEuropeanCities = qb.list();




一个连接也可以用关系引用单个实体使用。例如,我们想找到所有的人,其祖父的名字是“林肯”。


假设我们有一个fatherId属性指向同一实体的Person实体。随后,查询是建立这样的:
QueryBuilder qb = personDao.queryBuilder();
Join father = qb.join(Person.class, Properties.FatherId);
Join grandfather = qb.join(father, Properties.FatherId, Person.class, Properties.Id);
grandfather.where(Properties.Name.eq("Lincoln"));
List<Person> lincolnDescendants = qb.list();






在你greenDAO发电机模型必须为外键(ID)值的属性建模。使用这个属性,你可以添加使用


Entity.addToOne一个一对一的关系。
例:某用户有一个图片。




Property pictureIdProperty = user.addLongProperty("pictureId").getProperty();
user.addToOne(picture, pictureIdProperty);






每个关系有一个名称,它用于在承载的关系所产生的实体相应的属性。默认名称为目标实体的名称。


这个名称可以通过使用的setName方法被覆盖。如果实体有多个关系到同一个目标实体的默认名称是


不是唯一的。在这种情况下,你必须明确指定关系的名称。


让我们扩展前面的例子,并表示该用户也有缩小图片。因为无论是主画面和缩略图指代相同实体的图


片,将有一个名称冲突。因此,我们重命名第二个关系到“缩略图”:




Property customerId = order.addLongProperty("customerId").notNull().getProperty();
ToMany customerToOrders = customer.addToMany(order, customerId);
customerToOrders.setName("orders"); // Optional
customerToOrders.orderAsc(orderDate); // Optional


这样,我们可以简单地调用Customer类的产生getOrders()方法来获得订单:
List orders = customer.getOrders();




一对多的关系是在第一次请求懒洋洋地解决。在这之后,相关的实体被缓存在一个列表对象内源实体


。以关系的get方法后续调用不查询数据库。


需要注意的是更新到一对多的关系,需要一些额外的工作。因为对许多列表被缓存,当相关的实体被


添加到数据库中它们不更新。下面的代码说明的行为:


List orders1 = customer.getOrders();
int size1 = orders1.size();


Order order = new Order();
order.setCustomerId(customer.getId());
daoSession.insert(order);


Listorders2 = customer.getOrders();




获取到许多Java列表(这有持续的新的实体之前完成这一点,因为我们不知道,如果我们得到新的结


果的缓存,就像这样,我们知道现在的缓存。)
创建一个新的实体对象(在很多副作用)
新实体的外交属性设置为目标实体
坚持新的使用对象插入
新的对象添加到一对多Java列表


List orders = customer.getOrders();
newOrder.setCustomerId(customer.getId());
daoSession.insert(newOrder);
orders.add(newOrder);






需要注意的是getOrders被插入之前调用,以确保该列表缓存。如果getOrders将插入后调用,


newOrder会如果订单没有前发生缓存列表中的两次。


同样,你可以删除相关实体:


List orders = customer.getOrders();
daoSession.delete(newOrder);
orders.remove(newOrder);


有时,这可能是麻烦的,或甚至不可能更新所有一对多关系手动相关后的实体被添加或删除。救援,


greenDAO有复位方法来清除缓存列表。如果一个一对多的关系可能有潜在的变化,您可以强制


greenDAO重新加载相关实体的名单:


customer.resetOrders();
List orders2 = customer.getOrders();




有时你想浏览1:在两个方向n的关系。在greenDAO,你要一个增加一和一对多的关系,以实现这一目


标。下面的示例显示了客户和订单实体的完整的建模,我们之前使用的一个例子。这一次,我们使用


在客户属性既创造的关系:


Entity customer = schema.addEntity("Customer");
customer.addIdProperty();
customer.addStringProperty("name").notNull();


Entity order = schema.addEntity("Order");
order.setTableName("ORDERS"); // "ORDER" is a reserved keyword
order.addIdProperty();
Property orderDate = order.addDateProperty("date").getProperty();
Property customerId = order.addLongProperty("customerId").notNull().getProperty();
order.addToOne(customer, customerId);


ToMany customerToOrders = customer.addToMany(order, customerId);
customerToOrders.setName("orders");
customerToOrders.orderAsc(orderDate);




假设我们有一个订单实体。使用这两种关系,我们可以得到客户和客户已做过的所有订单:


List allOrdersOfCustomer = order.getCustomer().getOrders();




您可以通过模拟有一个实体的树关系建模一对一和一对多的关系指着自己:


Entity treeEntity = schema.addEntity("TreeEntity");
treeEntity.addIdProperty();
Property parentIdProperty = treeEntity.addLongProperty("parentId").getProperty();
treeEntity.addToOne(treeEntity, parentIdProperty).setName("parent");
treeEntity.addToMany(treeEntity, parentIdProperty).setName("children");




生成的实体可以让你浏览到其母公司和孩子们:
TreeEntity parent = child.getParent();
List grandChildren = child.getChildren();


建立数据库加密。




compile 'org.greenrobot:greendao-encryption:2.2.2'
compile 'net.zetetic:android-database-sqlcipher:3.5.1'


请注意,“greendao加密”取代“greendao”(删除平原“greendao”依赖如果你在项目中使用之前


greenDAO)。此外,您可能要检查最新版本。


同样的,你还需要使用另一个神器在您的发电机项目:


compile 'org.greenrobot:greendao-generator-encryption:2.2.0'


GreenDao讲义3:带你了解查询生成器和更加复杂的查询,有需要的朋友可以参考下。




查询会返回匹配特定条件的实体,使用GreenDao,你可以使用原生的SQL语句,也可以使用查询生成器(QueryBuilder)的API来生成查询。并且,查询也支持懒惰加载(lazy-loading)方式,这对于结果数目庞大的操作可能会节省内存提高性能。





1. QueryBuilder

QueryBuilder能够让你在不涉及SQL语句的情况下查询实体。写SQL有几个缺点,首先是易错的,其次是要在运行时才知道有没有问题(假如属性名是pid,你写成了id,也要到运营时才会崩溃),QueryBuilder能够在编译时检查错误(如属性的引用是否错误)。

例子:查询first name是"Joe"的人,得到的结果按照last name排序:

List <User> items = userDao.queryBuilder()
.where(Properties.FirstName.eq("Joe"))
.orderAsc(Properties.LastName)
.list();
例子:查询first name是"Joe"并且出生在1970年10月份或之后的人:

也就是FirstName.eq("Joe") AND ( YearOfBirth.gt(1970) OR ( YearOfBirth.eq(1970) AND MonthOBirth.ge(10) ) )

QueryBuilder qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq("Joe"),
qb.or(Properties.YearOfBirth.gt(1970),
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10))));
List youngJoes = qb.list();
请仔细观察括号的匹配。

我们看一下QueryBuilder.where函数的参数会发现,是一个变参函数(arg0, arg1...),这些参数是and关系

例子:加入购物车的生成如下:

Entity Cart = schema.addEntity("Cart");
Cart.addIdProperty();
Cart.addStringProperty("pid").notNull();
Cart.addStringProperty("pcount").notNull();
每个商品的pid都是不同的。

购物车中商品prd的数量增加x:

List<Cart> items = DbService.getInstance(getActivity()).queryCart(Properties.Pid.eq(prd));
if (items.size() == 0) {
	DbService.getInstance(getActivity()).addToCart(new Cart(null,prd,x));
	Log.d("debug","success to add to cart, new item create");
} else if (items.size() == 1) {
	Cart item = items.get(0);
	int c1 = Integer.valueOf(item.getPcount()).intValue();
	int c2 = Integer.valueOf(x).intValue();
	item.setPcount(Integer.toString(c1+c2));
	DbService.getInstance(getActivity()).updateCart(item);
	Log.d("debug","success to add to cart, item count modified");
} else {
	Log.d("error","error to add to cart");
}


2. Query (我理解为相当于SQL中的Prepare)

Query类是一个能够执行多次的查询语句,我理解为Prepare。

先查询1970年出生的Joe:

Query query = userDao.queryBuilder().where(Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)).build();
List <User> joesOf1970 = query.list();
然后查询1977年出生的Maria:

query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List <User> mariasOf1977 = query.list();




3. LazyList

greenDao支持唯一结果和结果链表。如果你只想获取唯一的结果,那么可以使用unique(),它要么给你唯一一个结果,要么null,如果你不想获得null,那么你可以使用uniqueOrThrow(),它会在匹配结果为空时抛出DaoException的异常。

如果你想获取多个结果,以下有几个方案:

list():所有结果都会载入内存,结果会死一个ArrayList。

listLazy():结果会按需载入内存,一旦其中一个元素被要求了,那么就会载入内存并且进行chache,必须手动关闭。

还有listLazyUncached()和listIterator(),也需要手动通过调用close()来关闭。





4. 多线程执行查询

如果一个现成正在执行查询,另一个现成试图修改参数,会抛出异常。不要使用自己定义锁的机制,不然可能出现死锁。

尽量避免多线程执行查询,如果实在需要就使用forCurrentThread()来执行。





5. 原生查询(不建议使用)

queryRaw()
queryRawCreate()

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值