How do you generate an Id in App engine datastore automatically?
I am using the automatically created Backend Function insertEntity().
As primary key I am using
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long Id;
And when I call the insert function I would like that the datastore automatically assigns this Id...
Thanks.
Technically, there is nothing wrong with what you have done. The ID will get generated automatically.
The problem I believe is the Auto Generated code that has been generated for the insertEntity() method. The code currently expects that the ID value has been provided and based on that, it first tries to retrieve an existing Entity. Since the ID is most likely null i.e. you have not passed it, it is resulting in an exception in the code.
Modify the code to check if the getId() != null. If not null, only then allow it to go forward, else just invoke the makePersistent() code.
Related
I try to make a chat solution on App Engine for my android app.
A decided that instead of save all messages send to a topic in a separated entity like ChatMessage or something like this, I can save them in a List of Strings inside the Topic entity, like this:
#Entity
public class Topic {
#Id
public String id;
public List<Long> users = new ArrayList<Long>(2);
public Long lastChangeTime;
public LinkedList<String> messages = new LinkedList<String>();
}
I came up with this because usually storing the topic id for every message is more data than the message string itself. :S
What I don't know is, can this list strong consistent?
This is how I add a new message to a Topic:
// 2. get topic if it exists or create a new if not
Topic topic = ofy().load().key(Key.create(Topic.class, topicId)).now();
if (topic == null) {
topic = new Topic(senderId, recipientId);
}
// 3. add message
// this method adds the new string into the topic and update the
// last change time
topic.addMessage(senderId, recipientId, send.message);
// 4. save topic & the new message
ofy().save().entity(topic).now();
So if two users send a message at the same time, can it happens that the first user load the Topic, add his message, but in the same time the second user already loaded the topic (without the first user's message) and add his own new message. The first save the topic first. But can the second override the previous save of first user? Or what happens?
If it can happen, how can i avoid this, bearing in mind that it's a high write rate entity so I need more write than 1/sec!
Thanks, and best regards.
What I don't know is, can this list strong consistent?
Consistency is determined by entity groups and queries, not properties.
So if two users send a message at the same time, can it happens that the first user load the Topic, add his message, but in the same time the second user already loaded the topic (without the first user's message) and add his own new message. The first save the topic first. But can the second override the previous save of first user? Or what happens?
You would need to do this inside a transaction. If a ConcurrentModificationException is thrown inside the transaction (your example scenario) then Objectify will retry for you.
But, to avoid the contention, you will need to change your data model. You could have a Message class and a Topic, like this:
#Entity
public class Topic {
#Id
String id;
List<Long> users = new ArrayList<Long>(2);
Long lastChangeTime;
}
And a Message referencing one or more topics (I'm making assumptions here):
#Entity
public class Message {
#Id
Long id;
Long lastChangeTime;
#Index
Ref<Topic> topic;
}
The #Index annotation on the topic will allow you to query for Messages by topic. You could change the Ref<Topic> to a List of same if you messages can be in multiple topics.
I'm trying to implement pagination using App Engine's RPC and GWT (it's an app engine connected project).
How can I pass both the query results and the web-safe cursor object to the GWT client from the RPC?
I've seen examples using a servlet but I want to know how to do it without a servelt.
I've considered caching the cursor on the server using memcache but I'm not sure if that's appropriate or what should be used as the key (session identifier I would assume, but I'm not sure how those are handled on App Engine).
Links to example projects would be fantastic, I've been unable to find any.
OK, so the best way to do this is to store the cursor as a string on the client.
To do this you have to create a wrapper class that is transportable so you can pass back it to the client via RequestFactory that can hold the results list and the cursor string. To do that you create a normal POJO and then a proxy for it.
here's what the code looks like for the POJO:
public class OrganizationResultsWrapper {
public List<Organization> list;
public String webSafeCursorString;
public List<Organization> getList() {
return list;
}
public void setList(List<Organization> list) {
this.list = list;
}
public String getWebSafeCursorString() {
return this.webSafeCursorString;
}
public void setWebSafeCursorString(String webSafeCursorString) {
this.webSafeCursorString = webSafeCursorString;
}
}
for the proxy:
#ProxyFor(OrganizationResultsWrapper.class)
public interface OrganizationResultsWrapperProxy extends ValueProxy{
List<OrganizationProxy> getList();
void setList(List<OrganizationProxy> list);
String getWebSafeCursorString();
void setWebSafeCursorString(String webSafeCursorString);
}
set up your service and requestFactory to use the POJO and proxy respectively
// service class method
#ServiceMethod
public OrganizationResultsWrapper getOrganizations(String webSafeCursorString) {
return dao.getOrganizations(webSafeCursorString);
}
// request factory method
Request<OrganizationResultsWrapperProxy> getOrganizations(String webSafeCursorString);
Then make sure and run the RPC wizard so that your validation process runs otherwise you'll get a request context error on the server.
Here's the implementation in my data access class:
public OrganizationResultsWrapper getOrganizations(String webSafeCursorString) {
List<Organization> list = new ArrayList<Organization>();
OrganizationResultsWrapper resultsWrapper = new OrganizationResultsWrapper();
Query<Organization> query = ofy().load().type(Organization.class).limit(50);
if (webSafeCursorString != null) {
query = query.startAt(Cursor.fromWebSafeString(webSafeCursorString));
}
QueryResultIterator<Organization> iterator = query.iterator();
while (iterator.hasNext()) {
list.add(iterator.next());
}
resultsWrapper.setList(list);
resultsWrapper.setWebSafeCursorString(iterator.getCursor().toWebSafeString());
return resultsWrapper;
}
a second option would be to save the webSafeCursorString in the memcache, as you already mentioned.
my idea looks like this:
the client sends always request like this "getMyObjects(Object... myParams, int maxResults, String clientPaginationString)". the clientPaginationString is uniquely created like shown below
server receives request and looks into the memcache if there is a webSafeCursorString for the key clientPaginationString
if the server finds nothing, he creates the query and save the webSafeCursorString into memcache with the clientPaginationString as the key. -> returns the results
if the server finds the webSafeCursorString he restarts the query with it and returns the results
the problems are how to clean the memcache and how to find a unique clientPaginationString:
a unique clientPaginationString should be the current UserId + the params of the current query + timestemp. this should work just fine!
i really can't think of a easy way how to clean the memcache, however i think we do not have to clean it at all.
we could store all the webSafeCursorStrings and timestemps+params+userid in a WebSafeCursor-Class that contains a map and store all this in the memcache... and clean this Class ones in a while (timestamp older then...).
one improvement i can think of is to save the webSafeCursorString in the memcache with a key that is created on the server (userSessionId + servicename + servicemethodname + params). however, important is that the client sends an information if he is interested in a new query (memcache is overriden) or wants the next pagination results (gets webSafeCursorString from memcache). a reload of the page should work. a second tap in the browser would be a problem i think...
what would you say?
I'm execute method Datastore.delete(key) form my GWT web application, AsyncCallback had call onSuccess() method .Them i refresh http://localhost:8888/_ah/admin immediately , the Entity i intent to delete still exist. Smilar to, I refresh my GWT web application immediately the item i intent to delete still show on web page.Note the the onSuccess() had been call.
So, how can i know when the Entity already deleted ?
public void deleteALocation(int removedIndex,String symbol ){
if(Window.confirm("Sure ?")){
System.out.println("XXXXXX " +symbol);
loCalservice.deletoALocation(symbol, callback_delete_location);
}
}
public AsyncCallback<String> callback_delete_location = new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}
public void onSuccess(String result) {
// TODO Auto-generated method stub
int removedIndex = ArryList_Location.indexOf(result);
ArryList_Location.remove(removedIndex);
LocationTable.removeRow(removedIndex + 1);
//Window.alert(result+"!!!");
}
};
SERver :
public String deletoALocation(String name) {
// TODO Auto-generated method stub
Transaction tx = Datastore.beginTransaction();
Key key = Datastore.createKey(Location.class,name);
Datastore.delete(tx,key);
tx.commit();
return name;
}
Sorry i'm not good at english :-)
According to the docs
Returns the Key object (if one model instance is given) or a list of Key objects (if a list of instances is given) that correspond with the stored model instances.
If you need an example of a working delete function, this might help. Line 108
class DeletePost(BaseHandler):
def get(self, post_id):
iden = int(post_id)
post = db.get(db.Key.from_path('Posts', iden))
db.delete(post)
return webapp2.redirect('/')
How do you check the existence of the entity? Via a query?
Queries on HRD are eventually consistent, meaning that if you add/delete/change an entity then immediately query for it you might not see the changes. The reason for this is that when you write (or delete) an entity, GAE asynchronously updates the index and entity in several phases. Since this takes some time it might happen that you don't see the changes immediately.
Linked article discusses ways to mitigate this limitation.
I am using Google App Engine and using Google's datastore interface for a database .
My question is this , I have the following code : I have a network object that I want to either update if it exists on db , or to create if it's the first time. . For this I have to catch an exception and repeat the same code twice - it seems ugly and redundant and makes me think I'm doing something wrong .
The second thing that strikes me as odd is that there is no method I can think of that copies an object to an entity or vice versa . Am I expected to implement this myself ? It is very uncomfrotable to use the setProperty or getProperty for each property and well ...I am just wondering why there is no objectToEntity method or something of the sort.
This is how my code currently looks ...
try {
Entity network=datastore.get(KeyFactory.stringToKey(networks.get(i)._ipDigits));
//If I get here no exception was thrown - entity already exists on db.
Network contextNet= //fetch the network object from servlet context ...
network.setProperty("ip", contextNet._ip); //update the fields using setProperty - no better way??
network.setProperty("offlineUsers",contextNet._offlineUsers);
datastore.put(network);
}
//Entity doesn't exist , create a new entity and save it (while repeating the same code)...
catch (EntityNotFoundException e) {
Entity network=new Entity("network",Long.parseLong(networks.get(i)._ipDigits));
Network contextNet= // ...fetch the network object from servlet context
network.setProperty("ip", contextNet._ip);
network.setProperty("offlineUsers",contextNet._offlineUsers);
datastore.put(network);
}
You don't have to get and put the entity in order to update it. If you know the ID of the entity you can just put it. If it exists it will be updated, if not it will be created.
Use objectify to automatically map your classes to entities.
I am struggling while handling sessions in GAE. I am trying to store a two classes and a string in session. Although on DEV environment it runs fine, on production a class and a string are not being persisted in session. The class that is not getting saved as a session attribute is as follows:
#PersistenceCapable(detachable="true")
public class Agent implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent private String name; //Name of the Agency
#Element(dependent = "true")
private List<Contact> contacts = new ArrayList<Contact>();
#Element(dependent = "true")
private List<Agency> agencies = new ArrayList<Agency>();
#Persistent private List<Long> subAgents = new ArrayList<Long>();
#Persistent private Date createdOn = new Date();
}
I would like to mention again that it works fine on DEV Environment but on production I get values as null. As you can see I have made the class implement Serializable. But I think it is not the problem because I am setting one more attribute as a simple string and that also is failing (I get the attribute value as null). Session however is created as I can see it at the backend and also there is one more class which is persisted in session.
Anybody have suggestions? Thanks in advance.
Your problem is probably related to either:
GAE often serializes sessions almost immediately, dev environment doesn't. So all objects in your graph must implement Serializable.
BUT EVEN MORE LIKELY is that after you modify a session variable, you must do something like req.getSession().setAttribute(myKey,myObj) - it WILL NOT see changes in your object and automatically write them back to the session... so the session attributes will have the value of whatever they had when they were last set.
Problem #2 above cost me countless time and pain until I tripped over (via a lengthy process of elimination).
Have you enabled sessions in your configuration file?
http://code.google.com/intl/en/appengine/docs/java/config/appconfig.html#Enabling_Sessions
Making classes Agency and Contact Serializable solves the problem. That mean each and every object (be it nested or otherwise) which is present inside a session attribute should be serializable.