Archive for November, 2009


GAE-Java中JDO部分的KEY

发现有些部分的文档,看一次是绝对不可能看懂的。必须要过两天回头再来看,才发现一些端倪。比如这里要说的是JDO中类的KEY。

习惯了RDBMS开发的同志们,都会想到这还不是一个很简单的问题,无非就是选择一个唯一的可以代表该实例的作为ID就可以了。特别是目前开发中,大多数人喜欢用系统自动生成的ID序列来构造key。然而在GAE所支持的JDO中,我们会发现文档中提供了四种方式来实现KEY,不同的方法适用于不同的场合。

1. Long变量
这就是上面所说的最常见的一种,由系统自动生成的长整型变量来表示KEY。注意,这种选择只适用于那些没有父类的类。什么叫没有父类的类呢?例如有两个类,People和ContactInfo,在People类中,有一个属性就是ContantInfo,那么在这种情况下,People类就是有父类的类,而ContactInfo就是没有父类的类。

2.Unencoded String
和上面的Long变量一样,这个KEY的实现同样只适用于那些没有父类的类。但是在这里,它的值是由程序指定的,在这里使用的是没有编码的String类型。

3.Key
Key类是GAE-Java中专门提供的作为KEY的类型,它包含了可能有的父类信息以及一个系统自动分配或者程序指定的ID。也就是说,它是在包括父类信息的前提之下,选择以上两种选择之一来共同完成Key的任务。用KeyFactory的createKey()方法即可完成这一功能。

4.Key作为编码String
这种类型和Key类型是很相似的,只是将Key转化为来String来进行操作,方便使用。Key和encoded String之间可以用KeyFactory的keyToString()和stringToKey()方法互相转换。因为是String类型,所以如果想要得到ID的其中的内容,可以在声明了Key之后,附加声明其String或者Long类型的ID。@Extension(vendorName=”datanucleus”, key=”gae.pk-name或者gae.pk-id”,value=”true”)。同样,name类型的值是可以更改的,但是id类型的是自动生成无法变化的。

如何生成Key?
1. 对于没有父类的类型,我们可以使用createKey()静态方法来实现。它的参数包括createKey(String kind, String name)。其中kind就是指的要创造的类的类型,一般用Class.class.getSimpleName()来实现。第二个参数取决于使用的是自动生成的Long变量还是程序指定的String变量而定。

2,对于有父类的类型,我们同样可以使用createKey()的另一个重载方法来实现createKey(Key parent, String kind, long id)。在这里,我们首先实现父类的Key,然后将其包括在方法参数内即可。
不过在教程上,提供了另外一种方法来是实现,使用的是KeyFactory.Builder类。在这里首先通过构造函数,实现一个Key,然后不断调用addChild()方法,把子类附加上,最后通过getKey()方法得到Key。这种方法的好处在于可以产生一个链状的Builder,更加方便使用。比如在我们上面的People和ContactInfo例子之中,首先我们生成ContactInfo的Key,然后下一步附加上People的Key即可。

如何通过Key得到对象
一个很简单的方法,就是pm.getObjectById(class, key/id);如果你有的是key,第二个参数就用key;如果有的是id,第二个参数就用id。同时,你可以选择使用非类里面声明的类型,来作为第二个参数。比如在类里面使用的是Key,你可以通过调用name的String来获得。唯一的例外是String和Long之间的互相调用是不允许的。

Restlet2.0初尝试

今天一直在研究Restlet的API,大约应该是在2007年起,1.0版就已经问世了。但是由于这个概念在当时还是比较新的,所以1.0版本的普及率特别是在国内的普及率还是很低的。百度一下就会发现研究的人的确不多。现在RESTful的概念是越吵越热,且不说它到底好坏与否,但是引起这么多人的关注,就说明了它有其出色的地方。

在网站上看到的Stable的版本是1.6,于是保守地先从这个版本试起,结果发现无论如何添加Connector,都会报找不到Protocol的错,恰好这时James过来看看我,于是在他的建议之下,尝试了2.0m5版本。至少在最简单的程序部分,能够很正常的运行。

下面把一些很简单的步骤作个罗列:

在我看来,完成一个全部由Restlet构造的程序,一共需要四个部分。首先便是资源文件,对于REST概念比较熟悉的同学都应该明白,Resource在REST中是根本中的根本。于是,我们首先定义一个十分简单的Resource,为了简单起见,我们只给它附加两个功能,GET和POST。其中GET方法,很简单的返回一个String,POST方法是将得到的两个数进行相加返回结果字符串。

public class TestResource extends ServerResource{

	@Get
	public String getResultGet(){
		return "This is my test!";
	}

