1

我有一个类要保存到 appengine 数据存储中,其中包含一个文本字段(类似于字符串的 appengine 数据类型,但不限于 500 个字符)。也是一个基本相同的孪生类,但在客户端使用(即没有任何 com.google.appengine.api.datastore.* 导入)。

是否有任何数据类型可以让我将文本服务器端字段保存到客户端?

一个可能的选择是将文本拆分成一些字符串,但这听起来很丑陋......

有什么建议么?

4

3 回答 3

1

您可以调用getValue()使其成为String.

于 2010-11-13T17:38:03.213 回答
1

您可以将 Text 用于可持久字段。您只需要一个 RPC 序列化程序就可以在客户端(在 GWT 中)使用它。看看http://blog.js-development.com/2010/02/gwt-app-engine-and-app-engine-data.html,它解释了如何做到这一点。

于 2011-01-08T18:21:29.767 回答
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=...|appengine-utils-client-1.1.jar

另一个解决方法是使字符串可序列化 blob 以克服 1500 字节的限制(它将失去对该字段的排序和过滤能力):

@Persistent(serialized = "true")
public String content;

使用生命周期侦听器(不是实例侦听器,它们将被发送到客户端并使其失败)从 com.google.appengine.api.datastore.Text 转换为 String 可以减少客户端的开销。将它与自定义序列化一起使用,从而允许客户端支持 com.google.appengine.api.datastore.Text 而无需额外的传输类。

com.google.appengine.api.datastore.Text 在发送到客户端之前可能会被清除以避免发送开销(最简单的方法是将其标记为瞬态)。

在服务器端,我们必须避免直接设置 String 属性,因为 jdo 不会捕获它的变化(只会捕获新记录或之后修改某些持久字段时)。这是非常小的开销。

记录的分离应通过 pm.makeTransient 执行。使用 pm.detachCopy 时,需要将实体标记为 detachable = "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 支持从旧字符串值加载。

一些将旧值批量转换为新值的低级代码(使用低级 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);
    }
}
于 2015-09-13T22:20:22.020 回答