Cool demo with jQuery plugin FancyBox

代码如下:

<script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>

<script type="text/javascript" src="http://www.haojii.com/webdev/fancyboxdemo/fancybox/jquery.mousewheel-3.0.2.pack.js"></script>

<script type="text/javascript" src="http://www.haojii.com/webdev/fancyboxdemo/fancybox/jquery.fancybox-1.3.1.js"></script>

<link rel="stylesheet" type="text/css" href="http://www.haojii.com/webdev/fancyboxdemo/fancybox/jquery.fancybox-1.3.1.css" media="screen" />

<script type="text/javascript">
		$(document).ready(function() {
			$("#login").fancybox({
				'padding'			: 0,
				'autoScale'			: false,
				'transitionIn'		: 'none',
				'transitionOut'		: 'none'
			});
			$("#flash").fancybox({
				'padding'			: 0,
				'autoScale'			: false,
				'transitionIn'		: 'none',
				'transitionOut'		: 'none'
			});
		});
	</script>

<div>
<h1><a id="login" href="#register-form" title="This is the title for register form">猛击我吧!</a></h1>
    <div style="display:none"> 

<!-- Registration -->
		<div id="register-form" style="width: 500px; height: 300px; margin:auto;">
			<h1><a id="flash" href="http://www.adobe.com/jp/events/cs3_web_edition_tour/swfs/perform.swf">A Flash Demo</a></h1>
		</div>
   </div>
<!-- /Registration -->  

</div>

美剧更新自动提醒服务(2)

美剧更新自动提醒服务已更新: http://jhnotifier.appspot.com/

源码host在: http://code.google.com/p/jhnotifier/

确切的说,这个应用目前只能叫做Easytv 更新通知服务,目前仅适合追美剧的有线通用户,当然追国产电视剧也是行的,程序的功能是美剧有更新时,主动发送邮件到你的邮箱实现通知。

现在支持多用户定制自己关心的电视剧,使用邮箱登陆上面的地址后,只要在Step2的表单里填入你关心的easytv剧集的URL地址即可体验,欢迎试用。

计划增加的功能:

1. 短信提醒

2. 支持任意用户,对关心的Easytv美剧提交自己的信息,收到更新通知 Done!

3. 增加其他的爬网页,发通知服务(例如美剧论坛BT种子更新,天气预报,或者是定制程度更高的应用)

如何使用GoogleAppEngine的数据存储服务?

本文描述如果使用GoogleAppEngine的数据存储服务,即如何在GAE上创建,删除,查询数据。重点例举四种类型主键的使用。本文是GAE 文档Creating, Getting and Deleting Data的扩展展开。

代码示例基于 appengine-java-sdk-1.3.3.1 +GAE Eclipse插件

1. 数据存储基础

不管是使用传统的关系型数据库还是使用NOSQL架构的数据存储,我们的应用程序存储实体对象时一般都需要主键,方便于之后的数据查询和更新。

JavaEE架构中使用JPA规范统一POJO在关系型数据库中的映射与持久化。因为JPA主要是为关系型数据库设计的,所以GAE仅支持部分JPA规范的内容,GAE文档里关于JPA的说明也比较简陋。

GAE文档里主要介绍基于JDO的数据存储,JDO规范的好处是它抽象级别更高,并不关心底层的数据库是何种实现,所以对于Google的BigTable架构比JPA适用,当前GAE仅实现了JDO规范里面的部分内容(基于开源的DataNucleus)。

GAE文档里的最简单例子,使用JDO存储数据的4个步骤是

1.1 定义POJO数据类,添加JDO Annotation

Employee.java

import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    private String firstName;

    @Persistent
    private String lastName;

    @Persistent
    private Date hireDate;

    public Person(String firstName, String lastName, Date hireDate) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.hireDate = hireDate;
    }

    // Accessors for the fields.  JDO doesn't use these, but your application does.

    public Long getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    // ... other accessors...
}

 

@PersistenceCapable(identityType = IdentityType.APPLICATION)标识该POJO能够通过JDO存储到数据存储区。

