JDO not fetching collection member field - java

Have a class:
class Node implements Serializable
{
private String name;
public String getName { return name; }
public void setName(String val){ name = val; }
public Node(){}
}
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable="true")
class NodeBag implements Serializable
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
Long id;
#Persistent(serialized="true")
private ArrayList<Node> nodes = new ArrayList<Node>();
public String getNodes { return nodes; }
public void setNodes(ArrayList<Node> val){ nodes = val; }
public NodeBag(){}
}
I can save it to the db with this
PersistenceManager pm = PMF.getManager();
try
{
pm.makePersistent(newBag);
}
finally
{
pm.close();
}
But when i load it back
PersistenceManager pm = PMF.getManager();
Query q = pm.newQuery(NodeBag.class);
try
{
List<NodeBag> pipelines = (List<NodeBag>)q.execute();
return nodeBags; // nodeBags[0].nodes is always empty
}
finally
{
q.closeAll();
}
Nodebag.nodes is always empty!
Did i miss something?
Thanks in advance.
Regards,
Paul

In your call to return the objects you can use the FetchPlan to specify what FetchGroup to return. See JDO docs for more information on the FetchGroup options.
You can ensure that all the entities are fetched, by specifying in your PersistenceManager the FetchGroup to use. The modified code is shown below:
PersistenceManager pm = PMF.getManager();
pm.getFetchPlan().setGroup(FetchGroup.ALL);
Query q = pm.newQuery(NodeBag.class);
try {
List<NodeBag> pipelines = (List<NodeBag>)q.execute();
return nodeBags; // nodeBags[0].nodes is always empty
} finally {
q.closeAll();
}

I had a heck of a time getting fetch groups to work. Both Query and PersistenceManager have a getFetchPlan(), but only the one on PersistenceManager seems to work.
Also, make sure you make your objects detachable and use pm.detachCopyAll() on the result.

Missed putting it in the fetch plan ? mark in default fetch group perhaps, or access the field, or put in a custom fetch plan, as per the DataNucleus docs and JDO spec.

Actually, i wanted also the return the answer across the wire by converting it to JSON.
And i've managed to load the child objects. The trick i use is detach. By detaching, everything will be loaded.
Thanks.

Use Collection insted of List and it should start working as long as it is inside a transaction

Related

Unable to change or delete relationship between nodes with Neo4j OGM and Spring Boot Data

