How do I setup a ManyToOne relationship using JPA with AppEngine? - google-app-engine

I've been trying to get a relationship working with 2 entities in AppEngine, using JPA, and am currently running into this error:
java.io.IOException: com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain
My entities look like this:
#Entity
public class MyUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
private List<MyMessage> messages;
}
and this:
#Entity
public class MyMessage {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
#ManyToOne(optional=false)
private MyUser user;
}
The user already exists, and here is where I'm inserting a new message and get the recursion error:
EntityManager mgr = getEntityManager();
MyUser myuser = mgr.find(MyUser.class, KeyFactory.createKey("MyUser", user.getEmail()));
mymessage.setUser(myuser);
myuser.addMessage(mymessage);
mgr.persist(myuser);
mgr.persist(mymessage);
How am I supposed to setup this relationship within JPA and AppEngine guidelines? Thank you!
UPDATE
My problem was involving Jackson, not JPA. The JPA relationship is fine, but I needed to remove the relationship and manage it through the code as it was causing infinite recursion in serializing messages referring to users referring to messages and so on. I've also had to make sure that I annotated the user property in MyMessage as #Transient to avoid persistence complaining about persisting a parent owned by a child which already existed.

I'm unaware of a reasonable way for Endpoints to serialize these classes. The JSON resulting from your current code would look smiilar to the following:
// 1
{
"key": "foo",
"messages": [
{
"key": "bar",
"user": {
// repeat 1
},
// and so on...
}
Your best bet is to define a class (or classes) to send over the wire, instead of your JPA entities, which define JSON an infinite number of levels deep.

Obviously your message is nothing to do with JPA persistence, it's to do with Jackson (so presumably related to how you pass those objects back?). Only you know where that is invoked from. Whether your actual persistence operation succeeds or not is not clear from your post since you don't produce the stack trace, and rest of persistence code (like where that Jackson call originates)

Related

Objectify doesn't always return results

I am using Objectify to store data on Google App Engine's datastore. I have been trying to implement a one-to-many relationship between two classes, but by storing a list of parameterised keys. The method below works perfectly some of the time, but returns an empty array other times - does anyone know why this may be?
It will either return the correct list of CourseYears, or
{
"items": [
]
}
Here is the method:
#ApiMethod(name = "getCourseYears") #ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public ArrayList<CourseYear> getCourseYears(#Named("name") String name){
Course course = ofy().load().type(Course.class).filter("name", name).first().now();
System.out.println(course.getName());
ArrayList<CourseYear> courseYears = new ArrayList<CourseYear>();
for(Key<CourseYear> courseYearKey: course.getCourseYears()){
courseYears.add(ofy().load().type(CourseYear.class).id(courseYearKey.getId()).now());
}
return courseYears;
}
The Course class which stores many CourseYear keys
#Entity
public class Course {
#Id
#Index
private Long courseId;
private String code;
#Index
private String name;
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public List<Key<CourseYear>> getCourseYears() {
return courseYears;
}
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public void setCourseYears(List<Key<CourseYear>> courseYears) {
this.courseYears = courseYears;
}
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
public void addCourseYear(Key<CourseYear> courseYearRef){
courseYears.add(courseYearRef);
}
#Load
#ApiResourceProperty(ignored = AnnotationBoolean.TRUE)
List<Key<CourseYear>> courseYears = new ArrayList<Key<CourseYear>>();
...
}
I am debugging this on the debug server using the API explorer. I have found that it will generally work at the start for a few times but if I leave and return to the API and try and run it again, it will not start working again after that.
Does anyone have any idea what might be going wrong?
Many thanks.
You might want to reduce the amount of queries you send to the datastore. Try something like this:
Course course = ofy().load().type(Course.class).filter("name", name).first().now();
ArrayList<CourseYear> courseYears = new ArrayList<CourseYear>();
List<Long> courseIds = new List<>();
for(Key<CourseYear> courseYearKey: course.getCourseYears()){
courseIds.add(courseYearKey.getId());
}
Map<Long, Course> courses = ofy().load().type(CourseYear.class).ids(courseIds).list();
// add all courses from map to you courseYears list
I also strongly recommend a change in your data structure / entities:
In your CourseYears add a property Ref<Course> courseRef with the parent Course and make it indexed (#Index). Then query by
ofy().load().type(CourseYear.class).filter("courseRef", yourCourseRef).list();
This way you'll only require a single query.
The two most likely candidates are:
Eventual consistency behavior of the high replication datastore. Queries (ie your filter() operation) always run a little behind because indexes propagate through GAE asynchronously. See the GAE docs.
You haven't installed the ObjectifyFilter. Read the setup guide. Recent versions of Objectify throws an error if you haven't installed it, so if you're on the latest version, this isn't it.

GAE jpa database model example

I am totally new at this, I am sorry if it is stupid question.
I am trying to design database model for Google App Engine in JPA, but I am unable to get it right. When I find the way I can't get annotations right or I am getting error about M:N not supported in Google App Engine.
I need entity user to have multiple groups and groups have multiple users and there are users who are also group admins.
My basic model was User -> usergroup(user; group; (bool)isAdmin) <-Group
Can somebody give a clean and simple example of how to define relationships?
Please try this.
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private UserGroup usergroup;
}
class userGroup
#Entity
public class UserGroup {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
private String name;
private boolean admin;
#OneToMany(mappedBy = "usergroup", cascade = CascadeType.ALL)
private List<User> users = new ArrayList<User>();
}
please be noticed GAE have limitation on JPA you can read more here
I don't know anything about Google App Engine, but I can help with JPA though.
The problem here is the "isAdmin" column, which prevents the data model to be a simple #ManyToMany relationship with a joiner table.
With the introduction of this field, in the data model you need a Map on the User entity with key=Group and value=isAdmin, similarly you need a corresponding Map in the Group entity in order to know if each User is an admin.
This is modeled with #ElementCollection in the following way:
#Entity
#Table(name="User")
public class User
{
#Id
#GeneratedValue(strategy= GenerationType.TABLE)
private int id;
private String name;
#ElementCollection
#CollectionTable(name="Users_Groups", joinColumns={#JoinColumn(name="userId")})
#MapKeyJoinColumn(name="groupId")
#Column(name="isAdmin")
private Map<Group, Boolean> groups;
}
#Entity
#Table(name="Group")
public class Group
{
#Id
#GeneratedValue(strategy= GenerationType.TABLE)
private int id;
private String name;
#ElementCollection
#CollectionTable(name="Users_Groups", joinColumns={#JoinColumn(name="groupId")})
#MapKeyJoinColumn(name="userId", insertable=false, updatable=false)
#Column(name="isAdmin", insertable=false, updatable=false)
private Map<User, Boolean> users;
}
The important annotation is #ElementCollection, the other annotations are just to name the specific columns of the collection table and make sure they match from both entities: #CollectionTable gives the name of the table and the name of the column representing the id in the current entity. #MapKeyJoinColumn gives the name of the column representing the id of the "key" element in the Map, and #Column gives the name of the "value" element in the map.
I'm not sure if the insertable=false and updatable=false are needed in one of the entities, might avoid adding duplicate rows due to the cyclic dependency between User and Group.
Also you need to manually create the collection table, because at least EclipseLink tries to create it with two "groupId" and "isAdmin" columns. You might consider reviewing the design if it is absolutely needed a cyclic dependency between User and Group.

Google App Engine JPA getting com.google.appengine.datanucleus.EntityUtils$ChildWithoutParentException

Update: I found out the problem in my case is that I am generating the FbUser primary key by myself using keyfactory.createKey() method. If I change it to auto generate it works fine. But the problem is I don't want to because my data is in String format for the key. So I need to change the type from String to Key manually and then persist it.
I am using Google App Engine JPA and trying to have a oneToMany relationship amongst my entities.
#Entity
public class DummyParent{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
//#Unowned
#OneToMany(targetEntity=FbUser.class, mappedBy="dummyP", fetch=FetchType.LAZY, cascade = CascadeType.ALL)
private ArrayList<FbUser> users;
}
And here FbUser as the child :
#Entity
public class FbUser {
#Id
private Key id;
private String name;
#ManyToOne(fetch=FetchType.LAZY)
private DummyParent dummyP;
}
So after that I instantiate the parent class set its id and set the users. But I get the following exception:
Caused by: com.google.appengine.datanucleus.EntityUtils$ChildWithoutParentException: Detected attempt to establish DummyParent(no-id-yet) as the parent of FbUser("1322222") but the entity identified by FbUser("1322222") has already been persisted without a parent. A parent cannot be established or changed once an object has been persisted.
at com.google.appengine.datanucleus.EntityUtils.extractChildKey(EntityUtils.java:939)
at com.google.appengine.datanucleus.StoreFieldManager.getDatastoreObjectForCollection(StoreFieldManager.java:967)
at com.google.appengine.datanucleus.StoreFieldManager.storeFieldInEntity(StoreFieldManager.java:394)
Any idea why this is happening?
P.s. HRD is already enabled.
So you persisted FbUser without a parent entity and then try to change it at a later date, and GAE Datastore doesn't allow that (as the message says pretty clearly). You present no persistence code so no comment is possible other than guesswork.
Solution : persist it correctly (parent first, then child), or persist them as Unowned.

Displaying Mutable PostgreSQL Arrays in the NetBeans Master/Detail Sample Form using JPA 1.0

Some Background
I have a game database with a table called Games that has multiple attributes and one called Genres. The Genres attribute is defined as an integer[] in PostgreSQL. For the sake of simplicity, I'm not using any foreign key constraints, but essentially each integer in this array is a foreign key constraint on the id attribute in the Genres table. First time working with the NetBeans Master/Detail Sample Form and Java persistence and it's been working great so far except for 1 thing. I get this error when the program tries to display a column that has a 1-dimensional integer array. In this example, the value is {1, 11}.
Exception Description: The object [{1,11}], of class [class org.postgresql.jdbc3.Jdbc3Array], from mapping [oracle.toplink.essentials.mappings.DirectToFieldMapping[genres-->final.public.games.genres]] with descriptor [RelationalDescriptor(finalproject.Games --> [DatabaseTable(final.public.games)])], could not be converted to [class [B].
Exception [TOPLINK-3002] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.ConversionException
My Research
From what I've been able to read, it looks like PostgreSQL arrays need something special done to them before you can display and edit them in this template. By default, the sample form uses TopLink Essentials (JPA 1.0) as its persistence library, but I can also use Hibernate (JPA 1.0).
Here is the code that needs to be changed in some way. From the Games.java file:
#Entity
#Table(name = "games", catalog = "final", schema = "public")
#NamedQueries({
// omitting named queries
#NamedQuery(name = "Games.findByGenres", query = "SELECT g FROM Games g WHERE g.genres = :genres")
})
public class Games implements Serializable {
#Transient
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
private static final long serialVersionUID = 1L;
// omitting other attributes
#Column(name = "genres")
private Serializable genres;
// omitting constructors and other getters/setters
public Serializable getGenres() {
return genres;
}
public void setGenres(Serializable genres) {
Serializable oldGenres = this.genres;
this.genres = genres;
changeSupport.firePropertyChange("genres", oldGenres, genres);
}
} // end class Games
Here are also some of the sites that might have the solution that I'm just not understanding:
https://forum.hibernate.org/viewtopic.php?t=946973
http://blog.xebia.com/2009/11/09/understanding-and-writing-hibernate-user-types/
// omitted hyperlink due to user restriction
Attempted Solutions
I'm able to get the data to display if I change the type of genres to String, but it is immutable and I cannot edit it. This is what I changed to do this:
#Column(name = "genres")
private String genres;
public String getGenres() {
return genres;
}
public void setGenres(String genres) {
String oldGenres = this.genres;
this.genres = genres;
changeSupport.firePropertyChange("genres", oldGenres, genres);
}
I also attempted to create a UserType file for use with Hibernate (JPA 1.0), but had no idea what was going wrong there.
I also attempted to use the #OneToMany and other tags, but these aren't working probably because I'm not using them properly.
What I'm Looking For
There has to be a simple way to get this data to display and make it editable, but since I'm completely new to persistence, I have no idea what to do.
The effort put into your question shows. Unfortunately JPA does not currently support PostgreSQL arrays. The fundamental problem is that arrays are not frequently used in many other databases frequently and so heavy reliance on them is somewhat PostgreSQL specific. Thus you can expect that general cross-db persistence API's are not generally going to support them well if at all. JPA is no exception, having currently no support for PostgreSQL arrays.
I have been looking at writing my own persistence API in Java that would support arrays, but it hasn't happened yet, would be PostgreSQL-only when written, and would be based on a very different principle than JPA and friends.