@Persistent标识字段为需要持久存储的

@PrimaryKey标识字段为主键

@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)标识在已经被标识为主键的Long字段上标识该主键值自动填充

1.2 设置JDO

1.3 增强POJO使其与JDO实现相关联

这两步GAE Eclipse插件都帮我们搞定了

1.4 获取PersistenceManager实例操作数据类

PMF.java

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }
}

 

1.4.1 存储数据对象

import java.util.Date;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;

import Employee;
import PMF;

// ...
        Employee employee = new Employee("Alfred", "Smith", new Date());

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            pm.makePersistent(employee);
        } finally {
            pm.close();
        }

 

1.4.2 查询获得数据对象

JDO规范的查询格式和JPA的其实差不多


import java.util.List;
import Employee;

// ...
        String query = "select from " + Employee.class.getName() + " where lastName == 'Smith'";
        List<Employee> employees = (List<Employee>) pm.newQuery(query).execute();

 

2. 使用四种类型主键的实体创建,查询,删除

上文中已经领略最简单的主键的例子,在GAE中所有实体都有一个唯一的标识唯一性的键值。根据文档:一个完整的键包含若干条信息,其中包括应用程序ID类型实体ID

直接读这个描述的话可能不太直观,这三个元素一起才能标识你需要在GAE上存储的实体,其实和JavaEE里配置DataSource+JPA ORM Annotation是类似的,可以这么比较

应用程序ID:相当于关系型数据库的Schema

类型:相当于关系型数据库的表

实体ID:相当于关系型数据库的主键

实体的主键字段可以有四种类型,长整型字符串(未编码字符串),编码字符串形式的主键com.google.appengine.api.datastore.Key 前面两个相对简单常见,比较容易用,后面两个稍微有点绕。

2.1 长整型

UserLong.java  POJO数据类


