I have the following two entities:
#Entity
public class SupermarketChain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String key;
#OneToMany(mappedBy = "supermarketChain")
#Basic
private List<Supermarket> supermarkets;
}
#Entity
public class Supermarket {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
private String key;
#ManyToOne(optional=true)
private SupermarketChain supermarketChain;
}
When I'm deleting a parent with em.remove(SupermarketChain.class, key), all orphans will be deleted too. I read the relevant paragraph in the documentation, even tried it with JDO with #Element(dependent = "false") but the problem remains. How can I retain the orphans in that relation?
Retaining an orphan makes no sense. In v1 of GAE JDO/JPA all relations are "owned" so you have to have a parent of any child. And if the parent no longer exists then the child is deleted. Always.
In v2 of GAE JDO/JPA you will also be able to have unowned objects, hence there is no "parent" and so they can continue to exist after.
Related
I'm new to App Engine and trying to figure out how to use relationships between entities.
I'm using JPA and having trouble to understand how to organize the relationships.
I have three classes City, Hotel and Attraction. I want cities to be standalone and able to be created on it's own. Every city has a list of all available hotels in the city. The hotel always need a city and can only have one city. Attractions have to have a city, but a city doesn't need to know about the attractions.
Classes:
#Entity(name = "City")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(??)
private List<Hotel> hotels;
//getters and setters
}
#Entity(name = "Hotel")
public class Hotel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String name;
#ManyToOne(??)
private City city;
}
#Entity(name = "Attraction")
public class Attraction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#Unowned??
private City city;
//getters and setters
}
I've been looking all over the web, but i can't find any good tutorials on this. Would really appreciate any pointers in the right direction!
There is series of posts - "JDO/JPA Snippets That Work" on appengine java google group, which is a good starting point.
This one shows how to create a bidirectional, owned, one-to-many relationship.
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.
I have a one-to-many relationship between Book and Chapter. I am able to create a book object and add chapters to it successfully (I look in the datastore and see my creation). However, after a fetch a book if I try to loop through the chapters, I get the error
javax.jdo.JDODetachedFieldAccessException: You have just attempted to access field
"chapters" yet this field was not detached when you detached the object. Either dont
access this field, or detach it when detaching the object.
After much research, I finally found a blog that says just place #Basic on the getChapters method. When I do that, I get this new error:
java.lang.IllegalStateException: Field "Book.chapters" contains a persistable object
that isnt persistent, but the field doesnt allow cascade-persist!
I have been trying all sorts of things, the latest look of the models is
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL)
private List<Chapter> chapters = new ArrayList<Chapter>();
}
#Entity
public class Chapter {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
#ManyToOne(fetch = FetchType.EAGER)//already tried without annotation and with FetchType.LAZY
private Book book;
}
You need to declare the cascade type on your Book attribute, so that JPA knows what to do when performing operations on your Chapter entity.
#Entity
public class Chapter {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
#ManyToOne(cascade = CascadeType.ALL) // you can also add fetch type if needed
private Book book;
}
Here is the description of all the cascade types.
#Entity
public class Group
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
}
#Entity
public class User
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
#ManyToOne(fetch = FetchType.LAZY)
private Group group;
}
After the following code :
EntityManager entityManager = EntityManagerFactoryHolder.getEntityManagerFactory().createEntityManager();
Group group = new Group();
entityManager.persist(group);
User user = new User();
user.setGroup(group);
entityManager.persist(user);
entityManager.close();
I get the following error
Detected attempt to establish User(28) as the parent of Group(27) but
the entity identified by Group(27) has already been persisted without
a parent. A parent cannot be established or changed once an object
has been persisted.
org.datanucleus.store.appengine.DatastoreRelationFieldManager$ChildWithoutParentException:
Detected attempt to establish User(28) as the parent of Group(27) but
the entity identified by Group(27) has already been persisted without
a parent. A parent cannot be established or changed once an object
has been persisted.
Seems that this only works with back association. Added #OneToMany set to Group object and it works now.
Does anyone have tried to implement an app in GAE having both java and python?
I have an existing app and my front end is in java. Now I want to use the existing datastore to be interfaced by python. My problem is i don't know how to define the relationships and model that would be equivalent to the one in java. I have tried the one-to-many relationship in python but when stored in the datastore, the fields are different than the one-to-many of java.
My data classes are as follows.
//one-to-many owned
Parent Class
public class Parent{
#PrimaryKey
#Persistent
private String unitID;
//some other fields...
#Persistent
#Order(extensions = #Extension(vendorName="datanucleus", key="list-ordering", value="dateCreated desc"))
private List <Child> child;
//methods & constructors were omitted
}
Child
public class Child{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key uId;
#Persistent
private String name;
/* etc... */
}
Through some testing, I finally figured out how to do this. First is define the models or in java, it is called classes.
class Parent(db.Model):
someData = db.StringProperty(multiline=True) ....
class Child(db.Model):
someData = db.StringProperty(multiline=True) ...
Now, to set the relationship of an instance of a child to its parent, just set the parent as the ancestor of the child.
parentModel = Parent(key_name='key_of_parent')
childModel1 = Child(parent=parentModel) #set the parentModel as parent of the childModel1
childModel2 = Child(parent=parentModel) #set the parentModel as parent of the childModel2
Now you have an owned one-to-many relationship.