Using DataNucleus' list-ordering extension leads to empty list - google-app-engine

So, I have an unidirectional one-to-many relationship where I want to keep the children in an ordered list. Since they already have an "index" property, I tried to follow the advice on http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html and use the "list-ordering" extension to use that index-property to determine the order of the children instead of using an auto-generated one.
Unfortunately, as soon as I add the annotation it stops returning children and only gives me an empty list.
I recreated the problem with this simple example:
#PersistenceCapable(detachable = "true")
#FetchGroup(name = "parent.children", members = {#Persistent(name = "children")})
public class Parent {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
#Order(extensions = #Extension(vendorName="datanucleus", key="list-ordering", value="index ASC"))
private List<Child> children;
// getters/setters
}
#PersistenceCapable(detachable = "true")
public class Child {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private Integer index;
// getters/setters
}
DAO:
public void save(T entity) {
PersistenceManager pm = getPersistenceManager();
Transaction tx = pm.currentTransaction();
try {
tx.begin();
pm.makePersistent(entity);
tx.commit();
} finally {
if(tx.isActive())
tx.rollback();
pm.close();
}
}
public T get(Key key, String... fetchGroups) {
PersistenceManager pm = getPersistenceManager();
Transaction tx = pm.currentTransaction();
addFetchGroups(pm, fetchGroups);
try {
tx.begin();
pm.setDetachAllOnCommit(true);
T entity = (T) pm.getObjectById(entityClass, key);
tx.commit();
return entity;
} finally {
if(tx.isActive())
tx.rollback();
pm.close();
}
}
Test code:
Parent parent = new Parent();
Child child = new Child();
child.setIndex(10);
parent.getChildren().add(child);
mParentDao.save(parent);
Parent parent2 = mParentDao.get(parent.getKey(), "parent.children");
Is there anything in particular that I am doing wrong?
[EDIT] Here is the related log output:
Datastore: Putting entity of kind PARENT with key PARENT(no-id-yet)
Datastore: Putting entity of kind CHILD with key PARENT(3)/CHILD(no-id-yet)
Datastore: INDEX : 10
Datastore: Committed datastore transaction: 0
Datastore: Started new datastore transaction: 1
Datastore: Getting entity of kind PARENT with key PARENT(3)
Datastore.Retrieve: Preparing to query for all children of PARENT(3) of kind CHILD
Datastore.Retrieve: Added sort: index ASCENDING
Datastore.Retrieve: Query had 0 results.
Datastore: Committed datastore transaction: 1

Im using GAE plugin 1.7.0 with JDO and my scenario is exactly the same. I have a list of items and i need to maintain their order as well.
Now i used the app for a long period of time without doing the above changes (without implementing the feature).
Today i implemented order feature using the article and the data isn't being retrieved! the data is present in database but are not fetched during parent object load. Even though the list is marked with:
#Persistent(defaultFetchGroup = "true")
#Element(dependent = "true")

Related

Objectify Delete doesn't seem to be working

