以前に投稿されたカスタムのシリアライズ可能なライブラリへのいくつかの追加
( 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
もう 1 つの回避策は、1500 バイトの制限を克服するために、文字列をシリアル化可能な BLOB を作成することです (このフィールドの並べ替えおよびフィルター機能が失われます)。
@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;
}
}
また、ライフサイクル ハンドラーでは、古い (短い) 値と新しい (長い) 値の両方を (フィールド名が異なる) 持っていて、古い値から新しい値に変換したくない場合に、これらの値を 1 つのエンティティに混在させることができます。しかし、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);
}
}