I want to support cascade update of "truncated graphs" in NHibernate.
Say I have a Student entity and a Class entity which has a Students collection. The Students collection is mapped with "cascade all". Now, suppose that in the client only the a class entity was changed, so I want the client to be able to send only the class entity without the contained students. My approach is to let the client send the class entity with the Students property nullified and let the DAL understand that the Students collection should be ignored.
Unfortunately, when NHibernate gets null property class.Students when given to session.Merge, it disconnects the child students from the parent class by setting their FKs to null and/or deleting them (depending on the specific cascade option).
I would expect NHibernate to behave like that only when getting an empty collection and not when the collection is nullified.
Is there a way to workaround this? E.g. by telling NHibernate somehow to skip the nullified properties during merge?
You can set the inverse = "true" for the Student collection in the Class entity, so that it will not clear the FK.
Related
There is a ManyToOne association defined between entities Pattern and Category (Pattern is an owning side of the relation). Category has many patterns, pattern belongs to one category. So there is a field Pattern.category with #ManyToOne Doctrine annotation.
Now, in my scenario I have the id of the Category entity (posted from form) that I want assign to Pattern.category field of the newly created Pattern (which will be persisted), but I don't want to load this Category entity - I don't need it, I just want to create a Pattern entity, assign it to a Category (which id I have), and persist it. It seems strange to me, that I have to load the Category entity just to establish the connection, when all I really need is just an id, which I already have.
Maybe it smells like using relational database concepts with ORM, but it seems completely pointless to load this entity just to establish connection, when I know id of that target entity.
I am new to Doctrine btw.
You can use Reference Proxy:
$category = $em->getReference('Category', $id);
$pattern->setCategory($category);
I am troubleshooting code that is attempting to update a disconnected entity that has uninitialized references to child entities. The intent is to update only the properties on Parent without loading children.
HasMany(x => x.ChildEntities)
.KeyColumn("ChildEntityId")
.Table("ChildEntity")
.Not.LazyLoad()
.Inverse()
.Cascade.All().AsBag();
When Session.Update(parent) is called, two update statements are executed. The first updates the parent object as expected.
update Parent set ... where ParentId = 12345
The second update confuses me...
update ChildEntity set ParentId = null where ParentId = 12345
Why is NHibernate issuing that second SQL statement? I realize that ChildEntities is uninitialized and that NHibernate is probably trying to enforce the state of Parent but I can't seem to tweak the mapping to not make this second update. I've tried Merge, lazy loading, various cascade options, etc. without success. The only connected entity in the session when it tries to commit is Parent.
Note that I typically approach this by retrieving the entity with lazy loading enabled and then mapping from the disconnected object (DTO or entity) to the connected entity before letting NHibernate persist to the database. I want to understand why the above isn't working before I suggest an alternative approach.
This was annoying.
A quick search through the NHibernate source for "could not delete collection" showed up in a block that could only execute if !isInverse (AbstractCollectionPersister.cs). That drew my attention because the mapping code was explicitly setting Inverse on that collection.
If Inverse is false and the collection is empty, NH executes an update on the child table setting the foreign key to null where the foreign key equals the parent id.
Fluent is configured to auto-map all entities in a given namespace. The assumption was that anything with a manual mapping would be ignored by auto mapping. A quick check of the hbm.xml files produced by Fluent confirmed that Inverse was not being set. I added Parent to the list of entities that were explicitly excluded from Auto Mapping and everything started working.
.IgnoreBase<Parent>()
I've got a many-to-many relationship mapped in my Entity Framework POCO classes. In the database it's a join table with a composite key, and the POCO properties are generated fine.
When I load an entity from the context, I can remove an item from the many-to-many collection and the database is updated when I save changes.
For Example:
var item = context.Items.First();
item.OtherItems.Remove(item.OtherItems[0]);
context.SaveChanges();
However, when the detached object graph comes back from WCF, I attach it to the context and mark it as modified. But the changes are not persisted.
Example:
// this happens on the silverlight client
item.OtherItems.Remove(item.OtherItems[0]);
// and on the server
context.Items.Attach(item);
context.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
context.SaveChanges();
In this case the record is not removed from the join table in the database. Any ideas how I can get this to work? Thanks very much in advance.
Changing object state marks your entity modified. You need to use ChangeRelationshipState to mark modified relation between two entities - this will perform DB modification on your join table. You will need to set relation's state as Added or Deleted.
I'm new to DDD and NHibernate.
In my current project, I have an entity Person, that contains a value object, let's say Address. Today, this is fine. But maybe one day I will have a requirement that my value object (in this case Address), will have to become an entity.
Before trying to model this on a DDD-way, in a more data-centric approach, I had a table Person, with an Id, and another table Address, whose PK was actually an FK, it was the Id of a Person (ie, a one-to-one relationship).
I've been reading that when I map a Value Object as a Component, its value will get mapped as columns on my Entity table (so, I would not have the one-to-one relationship).
My idea was that, when needed, I would simply add a surrogate key to my Address table, and then it becomes an Entity.
How should I design this using NHibernate? Should I already make my Address object an Entity?
Sorry, I don't even know if my questions are clear, I'm really lost here.
In the system we are building, we put Value-Objects in separate tables. As far as I know, NHibernate requires that an id must added to the object, but we ignore this and treat the object as a Value-Object in the system. As you probably know, a Value-Object is an object that you don't need to track, so we simply overlook the id in the object. This makes us freer to model the database the way we want and model the domain model the way we want.
You can Join and make it a Component allowing nHibernate to map it as a proper value object instead of an entity.
This way you won't need any virtual properties nor an empty protected ctor (it can be private).
Join("PROPOSAL_PRODUCT", product =>
{
product.Schema(IsaSchema.PROPOSALOWN);
product.KeyColumn("PROPOSAL_ID");
product.Component(Reveal.Member<Proposal, Product>("_product"), proposalProduct =>
{
proposalProduct.Map...
});
});
I have a parent object which has a one to many relationship with an IList of child objects. What is the best way to delete the child objects? I am not deleting the parent. My parent object contains an IList of child objects. Here is the mapping for the one to many relationship:
<bag name="Tiers" cascade="all">
<key column="mismatch_id_no" />
<one-to-many class="TGR_BL.PromoTier,TGR_BL"/>
</bag>
If I try to remove all objects from the collection using clear(), then call SaveOrUpdate(), I get this exception:
System.Data.SqlClient.SqlException: Cannot insert the value NULL into column
If I try to delete the child objects individually then remove them from the parent, I get an exception:
deleted object would be re-saved by cascade
This is my first time dealing with deleting child objects in NHibernate. What am I doing wrong?
edit: Just to clarify - I'm NOT trying to delete the parent object, just the child objects. I have the relationship set up as a one to many on the parent. Do I also need to create a many-to-one relationship on the child object mapping?
You are getting the first error because, when you remove the items from the collection, NHibernate's default mode of operation is to simply break the association. In the database, NHibernate tries to set the foreign key column on the child row to null. Since you do not allow nulls in that column, SQL Server raises the error. Clearing the collection will not necessarily delete the child object, but one way to do so is to set cascade=all-delete-orphan. This informs NHibernate that it should delete the newly orphaned rows instead of setting the foreign key column.
You are getting the second error because when you call SaveOrUpdate NHibernate first deletes all of the child objects. Then, because neither relationship is marked as inverse, NHibernate also tries to set the foreign key column in your child table to null. Since the rows have already been deleted, you receive the second error. You need to set inverse=true on one side of your relationship to fix this. This is usually done on the one (primary key or parent) side. If you do not do this, NHibernate will make the appropriate updates for each side of the relationship. Unfortunately, running two updates is not the appropriate thing to do.
You should always mark one side of your relationships as the inverse side. Depending on how you code, you may or may not need to use cascading. If you want to take advantage of one shot deletes as you are trying to do using Clear(), you need to define your cascade.
Acording to Chuck's answer, I've resolved my problem by adding Inverse = true in parent side mapping:
Message has many MessageSentTo:
[HasMany(typeof(MessageSentTo), Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
public IList<MessageSentTo> MessageSendTos
{
get { return m_MessageSendTo; }
set { m_MessageSendTo = value; }
}
I am using Castle ActiveRecord. Thank you Chuck.
Try using merge() instead of saveOrUpdate(). Also, make sure your cascade is set to all-delete-orphan and that your parent-child relationship is invertible (inverse=true on the parent and then a field in the child that is the parent-id with not-null=true).
In our example we have categories with many products where a product is not nullable.
You can work around the problem by deleting the product and removing it from the parent's collection before the flush but we're still looking for a better solution to this.
product = pRepo.GetByID(newProduct.ProductID);
product.Category.Products.Remove(product);
pRepo.Delete(product);
Hope it helps anyway
Change cascade attribute value from "all" to "all-delete-orphan".
set Not-Null = true in your mapping on the column causing the issue. I'm not sure of the exact syntax though (sorry).