I just ran into a major issue for me with NHibernate. I have 2 objects, each with a collection of things. I need to move one thing from the collection from Object A to the collection of Object B. I get an error about a deleted object because, I believe, NHibernate attempts to delete the thing from the collection of Object A when it needs to keep it for Object B.
From a DB standpoint, it's just a matter of updating the "parent" property to the new object (Object B). But with the collections, I am not really sure what do to...
Is there a well-known procedure to move objects from one collection into another one in NHibernate?
Thanks in advance for any help.
Regards,
Eric.
I'm assuming that you are using Cascade in the mapping for the class represented by objects A and B, i.e., A and B are both instances of some class X, and X's mapping contains a cascade attribute on the collection containing the object to be transferred.
Given that assumption, this may help.
If you want to be able to transfer the object from one collection to the other, you need to consider whether the thing that is being moved should have an existence that is independent of the two collections.
If the transfer object doesn't get saved on its own, you will run into problems when you do the transfer because the transfered object is already known to the session.
There are two ways out that I can think of. The better of the two is probably to treat the transfer object as an independent object which is saved on its own to the db (i.e, doesn't rely on Cascading in the mapping of the objects with the collection). Conceptually, this makes sense because if you can transfer it from one collection to the other that implies that somehow it is independent of the two objects having the collections. It does mean that you could end up with orphans.
If you want to stick with using cascade in the mappings, then you will need to remove from object A in a different transaction than the add to object B. I suspect that isn't what you want to do.
Have you tried mapping using
cascade="all-delete-orphan"
Related
I am writing a small program for a data structures class that basically stores member objects. One of the things the user needs to be able to do is delete and add new users. That being said, I use an arraylist to hold my objects and then I clone it so that I can have two arraylists sorted in different ways. Changing object fields in one list DOES change them in the other but when I delete an object from one arraylist, it still stays in the other. What would be the best method to completely delete that member object from all arraylists?
A good way would be to write a helper method that would remove the element from each of the array lists, and use that method for the removal.
I need to persist up to a maximum of about 100 complex objects (call them Object A). I say complex because each object is composed of other nested heirarchical objects.
I decided against storing them in core data because of their complex object graphs, so I was thinking of using archiving for persisting these objects.
However, I need to form relationships between these objects and other managed objects in core data (call them Object B). The cardinality is one object A (archived) to many object Bs (core data).
My question is, what would be the best way of doing this? I thought of using UUIDs for each archived object A and storing references to those UUIDs as string attributes in core data for Object B.
But I understand there may be performance and storage penalties associated with doing this. Is there another type of object ID for Object A perhaps that I may use?
It seems for all the effort you are going to put into mapping between CoreData and your archived object, it would just be easier to put it all through CoreData. If you have "complex" properties in this object that makes using CoreData undesirable, don't forget that CoreData has a transformable property.
This might be what you need.
I have changed a relationship on an entity to be a different managed object type in a new version of the data model. Rather than convert the old managed object types into the new managed object types is there a way to tell the mapping model between the two versions to delete all objects that were previously in this relationship?
If not, perhaps there is a much easier and simpler way to do this?
I find that if I delete all of the .h/.m files that correspond to the entities and then go back in and ask Xcode to make new ones (File>New>New File>Core Data>NSManagedObject subclass then the new .h/.m files that are created are the updated ones from my model. I am not sure if you want to risk just deleting them all.
I hope I understood your question, and I hope this helps
I'm having trouble understanding how NHibernate knows anything about objects removed from association (and then execute cascading style like delete-orphant). I mean, at a database level if I wanted to remove an association I'd have to physically log on and remove some FK. How does this happen in NH world? Do I remap my classes, remove previously established parent/child association (relationship), NH does comparative analysis, digs that something has been changed and then takes appropriate action? In this post Ayende talks about different cascading styles and delete-orphat is described as "... In addition to that, when an object is removed from the assoication and not assoicated with another object (orphaned), also delete it ..." How does this removal happen?
NHibernate watches all the mapped collections mapped that are owned by objects in the NHibernate session. As you make changes (adding/removing) NHibernate marks them as dirty. When it is time to flush the changes it compares the elements in the dirty collections and is able to identify what items have been added and removed. Depending on the cascade options for the collection NHibernate might then persist those changes to the database.
This is why you should always declare collection properties using interfaces (IList, ISet, etc.) and never replace a collection property on an object that has been loaded using NHibernate.
Additional info requested in comments:
There is a useful discussion by Fabio Maulo (NHibernate lead developer) of collection mapping here which I would strongly recommend to read. But to try and provide a brief answer to your questions:
But how does NH know that association between objects has been removed?
Generally when working in the OO model with many associations we manage relationships at the parent. That is, a child is considered to be associated with the parent when it is in a parent's collection. E.g.
child.Parent = parent;
parent.Children.Add(child); // This is the critical bit
session.Save(parent); // to have an INSERT generated here
Similarly removing an item from a collection breaks the association (assuming correct mapping attributes have used)
child.Parent = null;
parent.Children.Remove(child); // This is critical bit
session.Save(parent); // To have DELETE or UPDATE statement generated depending on cascade settings.
This is the opposite of how things work in the relational world where we manage the relationship at the child via the foreign key on the child row.
For a more detailed understanding there is nothing like downloading the NHibernate source code, creating a simple test case and then stepping through in the debugger.
What's the reason behind the "This is why..."
There are a number of things NHibernate takes care of in managing in association collections. It does this by using its own collection classes that keep track of whether they are dirty, what state they were in when they were loaded from the db and a number of other cool things. If you replace those objects then NHibernate loses that capability. So, for instance if you want to get rid of all the items in a collection you should do:
parent.Children.Clear(); // The collection object is preserved and NHibernate knows you want them all deleted.
You should NEVER do:
parent.Children = new List<X>(); // NHibernate will not track changes to this collection.
For further reading you might also want to take a look at this.
I'm considering using Core Data for an app I'm writing. However, after reading the docs I'm unsure how to model a particular relationship. Here's the basics:
I have an Entity called "ProjectFile" that has some basic string properties. (One of those is a path to a file on disk -- call it "File X" -- that my app is going to manipulate.) However, when the app manipulates file X, it may also need to manipulate OTHER files --- fileY and fileZ.
FileY and FileZ, like fileX, will be "ProjectFile" entities. So I need a way to tell Core Data "FileY and FileZ are associated with FileX." To do that, I created a relationship on the "ProjectFile" entity called "linkedFiles" and set the destination to "ProjectFile" and the inverse to "linkedFiles". I then set this as a "to-many" relationship, as each "ProjectFile" may have multiple linked files.
This seems recursive to me and I'm not sure I've done it correctly. The "linked" files (fileY and fileZ) need to exist on their own, just as fileX does. I need to be able to "delete" them from the "linkedFiles" relationship but still have them exist separately, if that makes sense. Essentially, I just need a weak relationship between separate objects in my model.
Have I done this correctly, or am I missing something? Thanks!
So, you have a data model that looks something like this:
ProjectFile{
path:string
infile<<-->>ProjectFile.infile
}
This will work because (1) Core Data relationships have directionality/cardinality and (2) each object is unique. Where you can get into trouble is with delete rules. You pretty much have to use No Action or Nullify in this circumstance or risk setting off a cascade delete. That in turn runs the risk creating orphaned objects that have no relationships and are hard to find and remove in the object graph.
A better model would encode more information in the relationships themselves. It appears that the real-world file objects you are modeling have two separate relationships to other file objects: (1) Each instance has other instances that it manipulates and (2) each instance has other instances that manipulate it. So, your model should reflect that:
ProjectFile{
path:string
toManipulateFiles<<-(nullify)->>ProjectFile.manipulatedByFiles
manipulatedByFiles<<-(nullify)->>ProjectFile.toManipulateFiles
}
This makes explicit the type relationship between the objects and lets you quickly and easily get the right objects for any particular operation. You can use Nullify on one relationship without orphaning the object on the other.
Although it isn't immediately obvious, relationships aren't just lines on a graphical model, they are actual live objects that can carry a lot of information. You need to design with this in mind.