GAE-J中的数据库设计[一]:JDOQL
众所周知,GAE上的datastore是由Google的BigTable来实现的。
或许你还不知道什么是BigTable,可以看看下面的一段介绍:
BigTable是压缩的、高性能、应用友好的专用数据库系统,基于Google File System (GFS), Chubby Lock Service,Google Scheduler和少量其它Google应用程序。
Google的很多应用都在使用这一数据库,如MapReduce,Google Reader,Google Maps, Google Print, “My Search History”, Google Earth, Blogger.com, Google Code hosting, Orkut和YouTube等。Google 开发这一数据库系统的目的是考虑到licensing costs,扩展性和更好地控制性能特征等。内部开发的BigTable是为跑在廉价的PC机上设计的。BigTable 让Google在提供新服务时的运行成本降低,最大限度地利用了计算能力。 Bigtable是一个快速而超级规模的DBMS。但是,它不是传统上的固定行的DBMS,而是一个能够同时共享面向行的(column-oriented)和面向列(column-oriented)的“多维稀疏有序图”的数据库。Bigtable容量可以达到P比特级,工作在成百上千的计算机上,并且这些计算机可以很容易地加入到该系统中,并且无需重新配置就可以自动使用这些计算机资源。
GAE的datasotre在底层是一个BT的实现,所以同样地强调读取和查询性能。所有的查询语句都会被预先索引以便在查询大数据集合的时候提供快速的检索能力。GAE的数据存储支持事务处理,可以通过定义实体组(entity group)来定义事务单元。
所谓的事务可以这样简单得理解为:在单个事务中可以处理多个操作。如果其中某项操作失败,则整个事务就会被回退。
GAE数据库是高并发的但不是传统意义上的关系性数据库。
针 对Java版的GAE,其数据库API分为JDO和JPA两种类型,JPA是所谓的低级API(low-level),我们可以在JPA之上实现其他类 型的接口,当然也可以直接在程序中使用。JDO和JPA是Java SDK中原生支持的,不过GAE在其基础上进行了修改,因为考虑到Bigtable特殊的存储结构,有些关系型数据库支持的特性将不被支持,具体的受限特 性如下:
JDO:
1、非所属关系[unowned relationships], 此特性暂时没有JDO的相关语法,但Google官方称可能会在后续版本中提供支持|至于GAE-J中的relationship以后会详细讲解。
2、所属的多对多关系[owned many-to-many relationships]
3、查询过滤中的contains()方法,对于一个集合列的contains方法不被支持,但可以直接使用collection == “value”的方式替代。
4、不支持”join”联合查询。执行父表查询的时候不能使用子表[形象上来说],但GAE提供了迂回的解决方案--使用Key。
5、不支持聚集操作,比如group。
6、无法使用查询得到子类的实例。
7、@PersistenceCapable的选项IdentityType.DATASTORE 不被支持,现只支持IdentityType.APPLICATION。
8、无法对超类进行存储,可能在后续版本中提供支持。
如 果是在本地开发GAE-J,则数据库索引文件和数据库文件分别对应于项目目录->WAR->WEB-INF文件夹下的datastore- indexes- auto.xml和local_db.bin,如果使用文本方式打开local_db.bin,可以看到String和Text类型的文本,但大部分还是 乱码|。
使用JDOQL
JDOQL是JDO中实现的类似于SQL语法的一种查询语句。但JDOQL比SQL更设合于像GAE datastore这样的基于面向对象特性的数据库。
查询:
一般的查询类似如下:
import java.util.List;
import javax.jdo.Query;
// …
Query query = pm.newQuery(Employee.class);
query.setFilter(“lastName == lastNameParam”);
query.setOrdering(“hireDate desc”);
query.declareParameters(“String lastNameParam”);
try {
List<Employee> results = (List<Employee>) query.execute(“Smith”);
if (results.iterator().hasNext()) {
for (Employee e : results) {
// …
}
} else {
// … no results …
}
} finally {
query.closeAll();
}
setFilter中的语句相当于SQL中Where子句,setOrdering是设置排序,最后declareParameters是设置变量声明,其中String是变量类型,lastNameParam类似execute的形参,他的作用是接收query.exeute()中传递的参数给Filter中的相应变量。
同样,也可以使用更类似SQL的语法,像下面的例子:
Query query = pm.newQuery(“select from Employee “ +
“where lastName == lastNameParam “ +
“order by hireDate desc “ +
“parameters String lastNameParam”)
List<Employee> results = (List<Employee>) query.execute(“Smith”);
与SQL的区别就是省略了*语法以及引用变量的方式不同。
Extents(可以理解为迭代):
Extents适合于不需要条件过滤的遍历查询,因为extent无须过滤,所以可以批量获取数据。在获取大批量数据的时候犹为适用,因为GAE对每次查询有1000个返回结果的限额,而每次的批量数据是作为一个结果集来计算的。
一个使用Extents的例子:
import java.util.Iterator;
import javax.jdo.Extent;
// …
Extent extent = pm.getExtent(Employee.class, false);
for (Employee e : extent) {
// …
}
extent.closeAll();
通过pm的getExtent方法得到一个class的所有对象作为一个extent返回,然后就可以使用for循环遍历了。