I am trying to move my Core Data object graph to Realm.
Currently I have an Entity called DBNode, which has
#NSManaged var children: NSSet
#NSManaged var parentNode: DBNode
where I can store the parent node and all the children of the node.
When I have a Realm object called RLMNode: RLMObject with
dynamic var children = RLMArray(objectClassName: RLMNode.className())
dynamic var parent = RLMNode()
it crashes when first trying to add an object.
Can I do this hierarchical structure in Realm?
Edit:
It seems I can do this, and just have one node in the array:
dynamic var parent = RLMArray(objectClassName:RLMNode.className())
Would this be the recommended way? Is it as quick as the object graph in Core Data?
The reason for the crash is probably that initialisation becomes recursive, when you create a node it creates a node for its parent, which in turn needs a node etc. You can check the stack trace to see if that is the case.
Realm in Swift supports optional object properties, and they are set to nil by default, so you can do something like this:
class DBNode: RLMObject {
dynamic var name = ""
dynamic var parent: DBNode?
dynamic var children = RLMArray(objectClassName: DBNode.className())
}
Arrays can in fact not be nil, and they do have to be initialised, but they can be empty.
Beware that you might get an exception thrown if you add both an object and its parent (or children) to the Realm explicitly.s They will be added automatically since you cannot link to objects that are not persisted.
Related
I am migrating from google datastore api to objectify(i have used datastore at compute engine and migrating to 6.0a1 objectify). In previous version i used this code to query users:
KeyFactory userKeyFactory = datastore.newKeyFactory()
.addAncestors(PathElement.of("UserList", "default"))
.setKind(USER_KIND);
//...save entity
Query<Entity> query = Query.newEntityQueryBuilder()
.setKind(USER_KIND)
.setFilter(PropertyFilter.hasAncestor(datastore.newKeyFactory().setKind("UserList").newKey("default")))
.build();
QueryResults<Entity> queryResults = datastore.run(query);
List<User> result = new ArrayList<>();
queryResults.forEachRemaining(entity -> result.add(transformUser(entity)));
return result;
Now i am trying to make same query with ancestor. However objectify don't work with PathElement. So following code:
ofy().load().type(User.class).ancestor(PathElement.of("UserList", "default")).list()
fails with
java.lang.IllegalArgumentException: No class 'com.google.cloud.datastore.PathElement' was registered
at com.googlecode.objectify.impl.Registrar.getMetadataSafe(Registrar.java:115)
at com.googlecode.objectify.impl.Keys.getMetadataSafe(Keys.java:56)
at com.googlecode.objectify.impl.Keys.getMetadataSafe(Keys.java:65)
at com.googlecode.objectify.impl.Keys.rawKeyOf(Keys.java:47)
at com.googlecode.objectify.impl.Keys.anythingToRawKey(Keys.java:117)
at com.googlecode.objectify.impl.QueryImpl.setAncestor(QueryImpl.java:203)
at com.googlecode.objectify.impl.SimpleQueryImpl.ancestor(SimpleQueryImpl.java:69)
at com.googlecode.objectify.impl.LoadTypeImpl.ancestor(LoadTypeImpl.java:23)
What is proper way to use PathElement with objectify? I see i can create com.googlecode.objectify.Key and pass it as ancestor but it require class, but i don't have UserList class (it is small application, all entities related to single group).
I tried to use this code:
ofy().load().type(User.class).ancestor(datastore.newKeyFactory().setKind("UserList").newKey("default")).list()
Now it fails that User don't have field with #Parent annotation. Here is stacktrace:
com.googlecode.objectify.LoadException: Error loading Key{projectId=projectId, namespace=, path=[PathElement{kind=UserList, id=null, name=default}, PathElement{kind=User, id=1, name=null}]}: Loaded Entity has parent but com.package.model.User has no #Parent
at com.googlecode.objectify.impl.EntityMetadata.load(EntityMetadata.java:84)
at com.googlecode.objectify.impl.LoadEngine.load(LoadEngine.java:187)
at com.googlecode.objectify.impl.LoadEngine$1.nowUncached(LoadEngine.java:145)
at com.googlecode.objectify.impl.LoadEngine$1.nowUncached(LoadEngine.java:131)
at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
at com.googlecode.objectify.impl.Round$1.nowUncached(Round.java:66)
at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
at com.googlecode.objectify.impl.HybridQueryResults.lambda$load$1(HybridQueryResults.java:87)
at com.google.common.collect.Iterators$5.transform(Iterators.java:757)
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:48)
at com.google.common.collect.MultitransformedIterator.next(MultitransformedIterator.java:66)
at com.google.common.collect.Iterators$4.computeNext(Iterators.java:623)
at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:145)
at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:140)
at com.googlecode.objectify.impl.HybridQueryResults.hasNext(HybridQueryResults.java:92)
at com.googlecode.objectify.util.IteratorFirstResult.nowUncached(IteratorFirstResult.java:31)
at com.googlecode.objectify.util.ResultCache.now(ResultCache.java:30)
at com.googlecode.objectify.LoadResult.now(LoadResult.java:25)
at com.package.UserObjectifyRepository.getUserByEmail(UserObjectifyRepository.java:43)
...
Caused by: java.lang.IllegalStateException: Loaded Entity has parent but com.package.model.User has no #Parent
at com.googlecode.objectify.impl.KeyMetadata.setKey(KeyMetadata.java:142)
at com.googlecode.objectify.impl.KeyMetadata.setKey(KeyMetadata.java:122)
at com.googlecode.objectify.impl.KeyPopulator.load(KeyPopulator.java:24)
at com.googlecode.objectify.impl.translate.ClassPopulator.load(ClassPopulator.java:118)
at com.googlecode.objectify.impl.translate.ClassTranslator.loadSafe(ClassTranslator.java:109)
at com.googlecode.objectify.impl.translate.NullSafeTranslator.load(NullSafeTranslator.java:21)
at com.googlecode.objectify.impl.EntityMetadata.load(EntityMetadata.java:80)
.. 125 more
I suppose correct way to fix this is to have parent field in my User entity, something like this:
#Parent
private UserList userList;
But i haven't such entity "UserList", i need ancestor just to make query with strong consistency.
UPD: Error is gone if i adding this code:
import com.google.cloud.datastore.Key;
#Parent
private Key userList;
Is it proper way to make consistent query?
The #Parent annotation requires a key.
You just have to create a key, so it can understand that Parent/Child relationship.
I have an ASP.NET WebApi OData service, and I am trying to consume it from JavaScript using JayData.
My model is as follows:
class Parent {
ICollection<Child> Children;
}
class Child {
ICollection<Parent> Parents;
}
I'm trying to add a new parent entity with a new child entity using JayData context, something like:
var child = new db.Children.Child({Parents: []});
var parent = new db.Parents.Parent({Children: [child]});
db.Parents.add(parent);
db.saveChanges();
As a result, JayData sends a batch request to the OData service containing 2 post requests: one for the child and another for the parent, where the child is given Content-ID = 1,
and the parent entity is serialized to JSON as:
{'Children': [{'__metadata': {'uri': '$1'}}]}
On the server side, the CreateEntity method of both Parent and Child EntitySetControllers gets executed, but the Parent argument has an empty collection of Children, i.e. it ignores the Content-ID reference to the new Child entity from the request.
Is there a convenient way to make it work in ASP.NET OData, or do I have to parse the Parent's JSON from the request and check whether such references exist by hand?
I have a NavigatorContent which is displayed when the user selects an item in a DataGrid.
This NavigatorContent contains a form and an accordion displaying the related objects.
When the user presses the Save button in the NavigatorContent the form and the children should be saved to the database by calling the server through BlazeDS:
saveObjectToDB()
{
//Map the form values to the object
object.field1 = object_field1.text;
object.field2 = object_field2.selectedDate as Date;
object.relatedobject3 = comboBox.selectedItem as RelatedObject3;
//etc.....
//Loop through accordion to save the child objects
for(var i:int= 0; i < accordion.numChildren; i++ )
{
if(accordion.getChild(i) is RelatedObject1Form)
{
var formRelated1:RelatedObject1Form = accordion.getChild(i) as RelatedObject1Form;
//Map the form values to the related object
object.relatedobject1.field1 = formRelated1.relatedobject1_field1.selectedDate;
//etc...
}
if(accordion.getChild(i) is RelatedObject2Grid)
{
var gridRelated2:RelatedObject2Grid = accordion.getChild(i) as RelatedObject2Grid;
//Get dataProvider for the datagrid of the relatedObject
object.relatedobject2 = gridRelated2.object.relatedobject2;
}
}
// Call the remoting object's saveObject method
var saveObjectOperation:Operation = new Operation();
saveObjectOperation.name = "saveObject";
saveObjectOperation.arguments=[object];
ro.operations = [saveObjectOperation];
saveObjectOperation.send();
if(isNewObject)
//dispatchEvent new object
else
//dispatchEvent object updated
}
My problem is as the question states that my application freezes for a few seconds when the user presses the save button that calls this method. I guess that is because Flex is single threaded, but still i dont quite get why this method would be so computational expensive? It doesnt seem to matter if i comment out the loop that loops over the accordion children.
I tried setting the objects related objects to null before calling the remote save method, and this seemed to speed up the save method, but it provided me with some troubles later.
My conclusion is that the remote call is whats freezing up the application, and if i set the related objects to null this seems to fix the issue. But is this really necessary? The related objects aren't really that big, so i don't quite get why the remote call should freeze the application for a few seconds.
This is how i create the accordion children when the NavaigatorContent is intialized:
var relatedObjectForm:RelatedObject1Form= new RelatedObject1Form();
accordion.addChild(relatedObjectForm);
relatedObjectForm.object= object;
relatedObjectForm.ro = this.ro;
The object that i pass to the accordion children is public and [Bindable] in the NavigatorContent and in the accordion children and is initially passed from the main DataGrid. May this be a problem relating to this issue?
Any help/comments is much appreciated. This issue is starting to affect my beauty sleep ;)
My guess would be that you're spending a lot of time in the serializer. Put a trace target in the app and watch the console when it runs to see what's being sent.
The most likely problems are from DisplayObjects - if they've been added to the application they will have a reference to the application itself, and will cause some serializers to start serializing the entire app. The bindable object might have some odd events attached that eventually attach to DisplayObjects - try copying the relevant values in it into your object instead of just taking a reference to the existing object.
I need to add multiple child objects to an existing parent Object. I am instantiating my parent object and sets it Key/Id in my UI processing layer(to which my child objects will be added).
Parent parenttoModify = new Parent();
parenttoModify.Parent_Id = 5; //this comes from some Index of a dropdown or a key column of a grid, i Have put a dummy value here for example
parenttoModify.Children.Add(child);
parenttoModify.Children.Add(child2);
DataAccess.ModifyParent(parenttoModify);
In my data access layer I have a method like :
public static bool ModifyParent(Parent parent)
{
int recordsAffected=0;
using (TestEntities testContext = new TestEntities())
{
testContext.Parents.Attach(parent);
var parentEntry = testContext.ObjectStateManager.GetObjectStateEntry(parent);
parentEntry.ChangeState(System.Data.EntityState.Modified);
recordsAffected = testContext.SaveChanges();
}
return recordsAffected > 0 ? true : false;
}
I get an error when testContext.Parent.Attach(parent) is called. It says:
Object with same key already exist.
I am not sure why is this happening since i am not adding a parent object, I am just attaching it and adding child objects within it.
Any idea where I am going wrong?
Where do you add childs? I guess you are not showing all code. When you call Attach or AddObject EF always attaches or adds all entities from object graph which are not known (tracked) to context at the moment. The exception says that some entity - probably parent - is already tracked by the context. So you have either:
Used shared context (you are creating a new instance in ModifyParent so it should not be a case)
Load parent from the context first in ModifyParent
Called Attach or AddObject on any child before attaching parent.
All these operations lead to the exception you are receiving.
Does anyone have any recommendations for working with name/value pairs in Flex?
Context:
I want to create a Flex UI for updating a database table. I want the UI to contain the field name(read only) and current field value (which will be editable). Im using the WebService component to retrieve the field value and then asssigning it to an object with the field name hardcoded e.g.
private function resultHandler(event:ResultEvent):Object
{
var resultsObj:Object;
resultsObj = {
name:event.result.name as String,
This approach however is adding the dependency that the table structure/field names will never change. Using the object type also requries that i write my own algorithm to sort the output.
I'm not sure I understand the issue.
If you want to avoid dependency of the properties returned from the event at this point, simply use the event.result object, which is already an associative array.
As for sorting, we would need more context on what you are attempting to sort.
That's pretty simple. Don't use for each in loop. Use For-In Loop
Demo is shown below.
var dicEntry:Object = new Object();
dicEntry["Name"] = "Raj";
dicEntry["sal"] = 10000;
dicEntry["age"] = 33;
for(var key:Object in dicEntry)
{
trace("Object Key: "+key+" Object Value: +dicEntry[key]);
}
That's it.