I'm trying to delete an entity from my datastore using objectify but doesn't seem to be deleted even after shutting down the instance and restarting it. This is what the entity looks like in the datastore (both when it's on the production server & dev server):
This is the code i'm using to try and delete it:
#ApiMethod(name = "deleteDataVersion")
public Result deleteDataVersion(#Named("id") String id) {
// Where id is the id of the entity in the datastore.
if (id != null && !id.equals("")) {
ofy().delete().type(DataVersion.class).id(id).now();
return new Result(Result.STATUS_SUCCESS);
} else
return new Result(Result.STATUS_FAILED);
}
I've also tried this code:
#ApiMethod(name = "deleteDataVersion")
public Result deleteDataVersion(#Named("id") String id) {
if (id != null && !id.equals("")) {
// DataVersion doesn't have a parent.
Key<DataVersion> key = Key.create(null, DataVersion.class, id);
ofy().delete().key(key).now();
return new Result(Result.STATUS_SUCCESS);
} else
return new Result(Result.STATUS_FAILED);
}
But the entity never gets deleted. This is the code for my entity:
#Entity
public class DataVersion {
#Id
private Long id;
String folderName;
#Index
String effective;
public DataVersion() {
}
public DataVersion(String folderName, String effective ) {
this.folderName= folderName;
this.effective = effective;
}
// Getters & setters..
}
I just can't seem to find the problem :( Any help would be greatly appreciated! I'm sure it's something minor I'm overlooking (fairly new to Objectify/AppEngine).
The ID you have in parameter in your Endpoint is a String, and you try to delete the object DataVersion where the ID is a Long.
ofy().delete().type(DataVersion.class).id(Long.valueOf(id)).now();
would work better !
First get the key.
Key<DataVersion> key = Key.create(null, DataVersion.class, id);
Then fetch the entity from the database using the key.
DataVersion dataVersion = ofy().load().key(key).now();
Then delete the entity using objectify.
ofy().delete().entity(dataVersion).now();

Hibernate #OneToMany remove child from list when updating parent

I have the following entities:
TEAM
#Entity
#Table
public class Team {
[..]
private Set<UserTeamRole> userTeamRoles;
/**
* #return the userTeamRoles
*/
#OneToMany(cascade = { CascadeType.ALL }, mappedBy = "team", fetch = FetchType.LAZY)
public Set<UserTeamRole> getUserTeamRoles() {
return userTeamRoles;
}
/**
* #param userTeamRoles
* the userTeamRoles to set
*/
public void setUserTeamRoles(Set<UserTeamRole> userTeamRoles) {
this.userTeamRoles = userTeamRoles;
}
}
and
USER_TEAM_ROLE
#Entity
#Table(name = "user_team_role")
public class UserTeamRole {
#ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinColumn(name = "FK_TeamId")
public Team getTeam() {
return team;
}
}
Now, when updating a Team entity that contains for example Team.userTeamRoles = {UTR1, UTR2} with {UTR1, UTR3}, I want UTR2 to be deleted. But the way I do it now, the old list remains the same and it only adds UTR3 to the list.
This is how I do it at the moment:
if (!usersDualListData.getTarget().isEmpty()) {
// the role for each user within the team will be "employee"
team.setUserTeamRoles(new HashSet<UserTeamRole>());
Role roleForUser = roleService
.getRoleByName(RoleNames.ROLE_EMPLOYEE.name());
for (User user : usersDualListData.getTarget()) {
UserTeamRole utr = new UserTeamRole();
utr.setUser(user);
utr.setTeam(team);
utr.setRole(roleForUser);
team.getUserTeamRoles().add(utr);
}
}
teamService.updateTeam(team);
I thought that by doing team.setUserTeamRoles(new HashSet<UserTeamRole>()); the list would be reset and because of the cascades the previous list would be deleted.
Any help is appreciated. Thank you
Instead of replacing the collection (team.setUserTeamRoles(new HashSet<UserTeamRole>());) you have to clear() the existing one. This happens because if Hibernate loads the entity (and its collections) from DB, it "manages" them, ie. tracks their changes. Generally when using Hibernate it's better not to create any setters for collections (lists, sets). Create only the getter, and clear the collection returned by it, ie:
team.getUserTeamRoles().clear();
Another thing is that you miss orphan deletion (ie. delete child object when it's removed from collection in the parent). To enable it, you need to add #OneToMany(orphanRemoval=true) in owning entity.

How to persistent Map in JPA in GAE

I don't know why I can't persistent MAP in JPA in GAE
AnnualReport thatyear = .......
if (stud.getAnnualReport() == null){
Map<Integer,AnnualReport> temp = new HashMap<Integer,AnnualReport>();
temp.put(thatyear.getAttrKey(), thatyear);
stud.setAnnualReport(temp);
} else{
Map<Integer,AnnualReport> temp2 = stud.getAnnualReport();
temp2.put(thatyear.getAttrKey(), thatyear);
stud.setAnnualReport(temp2);
}
em.getTransaction().begin();
try {
em.persist(stud);
em.getTransaction().commit();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
}
Actually in http:// localhost :8888/_ah/admin/datastore I can see the thatyear has been persistent; However, I can never get them; or, stud.getAnnualReport() is always empty.
EntityManager em;
em = EMF.get().createEntityManager();
AnnualReport thatyear = stud.getAnnualReport().get(yearselected);
I really don't know what to do. Following is the relationship between Stud & AnnualReport
Stud
#Entity( name = "Stud")
public class Stud{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key studID;
private String lastName = new String();
private Map<Integer,AnnualReport>annualReport = new HashMap<Integer,AnnualReport>(20);
#OneToMany(mappedBy="stud",cascade = CascadeType.ALL)
#MapKey(name = "attrKey")
#Basic
public Map<Integer, AnnualReport> getAnnualReport() {
return annualReport;
}
AnnualReport
#Entity( name = "AnnualReport")
public class AnnualReport implements Serializable{
private static final long serialVersionUID = 3581307841164176872L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key annualReportID;
public int attrKey;
#ManyToOne
Stud stud;
private String attendSchoolNote;
I don't know what happens. Why I can't get those map information which are already persistent?
No idea why you don't get the expected result, but then you present no debug info. You can easily follow the persistence process using the log, telling you what is actually persisted into the GAE Entity objects. GAE has a (JDO) unit test at
http://code.google.com/p/datanucleus-appengine/source/browse/trunk/tests/com/google/appengine/datanucleus/jdo/JDOMapTest.java
which demonstrates correct behaviour (and since JDO/JPA is simply a wrapper over the persistence engine, no reason to think the same would not persist fine using JPA).
Edit : in fact I just added a test for JPA maps at http://code.google.com/p/datanucleus-appengine/source/browse/trunk/tests/com/google/appengine/datanucleus/jpa/JPAMapTest.java and works fine.

GAE/JPA/DataNucleus: Strange exception while trying to persist entity (IllegalArgumentException: out of field index :-1)

I'm getting an exception after I added this embedded field in my entity:
#Entity
public class Team extends DataObject
{
#Embedded
private TeamEvolution teamEvolution = new TeamEvolution();
// NEW FIELD:
#Embedded
// #AttributeOverrides({ #AttributeOverride(name = "buffer", column = #Column) })
// #Enumerated
private ScoutBuffer scoutBuffer;
...
This guy is very simple:
#Embeddable
public class ScoutBuffer
{
private static final int BUFFER_SIZE = 150;
#Basic
private List<String> buffer;
... // from here on there are only methods...
When I try to merge my modifications I get the following exception:
java.lang.IllegalArgumentException: out of field index :-1
at com.olympya.futweb.datamodel.model.ScoutBuffer.jdoProvideField(ScoutBuffer.java)
at org.datanucleus.state.JDOStateManagerImpl.provideField(JDOStateManagerImpl.java:2585)
at org.datanucleus.state.JDOStateManagerImpl.provideField(JDOStateManagerImpl.java:2555)
at org.datanucleus.store.mapped.mapping.CollectionMapping.postUpdate(CollectionMapping.java:185)
at org.datanucleus.store.mapped.mapping.EmbeddedPCMapping.postUpdate(EmbeddedPCMapping.java:133)
// etc, etc...
I don't think there's anything to do, but I had to use JDOHelper.makeDirty before merging the entity for it to perceive that I modified scoutBuffer:
team.getScoutBuffer().add(playerIds);
JDOHelper.makeDirty(team, "scoutBuffer");
em.merge(team);
As you can see commented in the code, I tried the workaround described here, without success. Strange thing is that is from 2009... I'm using GAE 1.7.0, by the way. Also, I tried cleaning/re-enhancing the datamodel.

Object isnt saved for Unowned Many to Many relation

I have an unowned Many to Many relationship setup in JDO by adding the List in both the Persistence Capable objects.
For the sake of explaining my problem lets call these two entities with .
EntityA and EntityB
Now, when i have a new Object of EntityB to be attached to the Object of EntityA, i append that Key to the EntityA object and call makePersistent on it, which saves the object.
I verified that by printing it on the console.
Since, this is a Many to Many relation, i have to do the same on the other end of the relation as well.
So, i fetch all the objects of EntityB which are referred by EntityA using
select from " + clazz.getName()+ " where :keys.contains(key) and passing it the List of Keys which are present in Object of EntityA.
The problem that i encounter is, the objects returned back are Hollow, and hence they dont get saved into the datastore even if i append the EntityA keys to the fetched objects.
I am a newbie in JDO and GAE, and have been facing this problem since yesterday.
Can someone please shed some light on this? I can provide sample code if needed too.
Here is the code
#PersistenceCapable
public class Objective {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private boolean active;
#Persistent
private int corporate;
#Persistent
private String nameOfObjective;
#Persistent
private String shortDescription;
#Persistent
private int status;
#Persistent
private List<Key> scoreCardKeys; //List of Keys of Scorecards.
#PersistenceCapable
public class Scorecard {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private boolean active;
#Persistent
private int corporate; // synonymous to being public
#Persistent
private Date creationDate;
#Persistent
private String nameOfScorecard;
#Persistent
private String shortDescription;
#Persistent
private Key createdUserKey;
#Persistent
private List<Key> objectiveKeys; // List of Keys of Objectives
Objective and Scorecard entities are in an unowned Many to Many relation
Here is the processor method which will update a Scorecard.
public ScoreCardRepresentation updateScoreCard(ScoreCardRepresentation scoreCardRepresentation) {
Scorecard scoreCard = scoreCardTransformer
.transformRtoEForSave(scoreCardRepresentation);
scoreCard.setCreationDate(new Date());
Scorecard updatedScoreCard = scoreCardDAO.saveScoreCard(scoreCard); /* Update the scorecard, this already has the list of Key of Objectives in it, Hence blindly save it. */
/* Update the Key of the scorecard in the Objectives too */
updateRelatedObjectivesToScoreCard(scoreCardRepresentation,updatedScoreCard);
private void updateRelatedObjectivesToScoreCard(
ScoreCardRepresentation scoreCardRepresentation,
Scorecard updatedScoreCard) {
List<String> addedObjectivesIds = scoreCardRepresentation.getAddedObjectiveKeys();
List<String> deletedObjectivesIds = scoreCardRepresentation.getRemovedObjectiveKeys();
// Add ScoreCard to the newly added Objectives
if(addedObjectivesIds != null && addedObjectivesIds.size()>0){
Scorecard sc = scoreCardDAO.findScoreCardById(Scorecard.class, updatedScoreCard.getKey());
List<Key> objKeys = sc.getObjectiveKeys();
List<Objective> objectives = objectiveDAO.findObjectivesByKeys(Objective.class,objKeys);
// This uses the query select from " + clazz.getName()+ " where :keys.contains(key)
for(Objective obj : objectives){
List<Key> scoreCardKeys = obj.getScoreCardKeys();
if(scoreCardKeys != null){
scoreCardKeys.add(sc.getKey());
} else {
scoreCardKeys = new ArrayList<Key>();
scoreCardKeys.add(sc.getKey());
}
obj.setScoreCardKeys(scoreCardKeys);
Objective updatedObjective = objectiveDAO.saveObjective(obj);
System.out.println(new ObjectiveProcessor().viewObjective(KeyFactory.keyToString(obj.getKey())));
}
}
//Remove Scorecard entries from Objective.
if(deletedObjectivesIds != null && deletedObjectivesIds.size()>0){
List<Objective> objectives = objectiveDAO.findObjectivesByIds(Objective.class,deletedObjectivesIds);
for(Objective obj : objectives){
List<Key> scoreCardKeys = obj.getScoreCardKeys();
if(scoreCardKeys != null){
scoreCardKeys.remove(updatedScoreCard.getKey());
}
obj.setScoreCardKeys(scoreCardKeys);
}
}
}
All i have been able to realise is that when i get back the Objectives using **findObjectivesByKeys** i am getting back hollow objects, so i have to call makeTransient on them to enable them for persistence, else they just ignore the makePersistent method call.

Resources