Object isnt saved for Unowned Many to Many relation - google-app-engine

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.

Related

How to update tables with many-to-many relationship when performing crud operations in Spring Boot

I'm trying to create a Spring Boot backend for my project. In the database I have Deck and Word tables with a many-to-many relationship connected via DeckWord table. The bridge table has additional fields and a composite PK consisting of the other 2 tables' PK's.
I am not sure about how I should structure the crud operations in my project. Say I'm trying to add a new word and it should be assigned to a certain deck. What model's controller should handle the post operation in that scenario: Word or DeckWord? Should the Deck's List<DeckWord> be updated as well?
UPDATE:
Included the models, omitted the getters, setters and constructors
#Entity
#Table(name = "deck")
public class Deck {
#Id
#SequenceGenerator(
name = "deck_sequence",
sequenceName = "deck_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "deck_sequence"
)
#Column(name = "deck_id")
private Long id;
#Transient
private Boolean learnt;
private String name;
#OneToMany(mappedBy = "deck", cascade = CascadeType.ALL)
private List<DeckWord> deckwords;
#ManyToOne
#JoinColumn(name="appuser_id",referencedColumnName="appuser_id")
private Appuser appuser;
}
and
#Entity
#Table(name = "word")
public class Word {
#Id
#SequenceGenerator(
name = "word_sequence",
sequenceName = "word_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "word_sequence"
)
#Column(name = "word_id")
private Long id;
private String definition;
private String transcription;
#OneToMany(mappedBy = "word", cascade = CascadeType.ALL)
private List<DeckWord> deckwords;
}
and the bridge table:
#Embeddable
class DeckWordKey implements Serializable {
#Column(name = "deck_id")
Long deckId;
#Column(name = "word_id")
Long wordId;
}
#Entity
#Table
public class DeckWord {
#EmbeddedId
DeckWordKey id;
#ManyToOne
#MapsId("deckId")
#JoinColumn(name = "deck_id",referencedColumnName="deck_id")
Deck deck;
#ManyToOne
#MapsId("wordId")
#JoinColumn(name = "word_id",referencedColumnName="word_id")
Word word;
private Boolean learnt;
private LocalDate last_checked;
private WordGroup wordGroup;
}
Answering your questions:
What model's controller should handle the post operation in that scenario: Word or DeckWord?
Given that a Word should always be assigned to a Deck, then I would use a POST request to the URL "/decks/{deckId}/words" to create a new Word. The request body should include definition and transcription.
Should the Deck's List be updated as well?
Yes, it must. For that, you need to use deckId that you receive as a path parameter.

JDO 1:N issue (retriving data)

I have class to work on PDF
#PersistenceCapable
public class GoogleDrivePDF {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String pdfName;
... geter, seter...
}
In order to write and read data, I do like this:
GoogleDrivePDF pdf = new GoogleDrivePDF();
key=KeyFactory.createKey(GoogleDrivePDF.class.getSimpleName(),"0B1IQEoiXFg3IWHhJNEtlMzlvQWs");
pdf.setKey(key);
pdf.setPdfName("NAME");
pm.makePersistent(pdf);
and in order to read:
pdf = pm.getObjectById(GoogleDrivePDF.class, "0B1IQEoiXFg3IWHhJNEtlMzlvQWs1");
Everything works!
Now I will create One: Many realationship.
#PersistenceCapable
public class UsersPDFDocuments {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private List<GoogleDrivePDF> pdfs;
... seter, geter...
}
and I write to Database:
List <GoogleDrivePDF> pdfFilesDao=...
GoogleDrivePDF pdf = new GoogleDrivePDF();
Key key= KeyFactory.createKey(GoogleDrivePDF.class.getSimpleName(), "0B1IQEoiXFg3IWHhJNEtlMzlvQWs1");
pdf.setKey(key);
pdf.setVerifyed(false);
pdf.setPdfName("mari123");
pdfFilesDao.add(pdf);
key= KeyFactory.createKey(UsersPDFDocuments.class.getSimpleName(), "mail#mail");
UsersPDFDocuments userData = new UsersPDFDocuments();
userData.setKey(key);
userData.setPdfs(pdfFilesDao);
pm.makePersistent(userData);
OK everything works! BUT I CAN NOT READ PDF DATA NOW!
I have error:
HTTP ERROR 500
Problem accessing /test. Reason:
Could not retrieve entity of kind GoogleDrivePDF with key GoogleDrivePDF("0B1IQEoiXFg3IWHhJNEtlMzlvQWs1")
Caused by:
javax.jdo.JDOObjectNotFoundException: Could not retrieve entity of kind GoogleDrivePDF with key GoogleDrivePDF("0B1IQEoiXFg3IWHhJNEtlMzlvQWs1")
BUT I DO THE SAME , LIKE I WAS DOING:
Key key = KeyFactory.createKey(GoogleDrivePDF.class.getSimpleName(), "0B1IQEoiXFg3IWHhJNEtlMzlvQWs1");
GoogleDrivePDF pdf = pm.getObjectById(GoogleDrivePDF.class, key);
When I was testing, In order to get the value, I need enycrypted String of key. (See image) agtsdHYtY2hlY2tlcnJoCxIRVXNlcnNQREZEb2N1bWVudHMiIXZha2h0YW5nLmtvcm9naGxpc2h2aWxpQGdtYWlsLmNvbQwLEg5Hb29nbGVEcml2ZVBERiIcMEIxSVFFb2lYRmczSVdIaEpORXRsTXpsdlFXcww
when I add Unowned it works! why?!
#Unowned
#Persistent
private List<GoogleDrivePDF> pdfs;

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.

Using DataNucleus' list-ordering extension leads to empty list

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")

Google DataStore Query Set

I have a Course entity which contains a Set of Keys to my Tag entity. How would I go about creating a query to get a list of courses with a specific tag? For example, I want to find all the courses tagged with java.
Here are my entities:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class Course{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent private Set<Key> tags;
//etc
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
public class Tag{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent private String tagText;
}
Tag tag = getTagFromString("java");
Key tagKey = tag.getKey(); // i will assume you have a getKey() method
PersistenceManger pm = PMF.get().getPersistenceManager();
Query q = pm.newQuery(Course.class);
q.setFilter("tags == :tagParam");
List<Course> coursesTaggedWithJava = (List<Course>) q.execute(tagKey);
I don't think this is possible. Google DataStore does not allow to use Join queries. But I may be mistaken.
Here and here is the website where you can find more information about GQL.

Resources