I’m having problems with removing or changing existing relationships between two nodes using Spring Boot (v1.5.10) and Neo4j OGM (v2.1.6, with Spring Data Neo4j v4.2.10). I have found a few traces of similar problems reported by people using older Neo4j OGM versions (like 1.x.something) but, I think, it should be long gone with 2.1.6 and latest Spring Boot v1 release. Therefore, I don’t know whether that’s a regression or I am not using the API in the correct way.
So, my node entities are defined as follows:
#NodeEntity
public class Task {
#GraphId
private Long id;
private String key;
#Relationship(type = "HAS_STATUS")
private Status status;
public Task() {
}
public Long getId() {
return id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
#NodeEntity
public class Status {
#GraphId
private Long id;
private String key;
#Relationship(type = "HAS_STATUS", direction = Relationship.INCOMING)
private Set<Task> tasks;
public Status() {
tasks = new HashSet<>();
}
public Long getId() {
return id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Set<Task> getTasks() {
return tasks;
}
public void addTask(Task task) {
tasks.add(task);
}
public boolean removeTask(Task task) {
if(this.hasTask(task)) {
return this.tasks.remove(task);
}
return false;
}
public boolean hasTask(Task task) {
return this.tasks.contains(task);
}
}
This is how it can be represented in Cypher-like style:
(t:Task)-[:HAS_STATUS]->(s:Status)
Here is the Service method that tries to update the task’s statuses:
public void updateTaskStatus(Task task, Status status) {
Status prevStatus = task.getStatus();
if(prevStatus != null) {
prevStatus.removeTask(task);
this.saveStatus(prevStatus);
}
task.setStatus(status);
if(status != null) {
status.addTask(task);
this.saveStatus(status);
}
this.saveTask(task);
}
As a result of an update, I get two HAS_STATUS relationships to two different Status nodes (old and new one), or, if I try to remove existing relationship, nothing happens (the old relationship remains)
The complete demo that illustrates the problem can be found on the GitHub here:
https://github.com/ADi3ek/neo4j-spring-boot-demo
Any clues or suggestions that can help me resolve that issue are more than welcome! :-)
If you would annotate your commands with #Transactional (because this is where the entities got loaded) it will work.
The underlying problem is that if you load an entity it will open a new transaction with a new session (context), find the relationships and cache the information about them in the context. The transaction (and session) will then get closed because the operation is done.
The subsequent save/update does not find an opened transaction and will then, as a consequence, open a new one (with new session/ session context). When executing the save it looks at the entity in the current state and does not see the old relationship anymore.
Two answers:
it is a bug ;(
EDIT: After a few days thinking about this, I revert the statement above. It is not a real bug but more like unexpected behaviour. There is nothing wrong in SDN. It uses two sessions (one for each operation) to do the work and since nobody told it to do the work in one transaction the loaded object is not 'managed' or 'attached' (as in JPA) to a session context.
you can work around this by using an explicit transaction for your unit of work
I will close the issue for SDN and try to migrate all the information to one of the two issues on GitHub because it is a OGM problem.

DynamoDBMapper: How to get saved item?

For a simple Java REST-API I created a save function to persist my model to a DynamoDB table.
The model uses a auto generated range key as you can see here:
#DynamoDBTable(tableName = "Events")
public class EventModel {
private int country;
private String id;
// ...
#DynamoDBHashKey
public int getCountry() {
return country;
}
public void setCountry(int country) {
this.country = country;
}
#DynamoDBRangeKey
#DynamoDBAutoGeneratedKey
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
//...
}
Unfortunately the the DynamoDBMappers .save() method does not return anything. I want to return the created item to set the proper location header in my 201 HTTP response.
public EventModel create(EventModel event) {
mapper.save(event);
return null;
}
How can I make that work? Any suggestions? Of course I could generate the id on the client but I don´t want to do this because solving the potential atomicity issue needs additional logic on client- and server-side.
I´m using the aws-java-sdk-dynamodb in version 1.11.86.
Never mind, I figured out how to do it. The .save() method updates the reference of the object. After calling mapper.save(event); the id property is populated and has its value.
So the way to get it work is just:
public EventModel create(EventModel event) {
mapper.save(event);
return event;
}
That´s it!
There is direct way through dynamo db mapper to get what is saved in dynamodb after put/update Approach mentioned by m4xy# would work if you are saving with DynamoDBConfig as CLOBBER or UPDATE. If you are using UPDATE_SKIP_NULL_ATTRIBUTES, this approach won't work.
If you are using mapper, you have to specifically call db again to get existing value (which might have been updated if there are multiple writers and you might get unxpected result). To ensure read that you expect you can implement locking for write such that if lock is acquired by a given thread, no other thread can write for a given key. But, this approach as a downside of slowing down your application.
Alternatively, you can use dynamoDBClient that has apis to support return db values after write.
https://sdk.amazonaws.com/java/api/2.0.0-preview-11/index.html?software/amazon/awssdk/services/dynamodb/DynamoDbClient.html

RelationshipEntity not persisted

I'm having problems with relation
#RelationshipEntity(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION)
public class TagOnObjectEvaluation
{
#StartNode
private Mashup taggableObject;
#EndNode
private Tag tag;
// Other fields, getters and setters
}
In both the entities involved (Mashup and Tag), I have this field (with opposite Direction)
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION,
direction = Direction.INCOMING /*Direction.OUTGOING*/)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations =
new HashSet<TagOnObjectEvaluation>();
Then, I have various service class to manage Tag, Mashup and TagOnObjectEvaluation. The class under test now is the latter.
Note: the name is a bit confusing and it's a legacy from the previous coder, you can read DAO as Service. Also GenericNeo4jDAOImpl (again, read it as GenericServiceNeo4jImpl) simply defines standard methods for entities management (create(), find(), update(), delete(), fetch() )
#Service
public class TagOnObjectEvaluationDAONeo4jImpl extends
GenericNeo4jDAOImpl<TagOnObjectEvaluation> implements
TagOnObjectEvaluationDAO
{
#Autowired
private TagOnObjectEvaluationRepository repository;
public TagOnObjectEvaluationDAONeo4jImpl()
{
super(TagOnObjectEvaluation.class);
}
public TagOnObjectEvaluationDAONeo4jImpl(
Class<? extends TagOnObjectEvaluation> entityClass)
{
super(entityClass);
}
#Override
public TagOnObjectEvaluation create(TagOnObjectEvaluation t)
{
Transaction tx = template.getGraphDatabaseService().beginTx();
TagOnObjectEvaluation savedT = null;
try
{
// This is to enforce the uniqueness of the relationship. I know it can fail in many ways, but this is not a problem ATM
savedT =
template.getRelationshipBetween(
t.getTaggableObject(), t.getTag(),
TagOnObjectEvaluation.class,
RelTypes.Tag.TAG_ON_OBJECT_EVALUATION);
if (savedT == null)
savedT = super.create(t);
tx.success();
}
catch (Exception e)
{
tx.failure();
savedT = null;
}
finally
{
tx.finish();
}
return savedT;
}
}
It seems pretty straightforward until now.
But when I'm trying to persist a RelationshipEntity instance, I have many problems.
#Test
public void testRelationshipEntityWasPersisted()
{
TagOnObjectEvaluation tagOnObjectEvaluation = new TagOnObjectEvaluation(taggedObject, tag);
tagOnObjectEvaluationDao.create(tagOnObjectEvaluation);
assertNotNull(tagOnObjectEvaluation.getId());
LOGGER.info("TagOnObjectEvaluation id = " + tagOnObjectEvaluation.getId());
tagDao.fetch(tag);
assertEquals(1, tag.getTaggedObjectsEvaluations().size());
}
The last test fail: the size is 0 and not 1. Also, although it seems that the entity is correctly stored (it gets an id assigned), if I'm navigating the db later on there is no track of it at all.
I've also tried to add the relationship in a different way, using the sets of the involved nodes; f.e.
tag.getTaggedObjectsEvaluations().add(tagOnObjectEvaluation);
tagDao.update(tag);
but with no improvements at all.
You need to change the direction of the relationship in your entity Mashape, (entity corresponding to the #StartNode of your #RelationshipEntity TagOnObjectEvaluation).
#NodeEntity
class Mashape {
// ...
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION, direction = Direction.OUTGOING)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations = new HashSet<TagOnObjectEvaluation>();
}
Just point that according to the specifications of #RelatedToVia spring-data-neo4j annotation, the direction by default is OUTGOING, so you really don't need to specify the direction in this case. This also should be correct:
#RelatedToVia(type = RelTypes.Tag.TAG_ON_OBJECT_EVALUATION)
private Set<TagOnObjectEvaluation> tagOnObjectEvaluations = new HashSet<TagOnObjectEvaluation>();
Hope it helps.

Object not storing in Google App Engine datastore

I'm having a problem storing an object in the datastore. I have an object, MyObject, that I'm trying to store but when the code is executed nothing happens. I go to look at the datastore dashboard and MyObject isn't there. No exceptions are thrown and there are no errors.
Here's my object
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class MyObject{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
String name;
#Persistent
String beta;
#Persistent
double loc;
public MyObject(String name1){
name = name1;
}
//getters and setters
}
and here's the code to store the object
public static void saveMyObject(MyObject a)throws Exception{
PersistenceManager pm = PMF.get().getPersistenceManager();
try{
pm.makePersistent(a);
}
catch(Exception e){
throw e;
}
finally{
pm.close();
}
}
Can anyone see what I'm missing?
It looks like you are using JDO, so you might want either to add a JDO tag or else mention that somewhere....
I would replace
try
{
pm.makePersistent(a);
}
with
try
{
MyObject myObj = new MyObject(a.getName()); // or whatever the getter is
myObj.setField2(a.getField2()); // Copy 1 data member from a
... // Make a MyObject.copy(...) method?
pm.makePersistent(myObj);
}
The key thing is that JDO uses enhancement: magic bytecode that is inserted after main Java compilation. I manipulate my persistent entity objects within the lifecycle ("scope") of enhancement to get JDO to work.
I also use transactions for writing (I don't know your JDO auto-transaction setting(s)). I always use transactions when creating and persisting a new persistent entity. You might want to try that if the change above does not work.

Loading a collection of Enums with Google app engine datastore

I am using the Goole app engine datastore with Java and trying to load an Object with a List of Enums. Every time I load the object the List is null. The object is
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ObjectToSave {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
#Persistent
private List<AnEnum> anEnumList;
//public getters and setters
}
The enum is simple
public enum AnEnum {
VALUE_1,
VALUE_2;
}
The code to save it is
ObjectToSave objectToSave = new ObjectToSave();
List<AnEnum> anEnumList = new ArrayList<AnEnum>();
anEnumList.add(AnEnum.VALUE_1);
objectToSave.setAnEnumList(anEnumList);
PersistenceManager pm = pmfInstance.getPersistenceManager();
try {
pm.makePersistent(objectToSave);
} finally {
pm.close();
}
The code to load it is
PersistenceManager pm = pmfInstance.getPersistenceManager();
try {
Key key = KeyFactory.createKey(ObjectToSave.class.getSimpleName(), id);
ObjectToSave objectToSave = pm.getObjectById(ObjectToSave.class, key);
} finally {
pm.close();
}
I can view the data in the datastore using http://localhost:8080/_ah/admin and can see my List has been saved but it is not there when the object is loaded.
I created my project with the Eclipse plugin and haven't made any changes to the datastore settings as far as I know. So why id my Enum list null?
Yes but your List field is not in the default fetch group at loading so hence is not loaded.
Read JDO Fetch Groups. You could add it to the DFG, or enable a custom fetch group, or just "touch" the field before closing the PM.
--Andy (DataNucleus)
How are you creating an instance of ObjectToSave? The default value of all instance variable reference types is null, so unless you have (additional) code to create an instance of List<AnEnum> and assign it to anEnumList, null would be expected.

Categories