import java.util.Date;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class UserLong {

 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private Long id;

 @Persistent
 private Date date = new Date();
 
 @Persistent
 private String name;
 
 @Persistent
 private String type;

 public UserLong() {

 }

 public UserLong(String name, String type) {
  super();
  this.name = name;
  this.type = type;
 }

// ...

单元测试类TestUserWithLongId,对使用长整型字段为主键的对象的创建,查询,删除

import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.appengine.tools.development.testing.LocalTaskQueueTestConfig;
import com.haojii.jdo.utils.PMF;

public class TestUserWithLongId {

 private static final LocalServiceTestHelper helper = new LocalServiceTestHelper(
   new LocalTaskQueueTestConfig(),
   new LocalDatastoreServiceTestConfig());

 @BeforeClass
 public static void setUpBeforeClass() throws Exception {
  helper.setUp();
 }

 @AfterClass
 public static void tearDownAfterClass() throws Exception {
  helper.tearDown();
 }
 

 @Test
 public void doTest() {
  
  PersistenceManager pm = PMF.get().getPersistenceManager();
  
  System.out.println("0 Create and persist UserLong with @PrimaryKey: Long id");
  UserLong userLong = new UserLong("name1",UserLong.class.getSimpleName());
  pm.makePersistent(userLong);
  
  System.out.println("1 Query UserLong with getObjectById");
     UserLong tmpUserLong = null;
     Long id = userLong.getId();
     tmpUserLong = pm.getObjectById(UserLong.class, id);
     System.out.println("userLong==tmpUserLong?"+(userLong==tmpUserLong));
     Assert.assertTrue(userLong == tmpUserLong);
    
     System.out.println("2 Query UserLong with name1");
     tmpUserLong = null;
     Query query = pm.newQuery(UserLong.class);
     query.setFilter("name == nameParam");
     query.setOrdering("date desc");
     query.declareParameters("String nameParam");
     try {
         List<UserLong> results = (List<UserLong>) query.execute("name1");
         System.out.println("All UserLong size:"+results.size());
         Assert.assertEquals(1, results.size());
        
         if (results.iterator().hasNext()) {
             for (UserLong e : results) {
              tmpUserLong = e;
                 System.out.println(" "+e);
             }
         } else {
          System.out.println("2 Query get empty result");
         }
     } finally {
         query.closeAll();
     }
    
     Assert.assertTrue(tmpUserLong!=null);
     if (tmpUserLong != null)
     {
      System.out.println("3 Update the searched result[0] UserLong with name1->NAME!");
      System.out.println("userLong==tmpUserLong?"+(userLong==tmpUserLong));
      Assert.assertTrue(userLong == tmpUserLong);
     
      tmpUserLong.setName("NAME!");
      pm.makePersistent(tmpUserLong);
     }
    
     System.out.println("4 Delete UserLong with name1");
     System.out.println(" ->"+userLong);
     pm.deletePersistent(userLong);

    
     query = pm.newQuery(UserLong.class);
     int size = ((List<UserLong>) query.execute()).size();
  System.out.println("All UserLong size:"+size);
     Assert.assertEquals(0, size);
 }

}

2.1.1: LocalServiceTestHelper是为GAE本地单元测试提供的辅助类,这里不再赘述,若不明白看一下前面的link的参考文档。

2.1.2: 创建持久化对象pm.makePersistent(entityObject);

2.1.3: 删除持久化对象pm.deletePersistent(entityObject);

2.1.4: 查询长整型主键的对象

方法一,pm.getObjectById(UserLong.class, id)

第一个参数是实体对象的类型,第二个参数是能标识实体的主键这里是长整型的id

方法二,使用JDO查询,可设置查询条件,Query query = pm.newQuery(UserLong.class);

 这个查询返回的是实体的列表集合

2.2 字符串

UserUnEncodedString.java


@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class UserUnEncodedString {
 @PrimaryKey
 private String name;
 @Persistent
 private String type;

 @Persistent
 private Date date = new Date();
 
 public UserUnEncodedString() {
  
 }
 public UserUnEncodedString(String name, String type) {
  super();
  this.name = name;
  this.type = type;
 }

TestUserWithUnEncodedStringId.java

	@Test
	public void doTest() {

		PersistenceManager pm = PMF.get().getPersistenceManager();

		System.out.println("0 Create and persist UserUnEncodedString with @PrimaryKey: String name");
		UserUnEncodedString userUnEncodedString = new UserUnEncodedString("key_name1",UserUnEncodedString.class.getSimpleName());
		pm.makePersistent(userUnEncodedString);

		System.out.println("1 Query UserUnEncodedString with getObjectById");
	    UserUnEncodedString tmpUserUnEncodedString = null;
	    String key = userUnEncodedString.getName();
	    tmpUserUnEncodedString = pm.getObjectById(UserUnEncodedString.class, key);
	    System.out.println("UserUnEncodedString==tmpUserUnEncodedString?"+(userUnEncodedString==tmpUserUnEncodedString));
	    Assert.assertTrue(userUnEncodedString==tmpUserUnEncodedString);

	    System.out.println("2 Query UserUnEncodedString with key_name1");
	    tmpUserUnEncodedString = null;
	    Query query = pm.newQuery(UserUnEncodedString.class);
	    query.setFilter("name == nameParam");
	    query.setOrdering("date desc");
	    query.declareParameters("String nameParam");
	    try {
	        List<UserUnEncodedString> results = (List<UserUnEncodedString>) query.execute("key_name1");
	        System.out.println("All UserUnEncodedString size:"+results.size());
	        Assert.assertEquals(1, results.size());
	        if (results.iterator().hasNext()) {
	            for (UserUnEncodedString e : results) {
	            	tmpUserUnEncodedString = e;
	                System.out.println(" "+e);
	            }
	        } else {
	        	System.out.println("2 Query get empty result");
	        }
	    } finally {
	        query.closeAll();
	    }
	    Assert.assertTrue(tmpUserUnEncodedString != null);
	    if (tmpUserUnEncodedString != null)
	    {
	    	System.out.println("3 Update the searched result[0] UserUnEncodedString with key_name1->KEY_NAME!");
	    	System.out.println("UserUnEncodedString==tmpUserUnEncodedString?"+(userUnEncodedString==tmpUserUnEncodedString));
	    	Assert.assertTrue(userUnEncodedString==tmpUserUnEncodedString);

	    	tmpUserUnEncodedString.setName("KEY_NAME!");
	    	pm.makePersistent(tmpUserUnEncodedString);
	    }

	    System.out.println("4 Delete UserUnEncodedString with key_name1");
	    System.out.println(" ->"+userUnEncodedString);
	    pm.deletePersistent(userUnEncodedString);

	    query = pm.newQuery(UserUnEncodedString.class);
	    int size = ((List<UserUnEncodedString>) query.execute()).size();
		System.out.println("All UserUnEncodedString size:"+size);
	    Assert.assertEquals(0, size);
	}

2.2.1 这里也没有特别的地方,值得稍微说一下的是我们使用未编码的字符串作为主键,则需要我们自己保证主键的键值是唯一的,否则makePersistent方法就是更新对象,而不是创建。这个方法pm.getObjectById(UserUnEncodedString.class, key);接受未编码的字符串为第二个参数用于查询。

2.3 com.google.appengine.api.datastore.Key

UserKey.java

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class UserKey {
     
 @PrimaryKey
 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
 private Key key;
 @Persistent
 private Date date = new Date();
 @Persistent
 private String name;
 @Persistent
 private String type;
 
 public UserKey() {
  
 }
 public UserKey(String name, String type) {
  super();
  this.key = KeyFactory.createKey(UserKey.class.getSimpleName(), name);
  this.name = name;
  this.type = type;
 }

2.3.1 首先看这个POJO数据类,主键标识在下面这个字段上Key,参考GAE文档,它的键值包括父实体组(如果有)的键以及应用程序分配的字符串ID或系统生成的数字ID。要创建带应用程序分配的字符串ID的对象,请创建带有该ID的键值并将字段设置为该值。要创建带系统分配的数字ID的对象,请将键字段留为null。
这里我们选择使用程序自己分配字符串ID的Key,使用KeyFactory.createKey(String kind, String name)方法创建Key实例,第一个参数即上文提到的主键组成成分之一类型,第二个参数是标识这个主键的任意唯一的字符串,我的理解是和未编码字符串主键基本上一致。

TestUserWithKeyId.java

	@Test
	public void doTest() {

		PersistenceManager pm = PMF.get().getPersistenceManager();

		System.out.println("0 Create and persist UserLong with @PrimaryKey: Key");
		UserKey user = new UserKey("name1",UserKey.class.getSimpleName());
		pm.makePersistent(user);

		System.out.println("1 Query UserKey with getObjectById");
	    UserKey tmpUser = null;
	    Key key = user.getKey();

	    tmpUser = pm.getObjectById(UserKey.class, key);
	    System.out.println("(1)user==tmpUser ? "+(user==tmpUser));
	    Assert.assertTrue(user==tmpUser);

	    Key createdKey = KeyFactory.createKey(UserKey.class.getSimpleName(), user.getName());
	    tmpUser = pm.getObjectById(UserKey.class, createdKey);
	    System.out.println("(2)user==tmpUser ? "+(user==tmpUser));
	    Assert.assertTrue(user==tmpUser);

	    System.out.println("2 Query UserKey with name1");
	    tmpUser = null;
	    Query query = pm.newQuery(UserKey.class);
	    query.setFilter("name == nameParam");
	    query.setOrdering("date desc");
	    query.declareParameters("String nameParam");
	    try {
	        List<UserKey> results = (List<UserKey>) query.execute("name1");
	        System.out.println("All UserKey size:"+results.size());
	        Assert.assertEquals(1, results.size());

	        if (results.iterator().hasNext()) {
	            for (UserKey e : results) {
	            	tmpUser = e;
	                System.out.println(" "+e);
	            }
	        } else {
	        	System.out.println("2 Query get empty result");
	        }
	    } finally {
	        query.closeAll();
	    }
	    Assert.assertTrue(tmpUser != null);
	    if (tmpUser != null)
	    {
	    	System.out.println("3 Update the searched result[0] UserKey with name1->NAME!");
	    	System.out.println("user==tmpUser?"+(user==tmpUser));
	    	Assert.assertTrue(user==tmpUser);

	    	tmpUser.setName("NAME!");
	    	pm.makePersistent(tmpUser);
	    }

	    System.out.println("4 Delete UserKey with name1");
	    System.out.println(" ->"+user);
	    pm.deletePersistent(user);

	    query = pm.newQuery(UserKey.class);
	    int size = ((List<UserKey>) query.execute()).size();
		System.out.println("All UserKey size:"+size);
	    Assert.assertEquals(0, size);
	}

2.3.2 测试用例基本上和前面的一致,这里稍微值得说一下的是使用自己创建的key进行查询Key createdKey = KeyFactory.createKey(UserKey.class.getSimpleName(), user.getName());pm.getObjectById(UserKey.class, createdKey);本质上和上一种未编码字符串是一样的。

2.4 编码字符串形式的主键

UserEncodedString.java


@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class UserEncodedString {
     
 @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;
 @Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
    private String keyName;
 //"gae.pk-name" 字段可以在保存对象前设置为键名称。保存对象时,将使用包括键名称的完整键填充编码键字段。其类型必须为 String。
 @Persistent
 private Date date = new Date();
 @Persistent
 private String name;
 @Persistent
 private String type;
 
 public UserEncodedString() {
  
 }
 public UserEncodedString(String keyName, String name, String type) {
  super();
  this.encodedKey = KeyFactory.keyToString(KeyFactory.createKey(UserEncodedString.class.getSimpleName(), keyName));
  this.keyName = keyName;
  this.name = name;
  this.type = type;
 }

2.4.1 这个POJO数据存储类,更绕了一点点弯路,可以和上面的一种使用的Key作为主键的POJO数据类稍微对比一下看,然后就不会觉得太复杂了。
正如注释中所写
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
@Extension(vendorName=”datanucleus”, key=”gae.encoded-pk”, value=”true”)
private String encodedKey;

@Extension(vendorName=”datanucleus”, key=”gae.pk-name”, value=”true”)
private String keyName;
//”gae.pk-name” 字段可以在保存对象前设置为键名称。保存对象时,将使用包括键名称的完整键填充编码键字段。其类型必须为 String。

this.encodedKey = KeyFactory.keyToString(KeyFactory.createKey(UserEncodedString.class.getSimpleName(), keyName));
keyName字段只是用来产生使用程序自己分配字符串ID的Key的,根据这个字符串ID产生Key之后,将Key的编码字符串格式保存于encodedKey当中。

我的理解是可以encodedKey的作用==Key的作用,只是为了某些情况下使用方便。

TestUserWithEncodedStringId.java

	@Test
	public void doTest() {

		PersistenceManager pm = PMF.get().getPersistenceManager();

		System.out.println("0 Create and persist UserLong with @PrimaryKey: String encodedKey");
		UserEncodedString userEncodedString = new UserEncodedString("name1","name1",UserEncodedString.class.getSimpleName());
		pm.makePersistent(userEncodedString);

		System.out.println("1 Query UserEncodedString with getObjectById, using KeyFactory.stringToKey(encodedKey)");
	    UserEncodedString tmpUserEncodedString = null;
	    String encodedKey = userEncodedString.getEncodedKey();
	    System.out.println(userEncodedString);
	    Key key = KeyFactory.stringToKey(encodedKey);
	    System.out.println("KeyFactory.stringToKey(encodedKey)="+key);
	    tmpUserEncodedString = pm.getObjectById(UserEncodedString.class, key);
	    System.out.println("UserEncodedString==tmpUserEncodedString?"+(userEncodedString==tmpUserEncodedString));
	    Assert.assertTrue(userEncodedString==tmpUserEncodedString);

	    System.out.println("1(1) Query UserEncodedString with getObjectById, using KeyFactory.createKey(UserEncodedString.class.getSimpleName(), userEncodedString.getKeyName())");
	    key = KeyFactory.createKey(UserEncodedString.class.getSimpleName(), userEncodedString.getKeyName());
	    tmpUserEncodedString = pm.getObjectById(UserEncodedString.class, KeyFactory.keyToString(key));
		System.out.println(" (1)UserEncodedString==tmpUserEncodedString?"+(userEncodedString==tmpUserEncodedString));
		Assert.assertTrue(userEncodedString==tmpUserEncodedString);

	    System.out.println("1(2) Query UserEncodedString with getObjectById, using userEncodedString.getEncodedKey():"+userEncodedString.getEncodedKey());
	    tmpUserEncodedString = pm.getObjectById(UserEncodedString.class, userEncodedString.getEncodedKey());
	    System.out.println(" (2)UserEncodedString==tmpUserEncodedString?"+(userEncodedString==tmpUserEncodedString));
	    Assert.assertTrue(userEncodedString==tmpUserEncodedString);

	    System.out.println("2 Query UserEncodedString with name1");
	    tmpUserEncodedString = null;
	    Query query = pm.newQuery(UserEncodedString.class);
	    query.setFilter("name == nameParam");
	    query.setOrdering("date desc");
	    query.declareParameters("String nameParam");
	    try {
	        List<UserEncodedString> results = (List<UserEncodedString>) query.execute("name1");
	        System.out.println("All UserEncodedString size:"+results.size());
	        Assert.assertEquals(1, results.size());

	        if (results.iterator().hasNext()) {
	            for (UserEncodedString e : results) {
	            	tmpUserEncodedString = e;
	                System.out.println(" "+e);
	            }
	        } else {
	        	System.out.println("2 Query get empty result");
	        }
	    } finally {
	        query.closeAll();
	    }
	    Assert.assertTrue(tmpUserEncodedString != null);
	    if (tmpUserEncodedString != null)
	    {
	    	System.out.println("3 Update the searched result[0] UserEncodedString with name1->NAME!");
	    	System.out.println("UserEncodedString==tmpUserEncodedString?"+(userEncodedString==tmpUserEncodedString));
	    	Assert.assertTrue(userEncodedString==tmpUserEncodedString);

	    	tmpUserEncodedString.setName("NAME!");
	    	pm.makePersistent(tmpUserEncodedString);
	    }

	    System.out.println("4 Delete UserEncodedString with name1");
	    System.out.println(" ->"+userEncodedString);
	    pm.deletePersistent(userEncodedString);

	    query = pm.newQuery(UserEncodedString.class);
	    int size = ((List<UserEncodedString>) query.execute()).size();
		System.out.println("All UserEncodedString size:"+size);

	    Assert.assertEquals(0, size);
	}

2.4.2 测试用例还是和前面的一致,这里要说的就是下面三个使用getObjectById的查询结果是一致的。
pm.getObjectById(UserEncodedString.class, userEncodedString.getEncodedKey());
第一个使用Key的编码字符串格式直接查询

String encodedKey = userEncodedString.getEncodedKey();
Key key = KeyFactory.stringToKey(encodedKey);
pm.getObjectById(UserEncodedString.class, key);
第二个使用KeyFactory.stringToKey方法根据key查询

key = KeyFactory.createKey(UserEncodedString.class.getSimpleName(), userEncodedString.getKeyName());
pm.getObjectById(UserEncodedString.class, KeyFactory.keyToString(key));
第三个使用程序自己分配字符串ID生成的Key来查询

3. 总结

本文介绍了使用GAE存取数据的基本方法,主要展开并演示了使用4种主键的数据对象存取方法,属于GoogleAppEngine的入门级别文章。

对象关系,事务等相关内容也是构建GAE应用必备的,然而GAE实例代码中并没有很多现成的例子,我瞄到过GAE DataNucleus插件中有些单元测试代码可以参考,因为没有强烈的需求,我还没有深入研究,目前我也只是处于简单的1对1,1对N关系搭起来能用的阶段。