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.
Related
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.
I'm getting into problems with JPA. I'm trying to implement a database that allows users to follow other users and be followed.
I think I'd need (summing up) something like this:
USER_TABLE: id | userName
RELATIONSHIP_TABLE: id | follower | followed | acceptation
I have two entities (also summed up):
#Entity
public class User implements Serializable {
#Id
private Long id;
private String userName;
#OneToMany
private Collection<Relationship> followings;
}
#Entity
public class Relationship implements Serializable {
#Id
private Long id;
private User follower;
private User followed;
private boolean accepted;
}
My problem is that I'm not sure if it's possible to do this, because I obtain more tables that the two that I need.
Can anybody help me?
Thanks and sorry about my english.
You obtain more tables because you did not make the associations bidirectional. JPA has no way to know that Relationship.follower is the other side of the User.followings if you don't tell:
#Entity
public class User implements Serializable {
#OneToMany(mappedBy = "follower")
private Collection<Relationship> followings;
// ...
}
#Entity
public class Relationship implements Serializable {
#ManyToOne
#JoinColumn(name = "follower")
private User follower;
#ManyToOne
#JoinColumn(name = "followed")
private User followed;
// ...
}
The documentation of course explains how that works.
I'm starting my first app with Google Appengine and I am using JDO for managing persistence. I come from a relational database background so I'm having a bit of difficulty getting my head around the appengine datastore and the restrictions it has when it comes to joins.
In my simple example I have a Car and Owner object. Each car has one owner. I'd like to be able to select a car based on the id of the owner (simple to do in regular sql). Is this possible on appengine and if so, how would I go about do it?
Thanks
B
Below are my objects.
#PersistenceCapable
public class Car {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key id;
#Persistent
private String name;
#Persistent
private String colour;
#Persistent(defaultFetchGroup = "true", dependent = "true")
private Owner owner;
…
…
}
#PersistenceCapable
public class Owner {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key id;
#Persistent
private String name;
…
…
}
I have been trying to learn and creating a sample project using GWT/GAE/GoogleDatastore.
Am just trying to figure out what would be the best way to design the data model for a learning management system. Let's say in the traditional way the following are the entities.....
User
Role
UserCourses
Courses
Subjects
Materials
User is one to one to Role
Courses is one to many with Subjects
Subjects is one to many with Materials
Users is Many to Many with Courses using UserCourses
Can someone guide me what would be the best possible way to represent this in JDO ?
---> Extension of the question.
Thank You Shifty, but am completely stuck with unowned relationship model... trying/struggling to come out of the traditional relational model.
Let me take the simple Subjects vs Materials
Am trying out the following model,
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Subjects {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String id;
#Persistent
private List<Materials> materials;
}
public class Materials{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
private String id;
#Persistent
private String materialName;
#Persistent
private String author;
#Persistent
private String materialType;
#Persistent
private String url;
}
When i try to save the materials first and then assigning that object into subjects is having issues. As i read, you cannot assign the child to a parent which is already persisted without parent.
Sometimes it is possible to add materials without assigned to the Subjects, but can get assigned later on.
if you want to make a many-to-many relationship with GAE and JDO you have to store a list of the keys in the models.
User Model
import java.util.Set;
import com.google.appengine.api.datastore.Key;
#PersistenceCapable
public class User {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private Set<Key> courses;
}
Courses Model
import java.util.Set;
import com.google.appengine.api.datastore.Key;
#PersistenceCapable
public class Courses {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private Set<Key> users;
}
this way you don't need the UserCourses class.
EDIT:
If you use
#Persistent
private List<Materials> materials;
you work with a owned relationship model.
this way you can not persist the model first and then add this to the subject model and the persist the subject model.
Just add the not persistent material to the materials list of the subject model and persist the subject model. this will also save the materials.
maybe I could the question wrong but I hope this helps.