First of all, I am kinda a noob on this. So, I am trying to build a WebApp using GWT2.6.1 and GAE1.9.9.
I've done something like this...
#PersistenceCapable
#Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class Person implements IsSerializable {
#PrimaryKey
#Persistent
private String googleUserID;
#Persistent
private String name;
#Persistent
private String secondName;
#Persistent
private String surname;
#Persistent
private Boolean isActive = false; //default value
#Persistent
private String imageURL;
...
}
then,
#PersistenceCapable
#Inheritance(strategy = InheritanceStrategy.NEW_TABLE)
public abstract class User extends Person implements IsSerializable{
#Persistent
private String email;
...
}
and finally,
#PersistenceCapable
#Inheritance(strategy = InheritanceStrategy.NEW_TABLE)
public class Admin extends User implements IsSerializable, Serializable {
private static final long serialVersionUID = 1L;
#NotPersistent
public static final AccountTypes accountType = AccountTypes.Admin;
...
}
Then I am getting the following error:
javax.jdo.JDOFatalUserException: Found inheritance strategy "new-table" on epusp.pcs.os.model.person.user.Admin. This strategy is not supported in this context. Please see the documentation for information on using inheritance with JDO: http://code.google.com/appengine/docs/java/datastore/dataclasses.html#Inheritance
I read the documentation, but I still don't understand what I am doing wrong. Can anyone give me a hint?
PS.: I know, I know, I plan to add some new attributes to Admin and User in the future. Basically what I want to do is to check if a User is registered in database using a GoogleID and then redirect him to a specifed URL based on his AccountType (it may be an Admin, SuperUser, Auditor ...). I was doing something like this:
PersistenceManager pm = PMF.get().getPersistenceManager();
Admin user = null;
try{
user = pm.getObjectById(User.class, userId);
}finally{
pm.close();
}
switch(user.getType()){
case Admin:
return "";
case Agent:
return "";
case Auditor:
return "";
case Monitor:
return "";
case SuperUser:
return "";
default:
return null;
}
Thanks for supporting!
The "new-table" inheritance strategy allows you to split the data for a single data object across multiple "tables," but since the App Engine datastore does not support joins, operating on a data object with this inheritance strategy requires a remote procedure call for each level of inheritance. This is potentially very inefficient, so the "new-table" inheritance strategy is not supported on data classes that are not at the root of their inheritance hierarchies.
Second, the "superclass-table" inheritance strategy allows you to store the data for a data object in the "table" of its superclass. Although there are no inherent inefficiencies in this strategy, it is not currently supported. We may revisit this in future releases.
Now the good news: The "subclass-table" and "complete-table" strategies work as described in the DataNucleus documentation, and you can also use "new-table" for any data object that is at the root of its inheritance hierarchy.
Related
Mongodb is a no-schema document database, but in spring data, it's necessary to define entity class and repository class, like following:
Entity class:
#Document(collection = "users")
public class User implements UserDetails {
#Id private String userId;
#NotNull #Indexed(unique = true) private String username;
#NotNull private String password;
#NotNull private String name;
#NotNull private String email;
}
Repository class:
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
}
Is there anyway to use map not class in spring data mongodb so that the server can accept any dynamic JSON data then store it in BSON without any pre-class define?
First, a few insightful links about schemaless data:
what does “schemaless” even mean anyway?
“schemaless” doesn't mean “schemafree”
Second... one may wonder if Spring, or Java, is the right solution for your problem - why not a more dynamic tool, such a Ruby, Python or the Mongoshell?
That being said, let's focus on the technical issue.
If your goal is only to store random data, you could basically just define your own controller and use the MongoDB Java Driver directly.
If you really insist on having no predefined schema for your domain object class, use this:
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
private Map<String, Object> schemalessData;
// getters/setters omitted
}
Basically it gives you a container in which you can put whatever you want, but watch out for serialization/deserialization issues (this may become tricky if you had ObjectIds and DBRefs in your nested document). Also, updating data may become nasty if your data hierarchy becomes too complex.
Still, at some point, you'll realize your data indeed has a schema that can be pinpointed and put into well-defined POJOs.
Update
A late update since people still happen to read this post in 2020: the Jackson annotations JsonAnyGetter and JsonAnySetter let you hide the root of the schemaless-data container so your unknown fields can be sent as top-level fields in your payload. They will still be stored nested in your MongoDB document, but will appear as top-level fields when the ressource is requested through Spring.
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
// add all other expected fields (getters/setters omitted)
private String foo;
private String bar;
// a container for all unexpected fields
private Map<String, Object> schemalessData;
#JsonAnySetter
public void add(String key, Object value) {
if (null == schemalessData) {
schemalessData = new HashMap<>();
}
schemalessData.put(key, value);
}
#JsonAnyGetter
public Map<String, Object> get() {
return schemalessData;
}
// getters/setters omitted
}
I have a problem with retrieving objects from Google Cloud Datastore using JDO. It is incredibly frustrating, because 99.5% of the time my code works perfectly, but 0.5% of the time some of the data is missing, and I can't find consistent steps to replicate the bug. I'm fairly certain that my issue is with either how I've set up my model or how I'm querying the datastore (I have a suspicion that it may be to do with lazy loading or the default fetch group, but I'm not sure).
Before I explain what's happening it would help to understand the model.
Here is a simplified version of my model:
#PersistenceCapable
#Inheritance(customStrategy = "complete-table")
public class DataObject {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY, defaultFetchGroup="true")
protected Key theKey;
#Persistent()
protected String name;
//...
}
#PersistenceCapable
public class Class1 extends DataObject{
#Persistent()
#Element(dependent="true")
private List<Class2> listOfClass2 = new ArrayList<Class2>();
//...
}
#PersistenceCapable
public class Class2 extends DataObject{
#Persistent()
#Element(dependent="true")
private List<Class3> listOfClass2 = new ArrayList<Class3>();
//...
}
#PersistenceCapable
public class Class3 extends DataObject{
#Persistent()
private String value;
//...
}
And here is the code used to query the data store:
public class DataManager {
public DataObject get(
User user,
Class type,
Long id) throws OAuthRequestException
{
PersistenceManager mgr = getPersistenceManager();
DataObject obj = null;
try
{
obj = mgr.getObjectById(type, id);
getAllChildren(obj);
}
finally
{
mgr.close();
}
if(obj != null)
{
return obj;
}
else
{
throw new EntityNotFoundException("Entity not found");
}
}
/**
* Returns all of the children of the given object
* and their children etc. It is intended to 'touch' every object
* in the tree to accommodate for lazy loading.
*/
private List<StoredDataObject> getAllChildren(DataObject obj)
{
//...
}
}
The problem is that very occasionally, the query will be returned with all of the 'name' fields at a given level empty. For instance, If I retrieve an object of Class1, all of the child Class2 objects will have the 'name' attribute equal to "". The data is definitely in the data store because if I run the query again they will be populated correctly. I have never seen any of the other attributes empty, only the name field. Sometimes it occurs at the Class2 level, sometimes Class3, but never Class1 (as far as I have seen).
It is my understanding that any String attributes should automatically be included in the default fetch group, but am I possibly missing an annotation that forces the 'name' attribute to be retrieved every time?
New observation: When this occurs, it will happen consistently for about 15 minutes as long as I run the same query with the same user credentials. Could this be something to do with caching?
I have entities Profile, Like and Place
Places has Likes.
Likes has reference to place and Profile.
Place has 1-N relation on likes
#PersistenceCapable
public class Place {
#Persistent(mappedBy = "place")
#Element(dependent = "true")
private transient List<Like> likes;
Like has reference to Profile and reference to Place
#PersistenceCapable
public class Like implements Serializable {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private Profile profile;
#Persistent
private Place place;
And profile class hasn't relations to this objects
#PersistenceCapable
public class Profile {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private transient Key key;
What is the best way to add Like to Place existing place with existing profile?
I use the following code to do that:
Profile profile;
Place place;
List<Like> likes;
pm = PMF.get().getPersistenceManager();
try {
place = pm.getObjectById(Place.class, placeId);
likes = place.getLikes();
profile = pm.getObjectById(Profile.class, KeyFactory.createKey(Profile.class.getSimpleName(), login));
} finally {
pm.close();
}
likes.add(new Like(place, profile));
place.setLikes(likes);
pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(place);
} finally {
pm.close();
}
and have duplicate of Profile entity. Is there way to fix it?
Why go to all that trouble of retrieving objects in a transaction, and then close the PM (so the objects become transient, as per the JDO spec) if you're going to add a new Like to the likes of Place? Would make way more sense to just say
place.getLikes().add(new Like(place, profile));
whilst still in the transaction. Indeed, reading about object lifecycles ought to be prerequisite to anybody using any persistence spec (JDO or JPA). Obviously the above is not specific to GAE either.
I would like to model a simple thing but getting in trouble when reading from datastore. I found this question in different flavours but none of the answers helped in my case (using an interface instead of abstract is no option) I´ve one abstract class Media:
#SuppressWarnings("serial")
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
#Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class Media implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk",
value="true")
...
#Persistent
User owner;
}
Movie is extending it.
#SuppressWarnings("serial")
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
public class Movie extends Media implements Serializable{
...
}
One User has a List of Media.
#SuppressWarnings("serial")
#PersistenceCapable(identityType = IdentityType.APPLICATION,
detachable="true")
public class User implements Serializable{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#Extension(vendorName="datanucleus", key="gae.encoded-pk",
value="true")
protected String id;
#Persistent(mappedBy = "owner")
private List<Media> ownedMediaSet = new ArrayList<Media>();
}
The reading operation code is:
#Override
public List<UserDTO> readAllUser() throws IllegalArgumentException {
ArrayList<UserDTO> result = new ArrayList<UserDTO>();
PersistenceManager pm = pmf.getPersistenceManager();
Query q = pm.newQuery("select from " + User.class.getName());
List<User> res = null;
try {
res = (List<User>) q.execute();
for (User u : res) {
UserDTO uDTO = new UserDTO(u.getId(),null, u.getName(), u.getEmail());
result.add(uDTO);
}// for
} catch
This causes NPE:
java.lang.NullPointerException
at
org.datanucleus.store.appengine.DatastoreTable.addFieldMapping(DatastoreTable.java:531)
at org.datanucleus.store.appengine.DatastoreTable.initializeNonPK(DatastoreTable.java:440)
I dont get it. Without Media being abstract everything works fine. Maybe someone knows about the problem and can give me a hint.
Regards
You can not make a list of Media... because there is no instantiable class of media.
--> that means there is no "database table" media
Polymorph relationship doesen't work with GAE...
https://developers.google.com/appengine/docs/java/datastore/jdo/relationships#Polymorphic_Relationships
#Persistent(mappedBy = "owner")
private List<Media> ownedMediaSet = new ArrayList<Media>();
Make the Class Media not abstract then it works.
Or you make a List of movies...
#Persistent(mappedBy = "owner")
private List<Movie> ownedMediaSet = new ArrayList<Movie>();
but thats probably not what you want.
so the last option is what's in this artikle:
https://developers.google.com/appengine/docs/java/datastore/jdo/relationships#Polymorphic_Relationships
make a list of Keys:
#Persistent
private List<Key> ownedMediaSet = new ArrayList<Key>();
Try v2.0 of Googles JDO plugin and see how that goes. Likely it does nothing different yet, but if that is the case you can easily raise an issue with simple testcase at http://code.google.com/p/datanucleus-appengine/issues/list The fact is the v1 plugin did some things in illogical ways (see Sam's answer for some links that this illogical handling caused). You could also just set inheritance strategy to COMPLETE_TABLE since that is all that is really supported with BigTable (i.e each class has a "Kind" that holds all properties for that type).
I am getting this error when running junit test
Testcase: testGet_User(Authentication.UserManagerTest): Caused an ERROR
null
java.lang.NullPointerException
at org.datanucleus.store.appengine.query.DatastoreQuery.getMappingForFieldWithName(DatastoreQuery.java:1307)
at org.datanucleus.store.appengine.query.DatastoreQuery.addLeftPrimaryExpression(DatastoreQuery.java:1107)
at org.datanucleus.store.appengine.query.DatastoreQuery.addExpression(DatastoreQuery.java:871)
at org.datanucleus.store.appengine.query.DatastoreQuery.addFilters(DatastoreQuery.java:832)
at org.datanucleus.store.appengine.query.DatastoreQuery.performExecute(DatastoreQuery.java:230)
at org.datanucleus.store.appengine.query.JDOQLQuery.performExecute(JDOQLQuery.java:89)
at org.datanucleus.store.query.Query.executeQuery(Query.java:1489)
at org.datanucleus.store.query.Query.executeWithArray(Query.java:1371)
at org.datanucleus.jdo.JDOQuery.execute(JDOQuery.java:243)
at Authentication.UserManager.get(UserManager.java:86)
at Authentication.UserManagerTest.testGet_User(UserManagerTest.java:110)
code for get is:
public static UserBean get(User user) {
PersistenceManager pm = PMF.get();
// get user with id
Query query = pm.newQuery(UserCommon.class);
query.setFilter("id == idParam");
query.declareParameters("String idParam");
System.out.println("\t\tID:" + user.getUserId());
List<UserCommon> userDatas = (List<UserCommon>) query.execute(user.getUserId());
Where I have persistent classes that looks :
#PersistenceCapable(detachable="true")
#Inheritance(strategy = InheritanceStrategy.SUBCLASS_TABLE)
public abstract class UserCommon {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String id;
and
#PersistenceCapable(detachable="true")
public class Professor extends UserCommon {
and
#PersistenceCapable(detachable="true")
public class Student extends UserCommon {
basically, I'd like to have 2 types of users.
but while logging in, only information I have is their id.
Hence, I was trying to query on base class instead of either Professor or Student.
However, I got NullPointerException.
any suggestions to where I've made a mistake ?
Thanks in advance !
App Engine JDO has limited support for inheritance, and no polymorphic support http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html#Polymorphic_Relationships
this includes queries.
Personally I would suggest using some other persistence wrapper rather than JDO such as http://code.google.com/p/objectify-appengine/ and http://code.google.com/p/twig-persist/
which do a better job using the advantages of GAE.