How to store data owned by User? - google-app-engine

I'm learning Google App Engine + Google Cloud Endpoints + Objectify and I'm trying to understand how to create REST API which will let each particular user to save his data in the cloud.
My current struggle is how to store Entity owned by the User (com.google.appengine.api.users.User)?
So far I have endpoint:
#ApiMethod(name = "saveBook")
public void saveBook(Book book, User user) throws OAuthRequestException, IOException {
if (user == null) {
throw new OAuthRequestException("User is not authorized");
}
ofy().save()
.entity(BookRecord.fromBook(user, book))
.now();
}
Entity (in this context let's assume that User wrote the book):
#Entity
public class BookRecord {
#Parent
private Key<User> user;
#Id
private String id;
#Index
private String name;
public static BookRecord fromBook(User user, Book book) {
return new BookRecord(
Key.create(user),
book.getId(),
book.getName()
);
}
public BookRecord() {
}
private BookRecord(Key<User> user, String id, String name, String author) {
this.user = user;
this.id = id;
this.name = name;
this.author = author;
}
}
Problem arises from User not being an Entity, so I can't really use this solution and use User as #Parent directly. What is the general solution to solve this problem and store data owned by User?

Create your own User entity, this is where you'll store custom information/preferences for your app (as I suggested here).
You'll be using getUserId() to tie it with the Google user, and from there on you're free to use it as needed.

Related

Objectify index is not created

I try to use objectify on google app engine standart environment and get exception. My classes look like this:
#Entity
public class Company {
#Id Long id;
#Index String companyName;
public Company() {
}
public Company(Long id, String companyName) {
this.id=id;
this.companyName = companyName;
}
}
#Entity
public class CompanyProject {
#Id Long id;
#Index String projectName;
#Parent Key<Company> owner;
public String cost;
public CompanyProject() {
}
public CompanyProject(long userId, String projectName) {
this();
this.projectName = projectName;
owner = Key.create(Company.class, userId); // Creating the Ancestor key
}
}
When I query data like this:
Key<Company> theUser = Key.create(Company.class, 1);
Iterable<CompanyProject> projects = ObjectifyService.ofy().load().type(CompanyProject.class).ancestor(theUser).order("projectName").list();
I get exception
com.google.cloud.datastore.DatastoreException: no matching index found. recommended index is:
- kind: CompanyProject
ancestor: yes
properties:
- name: projectName
Without order("projectName") query works just fine. Removed all entities of this kind from datastore, than added new, still get this exception. I use Gradle, not Maven if this matters. Maybe should be extra build step to create indexes or smth.
You are filtering on multiple properties (well, ancestor counts as 'another property') so you need a multi-property index defined in datastore-indexes.xml.
See the official documentation: https://cloud.google.com/appengine/docs/standard/java/config/indexconfig

Objectify One-to-Many and Many-To-One in Google App Engine

I'm creating simple backend on Google App Engine using Objectify, that can store Users and thier Recipes.
Model look like this:
User has many Recipes, every Recipe has one author.
I want to be able to:
Get list of Recipes with authors in it
Get list of Users without fetching all recipes
Get one User with all his recipes
According to guide I have done this:
#Entity
public class User {
#Id
private Long id;
#Index
private String name;
List<Recipe> recipes = new ArrayList<>();
/* Other fields */
}
and Recipe :
#Entity
public class Recipe {
#Id
Long id;
String name;
#Index
#Load
Ref<User> author;
/* Other fields */
}
I save Recipe with author (User object) in it.
and 2. requirements work fine,
but when I try to get User with all recipes like this:
public User get(#Named("id") long id) throws NotFoundException {
User user = ofy().load().type(User.class).id(id).now();
if (user == null) {
throw new NotFoundException("Could not find User with ID: " + id);
} else {
List<Recipe> recipes = ofy().load().type(Recipe.class).filter("author", user).list();
account.recipes = recipes;
}
return account;
}
I get empty recipe list.
What am I doing wrong ?
Your author property in your Recipe is a Ref, which is essentially a Key, so you need to filter on that key, like this:
List<Recipe> recipes = ofy().load().type(Recipe.class).filter("author =", Key.create(user)).list();
Incidentally, instead of using .now(), checking for null, then throwing an exception, you could use .safe() which does the same thing for you.

AppEngine + Datastore + Objectify: Http Request returns inconsistent responses

I am implementing AppEngine server as a backend for my Android application. I use Datastore, I query it via Objectify service and I use Endpoints that I call via URL.
I have an entity User with properties like this:
#Id
Long id;
/**
* Name or nickname of the user
*/
public String name;
#Index
public String email;
#JsonIgnore
List<Key<User>> friends;
public User()
{
devices = new ArrayList<String>();
friendsWithKey = new ArrayList<Key<User>>();
}
public static User findRecordById(Long id)
{
return ofy().load().type(User.class).id(id).now();
}
#ApiMethod(name = "friends", httpMethod = "GET", path = "users/{userId}/friends")
public JSONObject getFriends(#Named("userId") String userId)
{
User user = User.findRecordById(Long.parseLong(userId));
JSONObject friendsObject = new JSONObject();
JSONArray jsonArray = new JSONArray();
JSONObject friend;
List<User> userList = new ArrayList<User>();
if (user.friendsWithKey != null)
{
for (Key<User> id : user.friendsWithKey)
{
friend = new JSONObject();
User user1 = User.findRecordById(id.getId());
userList.add(user1);
friend.put("name", user1.name);
friend.put("email", user1.email);
friend.put("id", user1.id);user1.lastTimeOnline.getTime());
jsonArray.add(friend);
}
friendsObject.put("friends", jsonArray);
}
return friendsObject;
}
It sometimes returns only a subset of friends. It is weird and I do not get where I could go wrong. If I get the User object from the DB, it already has a wrong List of Key values. But if I look into the database via console, I can see all of the users that ahve been added as friends.
I reaally need to fix this bug. Please, help.
It is very strange because it only happens once in a while and is non-deterministic in every way.

[RuntimeException: No EntityManager bound to this thread. Try to annotate your action method with #play.db.jpa.Transactional]

this is the problem once i try to save data into db with sql statement insert.
my function is this:
public void save(){
JPA.em().persist(this);
}
and
public static Result registered() {
Form<User> requestform = form(User.class).bindFromRequest();
if(requestform.hasErrors()){
return badRequest("<p>fehlerhafte eingabe!</p>").as("text/html");
} else {
User user = requestform.get();
String fullname = user.fullname;
String email = user.email;
String password = user.password;
String username = user.username;
new User(username, password, fullname, email).save();
}
return redirect(controllers.routes.Application.index());
}
thanks for help
It is just like the debugging message says, you do not have an entity manager bound to your methods because they are not marked as transactions.
#play.db.jpa.Transactional
public static Result registered() {
Also, if you are using EBean, you could just extend Model for your User class, which comes with many handy built in functions for database use, see documentation here: http://www.playframework.org/documentation/2.0.1/JavaEbean

How do I query a single field in AppEngine using JDO

I've got a Product POJO that looks like.
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Product extends AbstractModel {
#Persistent
private String name;
#Persistent
private Key homePage;
#Persistent
private Boolean featured;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Key getHomePage() {
return homePage;
}
public void setHomePage(Key homePage) {
this.homePage = homePage;
}
public boolean isFeatured() {
return featured;
}
public void setFeatured(Boolean featured) {
this.featured = featured;
}
}
My DataStore is currently completely empty.
I'd like to retrieve all homePage keys where featured is true for the Product.
I'm trying
PersistenceManager persistenceManager = getPersistenceManager();
Query query = persistenceManager.newQuery("SELECT homePage FROM " + getModelClass());
query.setFilter("featured == true");
List<Key> productPageKeys = (List<Key>) query.execute();
However this is giving me a null pointer error. How should I be constructing this query?
Cheers,
Peter
To do a projection, you would do something like
Query q = pm.newQuery("SELECT myField FROM mydomain.MyClass WHERE featured == true");
List<String> results = (List<String>)q.execute();
where String is the type of my field. Any basic JDO documentation would define that.
Internally GAE/J will retrieve the Entity, and then in the post-processing before returning it to the user it is manipulated into the projection you require.
As Nick pointed out in the other reply, this gives no performance gain over doing it yourself ... but then the whole point of a standard persistence API is to shield you from such datastore-specifics of having to do such extraction; it's all provided out of the box.
Entities are stored as serialized blobs of data in the datastore, so it's not possible to retrieve and return a single field from an entity. You need to fetch the whole entity, and extract the field you care about yourself.

Resources