我有一个类要保存到appengine数据存储区,其中包含一个Text字段(类似String的appengine数据类型,但不限于500个字符)。也是一个基本相同的双胞胎,但在客户端使用(即没有任何com.google.appengine.api.datastore。* import)。
是否有任何数据类型,可以让我将文本服务器端字段保存到客户端?
一个可能的选项是将文本拆分成一些字符串,但这听起来很难看......
有什么建议吗?
答案 0 :(得分:1)
您可以致电getValue()
将其设为String
。
答案 1 :(得分:1)
您可以将Text用于可持久字段。您只需要一个RPC序列化器就可以在客户端上使用它(在GWT中)。 看看http://blog.js-development.com/2010/02/gwt-app-engine-and-app-engine-data.html,它解释了如何做到这一点。
答案 2 :(得分:0)
在
之前发布的自定义可序列化库的一些附加内容(http://juristr.com/blog/2010/02/gwt-app-engine-and-app-engine-data/ http://www.resmarksystems.com/code/ - 将com.google.appengine.api.datastore.Text和其他数据存储类型转移到客户端)
还需要更新com.google.appengine.eclipse.core.prefs以包含库: filesCopiedToWebInfLib = ... |应用服务引擎-utils的客户端-1.1.jar
另一个解决方法是使字符串可序列化blob克服1500字节限制(它会丢失此字段的排序和过滤能力):
@Persistent(serialized = "true")
public String content;
从com.google.appengine.api.datastore.Text转换为具有生命周期监听器的字符串(不是实例监听器,它们将被发送到客户端并使其失败),客户端可能会减少开销。与自定义序列化一起使用,允许客户端支持com.google.appengine.api.datastore.Text,无需额外的传输类。
com.google.appengine.api.datastore.Text可能会在发送到客户端之前被清除,以避免发送开销(最简单的方法是将其标记为瞬态)。
在服务器端,我们必须避免直接设置String属性,因为jdo不会捕获它的更改(仅捕获新记录或者之后修改某些持久字段)。这是非常小的开销。
应通过pm.makeTransient执行记录分离。当使用pm.detachCopy时,需要将实体标记为可分离=" true" (要调用DetachLifecycleListener)并实现与StoreLifecycleListener.preStore类似的DetachLifecycleListener.postDetach。不会复制非持久性字段(通过pm.detachCopy),并且在客户端上将为空。
可以以类似的方式处理几个类
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.listener.DetachLifecycleListener;
import javax.jdo.listener.InstanceLifecycleEvent;
import javax.jdo.listener.LoadLifecycleListener;
import javax.jdo.listener.StoreLifecycleListener;
import com.google.appengine.api.datastore.Text;
import com.mycompany.mywebapp.shared.Entity;
import com.mycompany.mywebapp.shared.Message;
@SuppressWarnings("rawtypes")
public class PersistenceManagerStuff
{
public static final PersistenceManagerFactory PMF = JDOHelper.getPersistenceManagerFactory("transactions-optional");
public static EntityLifecycleListener entityLifecycleListener = new EntityLifecycleListener();
public static Class[] entityClassList = new Class[] { Entity.class };
public static MessageLifecycleListener messageLifecycleListener = new MessageLifecycleListener();
public static Class[] messageClassList = new Class[] { Message.class };
public static PersistenceManager getPersistenceManager()
{
PersistenceManager pm = PMF.getPersistenceManager();
pm.addInstanceLifecycleListener(entityLifecycleListener, entityClassList);
pm.addInstanceLifecycleListener(messageLifecycleListener, messageClassList);
return pm;
}
// [start] lifecycle listeners
public static class EntityLifecycleListener implements LoadLifecycleListener, StoreLifecycleListener//, DetachLifecycleListener
{
public void postLoad(InstanceLifecycleEvent event)
{
Entity entity = ((Entity) event.getSource());
if (entity.content_long != null)
entity.content = entity.content_long.getValue();
else
entity.content = null;
}
public void preStore(InstanceLifecycleEvent event)
{
Entity entity = ((Entity) event.getSource());
entity.setContent(entity.content);
/*
need mark class @PersistenceAware to use code below, otherwise use setter
if (entity.content != null)
entity.content_long = new Text(entity.content);
else
entity.content_long = null;
*/
}
public void postStore(InstanceLifecycleEvent event)
{
}
/*public void postDetach(InstanceLifecycleEvent event)
{
}
public void preDetach(InstanceLifecycleEvent event)
{
}*/
}
public static class MessageLifecycleListener implements LoadLifecycleListener, StoreLifecycleListener
{
public void postLoad(InstanceLifecycleEvent event)
{
Message message = ((Message) event.getSource());
if (message.content_long != null)
message.content = message.content_long.getValue();
else
message.content = null;
}
public void preStore(InstanceLifecycleEvent event)
{
Message message = ((Message) event.getSource());
message.setContent(message.content);
}
public void postStore(InstanceLifecycleEvent event)
{
}
}
// [end] lifecycle listeners
}
@SuppressWarnings("serial")
@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "false")
public class Entity implements Serializable
{
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
public Long id;
@NotPersistent
public String content;
@Persistent(column = "content")
public transient com.google.appengine.api.datastore.Text content_long;
public void setContent(String content)
{
this.content = content;
if (content != null)
content_long = new Text(content);
else
content_long = null;
}
public Entity() {}
}
@PersistenceAware
public class DataServiceImpl extends RemoteServiceServlet implements DataService
{
public Entity renameEntity(long id, String newContent) throws NotLoggedInException
{
PersistenceManager pm = PersistenceManagerStuff.getPersistenceManager();
Entity result = null;
try
{
Entity entity = (Entity) pm.getObjectById(Entity.class, id);
if (entity.longUserId != getLongUserId(pm))
throw new NotLoggedInException(String.format("wrong entity %d ownership", entity.id));
entity.modificationDate = java.lang.System.currentTimeMillis(); // will call lifecycle handlers for entity.content, but is still old value
//entity.content = newContent; // will not work, even owner class is @PersistenceAware
entity.setContent(newContent); // correct way to set long value
pm.makeTransient(result = entity);
}
catch (Exception e)
{
LOG.log(Level.WARNING, e.getMessage());
throw e;
}
finally
{
pm.close();
}
return result;
}
}
同样在生命周期处理程序中,如果您同时拥有(具有不同的字段名称)并且不希望将旧的转换为新的,则可以将旧(短)和新(长)值混合到单个实体中。但似乎com.google.appengine.api.datastore.Text支持从旧的String值加载。
一些将旧值批量转换为新值的低级代码(使用低级别的com.google.appengine.api.datastore api):
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query q = new Query("Entity");
PreparedQuery pq = datastore.prepare(q);
for (com.google.appengine.api.datastore.Entity result : pq.asIterable())
{
String content = (String) result.getProperty("content");
if (content != null)
{
result.setProperty("content", new com.google.appengine.api.datastore.Text(content));
datastore.put(result);
}
}