	@Post
	public String getResultPost(Representation entity){
		Form form = new Form(entity);
		String first = form.getFirstValue("first");
		String second = form.getFirstValue("second");
		int a = Integer.parseInt(first);
		int b = Integer.parseInt(second);

		return "The result of "+a+"+"+b+"is "+(a+b);
	}
}

在这一步结束之后,我们需要把资源放到一给应用(Application)中去,同时要提供访问它的路径。我个人心目中就把这个部分成为RouterPath,因为这两个单词也是这个文件中最多见的。

public class RouterPath extends Application{
        @Override
	public Restlet createInboundRoot() {
		// TODO Auto-generated method stub
		Router router = new Router(getContext());

		router.attach("/greeting",TestResource.class);

		return router;
	}
}

在这一给类中,它继承来Application这个类,目的就可以看成是提供了一个程序环境,在这个大的环境之下,你输入的方法,就将被引导到某个类之中去。比如在这里,所有的以greeting结尾的请求,都会被引导到我们刚才所创建的类里面去。

有来这样两样以后,我们只需要启动这个资源让它开始服务就可以了。我把这个类命名为StartResource类。

public class StartResource {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			// Create a new Component.
			Component component = new Component();  

			// Add a new HTTP server listening on port 8182.
			component.getServers().add(Protocol.HTTP, 8182);  

			// Attach the sample application.
			component.getDefaultHost().attach(new RouterPath());  

			// Start the component.
			component.start();
		}catch (Exception e) {
			// Something is wrong.
			e.printStackTrace();
		}
	}
}

我们可以很清楚的看到,这个类启动来在端口8182上的HTTP协议,然后将前面的Application附加在来这个端口上。然后服务就开始启动,等待他人的使用了。

现在我们就可以直接在浏览器中输入这个URL,来调用GET方法了。在这里,我们输入http://localhost:8182/greeting,屏幕上就出现来This is my test!字样。当然浏览器是不支持POST,PUT和DELETE直接在地址栏中的,我们可以用一个简单的JSP界面做一给POST框体,也可以像如下的类用Restlet提供的Client来完成请求。

public class Client {
	public static void main(String[] args) {
		int a=4;
		int b=9;
		ClientResource client = new ClientResource("http://localhost:8182/greeting");
		try {
			System.out.println(client.get().getText());
			Form form = new Form();
			form.add("first", a+"");
			form.add("second",b+"");
			System.out.println(client.post(form.getWebRepresentation()).getText());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

很容易看到,GET方法是可以直接用client得到的,而POST方法需要传入一个参数,也就是Resource端所需要的参数。用类似的办法,我们同样可以完成PUT和DELETE。

以上的这个例子实际上非常基础,但是至少展现来Restlet的一些特点,首先它是一个很完善的既支持客户端又支持服务端的API;其次它的结构也十分清晰,只要很好的划分了不同的请求,就能很好地看清楚之间的逻辑;最后就是API的封装还是很简洁的,尤其是在客户端调用这一块。

明天开始继续深入研究,更多的Tutorial可以参见http://www.restlet.org/documentation/2.0/tutorial

几个在GAE的datastore方面不太理解的问题

乍一看上去,觉得GAE的确是给很好的东西,理念十分先进,虽然datastore看上去和关系数据库很相似,但实际在底层的设计上却是有着决然的不同。今天看来一天,发现来几个问题还是无法理解的,先mark出,以免忘记。

1. datastore的数据是不是永远存在的,也就是说google会不会主动去删除。问题一在,如果数据越增越多,会不会影响效率,当然在小范围内我们可以预见到是没有问题的,可过给十年八年呢?问题二,是不是每建一给数据类型,就会有一个对应的Entity类型出现,那也就是说如果我把以前的数据类型的某一项属性稍微更改一下,那么这个旧数据就相当于垃圾一样存在来datastore中?

2. Key这个玩意,感觉上和主键很相像,但是却有很多不同之处。比如GAE规定,Key只能有四种形式,Long, Unencoded String, Key和Key as Encoded String。对于这之间的区别实在是十分模糊。比如在关系数据库中,我们常用的是设立一个自增的主键,而在这边呢,到底是用哪一种呢?而且Key的String值还是自动随机生成的。

3. 数据在google那边到底是如何存储的,是杂乱无章的,还是按照某种顺序?也许就是直接按照Hash来进行存储的,但在获取的时候,如何才能提高效率呢?特别是当数据量非常之大的时候?

思考一下,有结果了再来继续。

另外加些

1. GAE的一些本地内容是存储在\war\WEB-INF\appengine-generated下面的,有两个。分别是datastore-indexes-auto.xml负责存储index信息,和 local-db.bin存储的是本地的数据。

Newer Entries
  • English Version

    • Cannot read Chinese? Please take a look at my English site, hope you can find more you need there!
  • 感谢支持

  • twitter

    facebook

    linkedin

  • Categories