How to auto-fetch JDO nested collection of entities?

Probably a very trivial problem.
I have an object that looks like this:
#PersistenceCapable
public class Parent {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private String _id;
#Persistent
private List<Child> _children;
//...
}
... the nested entity looks like this (I am forced to declare primary key as Key otherwise it won't persist):
#PersistenceCapable
public class Child {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key _id;
#Persistent
private String _whatever;
//...
}
When I persist everything gets persisted OK (including Child entities), but I would like to get back everything by getting the parent object (e.g. getObjectById), but the collection comes back as null.
Owned One-to-Many Relationships seem to be what I am looking for -- but I am having trouble to see how it can help me to get back the parent object with the populated collection of children entities.
Any help appreciated!
#Persistent(defaultFetchGroup = "true")
Does the trick, you're right.
The content of your _children attribute is loaded only when you access it (before pm.close !) for the first time. It's called lazy-loading. If you want to have the child Entity or Collection of Child Entities to be directly loaded by default, apply the above "trick".
In my app, in case of a Collection of child Entities, it generates an Error message (Datastore does not support joins..) on the Dev Server, but you can ignore this wrong error, it is working fine in Dev and Prod Environments.
Be aware that fetching a Collection through it's parent Entity costs 1 datastore fetch per Child Entity.
this seems to do the trick:
#Persistent(defaultFetchGroup = "true")
True, while setting
#Persistent(defaultFetchGroup = "true")
at the field is the way to auto-load the related object in main object during fetch, things may not work as expected for nested objects if not supported with right configuration. If your have class with related object hosting other related object down to level 2 or 3, then configuring maxFetchDepth appropriately is critical.
<property name="datanucleus.maxFetchDepth" value="2"/>
is the configuration element in your JDOconfig.xml file to configure how deep you want your default fetch group objects to be loaded with main fetch.

Resources