I would like to refresh an entity and all its child collections. What is the best way to do this? I'm talking about nhibernate:)
I've read about session.Evict, session.Refresh...
But I'm still not sure if doing like:
RefreshEntity<T>(T entity)
{
session.Evict(entity);
session.Refresh(entity);
}
would work exactly how I want it to work
Is it going to work? If not What else I can do?
Refresh after Evict probably won't work.
Theoretically, Refresh alone should be enough. However, it has known issues when elements of child collections have been deleted.
Evict followed by Get usually gets things done.
Refresh(parentObject) would be a good option, but for me, it first fetched all children one by one with single requests. No batching, no subquery, no join. Very bad!
It helped to .Clear() the child collection of the parent object; I also evicted the child objects before.
(these had been changed by a HQL update before where multiple inserts by parent/children SaveOrUpdate would cause expensive clustered index rebuilds).
EDIT: I removed the HQL update again, since the query (decrement index by a unique, large number) was more expensive than hundreds of single row updates in a batch. So I ended up in a simple SaveOrUpdate(parentObject), with no need to refresh.
The reason was a child collection with unique constraint on ParentID and Index (sequential number), which would result in uniqueness violations while updating the changed children items. So the index was first incremented by 1000000 (or an arbitrary high number) for all children, then after changes, decremented again.
Related
Imagine I have a Parent.hasMany(Child) relationship, if I have an API to query a Parent but also need to surface how many children this parent has, I have 2 immediate options:
Run a query on COUNT(child.id) (I feel this must be very hard to scale as we add more and more children in for a given Parent
Maybe have a n_count attribute defined on the Parent and do a SQL transaction to modify the count on the parent every time a Child is created/deleted
Which is the better option here, or is there a third and best way?
Storing n_count in the parent is generally considered undesirable because it is redundant information (information which can be obtained more reliably by counting the child records). Having said that, if the updates to the parent and child rows (n_count) is controlled to guarantee correct updates (by database triggers, for example), then this can be called a type of 'controlled de-normalisation', and used for performance improvement (only improves read-queries, updates/inserts will be slower, of course).
I have a two tables in a DataSet that have a Parent-Child relation. The Parent is built from external data that I don't have control over, while the Child is sourced from the database that I am using for my internal data. This means that I can't always be certain that there will be a Parent for my Children. If this happens, I would prefer to filter out the Children from the DataGridView that is consuming the data via a BindingSource; I don't want to remove the rows from the database because settings changes (or other events I have no control over) might reintroduce rows that were previously present and removed.
I had previously had to figure out how to go the opposite way, to create Children for previously unencountered Parents via:
dim newrows() as ParentRow = ParentTable.Select("Count(Child(MyRelation).ForeignKeyColumn) < 1")
For Each row as ParentRow in newrows
ChildRow.AddChildRow(row, otherData)
Next
I thought I could use a similar approach:
BindingSource.Filter = "PARENT(MyRelation).KeyColumn IS NOT NULL"
But this filters out everything. Investigating, I discovered that running
ChildTable.Select("PARENT(MyRelation).KeyColumn IS NULL")(0).GetParentRow("MyRelation").Item("KeyColumn")
on a table where the given result has a parent succeeds and gives a value, which seems to contradict the Select statement, so clearly something unexpected is happening with the Select statement.
I am at a loss. Is there any way to filter out (but retain in the backend) rows that don't have a parent via the BindingSource? What am I not seeing here?
Answer
It turns out that making a Boolean column with an expression that calculates Parent.Key IS NOT NULL works and is filterable by the BindingSource, without throwing a flag for the row having been changed.
Possible Explanation
Combined with the observation that the original method only fails when constraints are turned off, this makes me think that this might be a design decision by Microsoft in light of the fact that, when constraints are off, there is no guarantee that a Child will only have one Parent. So the filtering by a Parent column will fail when the constraints are off, but filtering by a column calculated by the Parent column doesn't care about the constraints and so is okay. I just have to do my own work to make sure that there is only one Parent; fortunately this is guaranteed by the data source I am generating the Parent from.
I have an entity which contains several sets/bags.
is there any recommendation from NHibernate on how to delete it?
of course I can do foreach on each list and delete each child, but that will create many delete statements. is it better to create HQL for each child table, or maybe other approach?
I've also seen on another thread to use IStatelessSession. is it wise here?
Personally I think HQL works well in this instance.
Or if your stomach can take it use cascading deletion at the database level then delete the parent and the children precede automatically.
If I have a parent and a child table filled with data, is it trivial to add a new table between them?
For example, before introduction the relationship is:
Parent -> Child
Then:
Parent -> New Table -> Child
In this case I'm referring to SQLite3 so a Child in this schema has a Foreign Key which matches the Primary Key of the Parent Table.
Thanks!
This may be too obvious, but...
How trivial it is will be dependent on how much code has already been written that needs to change with this. If this is a new app, with little code written, and you're just beginning to work on the design, then yes it's trivial. If you have tons of functions (whether it's external code, or DB code like stored procedures and views) accessing these tables expecting the original relationship then it becomes less trivial.
Changing it in the database should be relatively non-trivial, assuming you know enough SQL to populate the new table and set up the relations.
As with all development challenges, you just need to look at what will be affected, how, and determine how you're going to account for those changes.
All of this is a really long-winded way of saying "it depends on your situaiton".
I am not disagreeing with David at all, just being precise re a couple of aspects of the change.
If you have implemented reasonable standards, then the only code affected with be code that addresses the changed columns in Child (not New_Table). If you have not, then an unknown amount of code, which should not need to change, will have to change.
The second consideration is the quality of the Primary Key in Child. If you have Natural Relational Keys, the addition of New_Table has less impact, not data changes required. If you have IDENTITY type keys, then you may need to reload, or worse, "re-factor" the keys.
Last, introducing New_Table is a correction to a Normalisation error, which is a good thing. Consequentially, certain Child.columns will become New_Table.columns, and New_Table can be loaded from the existing data. You need to do that correctly and completely, in order to realise the performance gain from the correction. That may mean changing a couple more code segments.
If you have ANSI SQL, all the tasks are fairly straight-forward and easy.
I'm trying to figure out why NHibernate handles one-to-many cascading (using cascade=all-delete-orphan) the way it does. I ran into the same issue as this guy:
Forcing NHibernate to cascade delete before inserts
As far as I can tell NHibernate always performs inserts first, then updates, then deletes. There may be a very good reason for this, but I can't for the life of me figure out what that reason is. I'm hoping that a better understanding of this will help me come up with a solution that I don't hate :)
Are there any good theories on this behavior? In what scenario would deleting orphans first not work? Do all ORMs work this way?
EDIT: After saying there is no reason, here is a reason.
Lets say you have the following scenario:
public class Dog {
public DogLeg StrongestLeg {get;set;}
public IList<DogLeg> Legs {get;set;
}
If you were to delete first, and lets say you delete all of Dog.Legs, then you may delete the StrongestLeg which would cause a reference violation. Hence you cannot DELETE before you UPDATE.
Lets say you add a new leg, and that new leg is also the StrongestLeg. Then you must INSERT before you UPDATE so that the Leg has an Id that can be inserted into Dog.StrongestLegId.
So you must INSERT, UPDATE, then DELETE.
Also as nHibernate is based on Hibernate, I had a look into Hibernate and found several people talking about the same issue.
Support one-to-many list associations with constraints on both (owner_id, position) and (child_id)
Non lazy loaded List updates done in wrong order, cause exception
wrong insert/delete order when updating record-set
Why does Hibernate perform Inserts before Deletes?
Unidirection OneToMany causes duplicate key entry violation when removing from list
And here is the best answer from them:
Gail Badner added a comment - 21/Feb/08 2:30 PM: The problem arises when a new
association entity with a generated ID
is added to the collection. The first
step, when merging an entity
containing this collection, is to
cascade save the new association
entity. The cascade must occur before
other changes to the collection.
Because the unique key for this new
association entity is the same as an
entity that is already persisted, a
ConstraintViolationException is
thrown. This is expected behavior.