I’m happy to announce that as of now the next installment of the Summer of NHibernate screencast series is available for general download from the Main Site.
In this installment we dig into the strategies for modeling object inheritance hierarchies in our database schema using either of NHibernate’s two approaches to this challenge: Table-Per-Class-Hierarchy or Table-Per-SubClass.
We also pull back the covers on NHibernate’s powerful support for polymorphic queries to begin to understand the effects each of these inheritance modeling approaches has on the generated SQL that is ultimately passed to the database.
A few points about the code download for this session
The code download that accompanies this session is actually a two-for-one deal: the single download ZIP file contains two included ZIP files, one each that represents the code for each of the two strategies we investigated in this installment.
Since each of these strategies also required changes to the database schema, each of the two ‘child’ ZIP files contains a DDL scripts (CreateDatabase.sql) in the root of each of the ZIP files that can be used to create the database schema that properly aligns with the code that exercises each of the two inheritance-modeling strategies.
If you try the code download, please be sure that you run the right SQL script to create the right database schema that corresponds to the right set of code that you are running, else you will definitely get errors
Feeback
As always, questions, comments, feedback, (constructive) criticism, and any other thoughts are welcome!
Hi Steve,
just curious to know which pattern(Table-Per-Class-Hierarchy, Table-Per-Subclass) is widely know and i understand that its depend on the requirement and business rule but like to know which pattern is recomeneded? – thanks.
Thanks
@Nisar:
AFAIK, both represent completely viable approaches to the problem but as mentioned in the video both have their pros and cons so without alot of context for such a decision, its not really possible to recommend one pattern over the other.
Unlike using IInterceptor on the session events vs. having your peristent objects implement ILifeCycle where this represents a choice between two competing philosophies (e.g., the relative value of peristence ignorance in one’s persisted classes), either of these two patterns is completely in keeping with the same underlying philosophy of software design so a rule, principle, etc. isn’t going to result in guidance about which to choose in this case.
PERSONALLY, I tend to prefer to start with table-per-subclass b/c it allows me to ‘properly’ model my null constraints in the database and then later if I discover that I have terrible performance in one or more polymorphic query contexts I will consider flipping back to table-per-class-hierarchy. The benefit of NHib in these cases is that my CODE doesn’t need to change AT ALL if/when I want to make such a change.
Hope this helps.
the db i’m working has tooo normalized which means i have so many joins in my SP i can say i have half a dozens of joins (outer,inner..) and the performance is not bad….. anyway i’m thinking the same table-per-subclass because your tbl structure is more clean and very readable and its easy to understand when you look at it.
thanks for reply. and thanks for the video.
looking forward to see the rest 😉
-Nisra
A few things crossed my mind when watching this tutorial:
o When using the table per class hierarchy approach, can you set the properties in the sub-class in the mapping file to not allow nulls, even though they allow nulls in the database? (I guess what I’m asking is whether the not-null attribute on a property tag is describing the database schema, or whether the class allows the property to be null)
o With the two approaches that you demonstrated, the Customer class is a concrete class, hence I was wondering whether you can have abstract base classes or interfaces at the root? and if so, is that described any differently in the mapping files?
o Have you found that the discriminator column in the table per class hierarchy approach can sometimes cause performance issues too? For example, if you have five different types of customers, and lots of records of each, then the selectivity on the discriminator column might result in worse performance than the table per sub-class approach, when you’re only interested in retrieving the data for a single sub-class, as indexes with low selectivity tend to not get used. (perhaps other SQL performance improvement strategies do work in such cases though, e.g. indexed views or partitioned tables)
Hi Steve,
Just wondering – if you go with the table-per-class-hierarchy approach, couldn’t you just define sensible default values for the properties introduced in subclasses that would be set on the superclass entries, so that you would be able to specify those fields as “NOT NULL” if needed?
E.g. on your standard customers, couldn’t you specify “ORderDiscountRate” to be NOT NULL and just set it to a value of 0.0 for the standard customers?
That way, you get the much more efficient SQL for polymorphic queries, you get NOT NULL even on “derived” entities if your business rules require it – why not?
— Marc
How do you persist an update or promotion to PreferredCustomer using table per sub-class when the original Customer already exists within the session and trigger an update to the Customer table and an insert to the PreferredCustomer table? I’m running into this issue in one of our projects because we get an Exception saying “a different object with the same
identifier value was already associated with the session”.
@James:
This is an interesting question and I think it stems from an area where I may not have been all that clear in the video(s) re: the not-null attribute in the mapping elements.
The not-null tag in the mapping file actually has ZERO impact on NHibernate’s behavior — its there for only two reasons: 1) to remind me about how the field is defined in the DB when I’m working with the DTOs and 2) to inform NHib how to generate the schema in the db should I run SchemaExport using these mapping files.
You could certainly prevent the value from being NULL in your property getter/setter or by providing a sensible default value in your classes (and this is REALLY where this business logic belongs in the first place rather than as a default in the DB, IMHO).
I didn’t demo the notion of an Interface or an Abstract Class at the ‘top’ of the inheritance hierarchy tho this is definitely supported; add the abstract=”true” attribute to the class element in the XML to support this implementation and the rest works much the same as demo-ed.
Your point about the discriminator column causing its own perf issues is well-taken and this is one of the main reasons its so damned hard to make recommendation about one strategy for this vs. the other — there are just too many variables to consider as…
1) depth of hierarchy
2) qty of each type in each level of the hierarchy
3) will you be doing much polymorphic querying?
4) are most of your actions inserts or updates?
5) will you mostly be operating on objects at the top, middle, or bottom of the inheritance hierarchy?
6) etc., etc., ett.
The take-away here is: you can change strategies during development without any changes to your data-access-layer, your DTO-consuming code, etc. by just changing your database and your mapping files so experimentation (with real-world scenarios to capture useful benchmarks) is strongly encouraged.
@Marc:
As I understand this suggestion, if my superclass contains the same properties as my subclass, then why would I need a class hierarchy in the first place –?
Aren’t you just describing “some customers have a non-zero discount rate and other customers have a > 0 discount rate”?
This strategy seems to lead me to also providing a ‘CustomerType’ property on my Customer Class that I can/should interrogate the value of in order to know if I’m dealing with a Customer or a PreferredCustomer and that’s how I know what type of Customer I have (e.g, it might actually be the case that a PreferredCustomer ALSO has to have a discount rate of 0.0 so that cannot be my ‘discriminator’).
This sounds like there would be no need to model different objects at all, but just to have a single class in the table with some props that tell ME what ‘logical’ object type I’m dealing with when the purpose of all of this was to have a discriminator that told NHibernate what type of object to retrieve for me.
Am I misunderstanding your suggestion –?
@Corey:
There are a few ways to accomplish this (promotion from Customer to PreferredCustomer) and AFAIK the same challenge will face you if/when you try the same in the table-per-class-hierarchy strategy too (e.g., its not just in joined-sublcass mapped children but also in subclass-mapped children that this issue appears).
It actually turns out that this very topic (in a round-about way) is going to be addressed in the next installment where we talk about evicting entities from the session and then reattaching them. We’ll look @ one way to accomplish this in that installment.
You have to consider that this transformation is viewed by NHibernate as a change from one type of object to another type of object — you see it as a entry in your database, but NHibernate sees it as actually changing an object from one type to another.
If this is something that you see the need to do in your domain model (promote Customers to PreferredCustomers) then the recommended way to accomplish this is offer a method on your Customer class that does exactly this, something with a signature along the lines of…
public PreferredCustomer PromoteToPreferred(Customer c)
…and then in the body of the method instantiate a new PreferredCustomer instance and set all the props on it from the passed-in Customer instance so that the new PreferredCustomer is setup properly and then simply persist this new PreferredCustomer instance and delete the existing Customer instance from the database since its no longer needed.
There is (or should be) no ‘penalty’ for the new PreferredCustomer having a diff PK than the former Customer object since you will be setting all the props on the new object as needed to ensure that FK relations to other objects (tables) in the DB are still enforced.
@Corey:
that method signature should be just…
public PreferredCustomer PromoteToPreferred()
…with no passed in Customer instance since this method is actually *IN* the Customer class and so of course can just operate on its own instance of Customer 🙂
@Steve:
Thanks for the responses.
I agree that the classes should provide handling of nulls, i.e. implement the rules around the properties etc – It’s just nice to have such constraints in the database too, for reasons like defensive programming and revealing intent etc.
I appreciate that the performance topic is certainly not a simple one – I really only mentioned it here, as I got the impression (rightly or wrongly) from the screen-cast that the table per hierarchy approach was being described as the more performant one, which seemed like an over-simplification, as there are probably scenarios when that wouldn’t be the case.
@JAmes:
I agree that I was leaving the impression that table-per-class-hierarchy was the more performant approach (and I still think it *usually* is) but your example definitely points out a very real scenario for which it might very well not be.
I also agree that non-null constraints SHOULD be surfaced in the database (usually in addition to their being modeled in the domiain classes) and this is generally why I tend to prefer the table-per-subclass approach as it tends to feel more natural to me (e.g., most like I would be inclined to model things if I weren’t using NHibernate).
Thanks again for the continued comments~!
Great session Steve demonstrating the two inheritance implementations! I didn’t see any reference to supporting Abstract classes. I have a series of ???Type classes that inherit from an abstract BaseType class, which is simply a data abstraction of the properties in common with all of my ???Type derived classes. I thought I would use the table-per-subclass strategy, but how can I map the abstract class so that nHibernate will not be expecting an underlying table for the BaseType abstract class?
Thanks,
— Ray
@Ray:
I didn’t have time to cover using abstract base classes or interfaces as the root of the inheritance hierarchy in the screencast but read comment #7 above (apply the abstract=”true” attribute to the superclass mapping tag) for a beginning of where to look for anwers to this one.
That did the trick, thanks. I did however end up choosing to use a one-to-one relationship rather than inheritance, purely because I didn’t really like having to evict my entity and then perform the promotion. Luckily the compromise still seems pretty consistent with the domain and doesn’t feel like a compromise. Thanks again for the help.
[…] public links >> exceptions Summer of NHibernate Session 11 Screencast: Techniques for … Saved by jenadamz on Fri 09-1-2009 Built in Exceptions you should use Saved by grumpygrim on Fri […]
[…] by westernsuffolkboces | 6 days ago Web Analytics First saved by smartia | 6 days ago Summer of NHibernate Session 11 Screencast: Techniques for … First saved by TakaSohma1 | 8 days ago Fewer Natural Oils Means Drier Skin First saved by […]
Steve
Thanks once again for taking the time to do these sessions. As great as nhibernate is, there is a learning curve!
I have a question regarding multiple levels of inheritance. I have a FinTransaction abstract class modeling financial transactions.
I have a derived class called Refunds that model relevant Refund info. I have another derived abstract class that models Payments. Payments has subclasses like CCPayment, CashPayment etc
I have started off with table per subclass and can map the FinTransaction & Refund sublass fine. How does one map the Payment abstract class and relevant subclasses?
Class diagram attached here:
http://blackdiamonddev.com/fin.png
Any help greatly appreciated as I am going buts here 🙂
Mapping file attached: