Summer of NHibernate Session 09: Effective Model-Driven Schemas is now available
The next installment of the Summer of NHibernate Screencast series is now available for general download!
In this session we explore the NHibernate SchemaExport class and start to exercise its ability to generate our database schema directly from our hbm.xml mapping files. In addition, we touch briefly on some of the relative advantages of DDD (Domain Driven Design) and start to understand how this aspect of NHibernate can help us avoid having to place the database front-and-center in our development mind as we are refining our Domain Model in our overall software development process.
Some of the highlights from this session include:
- Overview of the SchemaExport class and its role in our development process
- Understanding the somewhat awkward API methods exposed by the SchemaExport class
- Interpreting the somewhat arcane arguments to methods of the SchemaExport class
- Controlling the names of foreign keys in our generated schema
- Understanding the limitations of the DROP capabilities exposed by the SchemaExport class
- ‘Tricking’ the SchemaExport class into creating VIEWs from our mapping files instead of TABLEs
- defining a database INDEX in our mapping files
- applying unique constraints to the database from our mapping files
As usual, comments, suggestions, feedback and constructive criticism are appreciated~! ![]()


i was waiting for the video… i’m still downloaidng
August 18th, 2008 at 2:03 pmthanks Steve
Steve,
i’m wondering what are the choice in term retriving my data after i do SchemaExport (drop the database and recreate it again).?
i’m on the half of screen cast, thought to ask you.
thanks again.
August 18th, 2008 at 3:26 pmSteve,
fyi: you can delete all your tables/views at one shot, just do SHIFT + object and hit the delete
i see that you are deleting every single object one at a time… thought to share with you.
thanks.
August 18th, 2008 at 7:36 pm@Nisar:
Are you suggesting that SHIFT + DEL in sql mgt studio will drop the table even if there are FK dependencies on it? That’s why I was doing that in the order you saw in the screencast (to avoid the “sorry, you cannot drop table xyz because it will violate FK contraints…” message).
Can you advise? If this is true, its certainly news to me (but also a very useful technique).
August 18th, 2008 at 8:32 pmHi steve,
In your previous screen cast, you have a many to many relationship on the OrderProduct Table and you created a mapping file without a class. My question is how do you insert into the mapping table.
thanks
August 18th, 2008 at 9:28 pm@Lookman:
In the case of a m:n mapping table where the intermedial mapping table contains ONLY the pair of foreign key values (as is the case in this very simple model), there is no need to explicitly perform any CRUD operations against the actual ‘mapping’ table. This is one of the ‘geniuses’ of NHibernate — its capable of inferring what you need it to do from the mapping files that describe both ends of the relationship as being m:n.
If you find that you need to store additional info in the intermediate mapping table (OrderProduct in the case of this model) other than just a pair of FK values then you actually DO have to define a class (an ‘entity’) that relates to that entry in the mapping table. In this case, you would define a pair of 1:m relationships to the entry in the mapping table rather than a m:n relationship that simply traverses THROUGH the intermediate table.
An obvious example of this kind of need would be something that I’m actually planning to do in an uncoming session: add a ‘quantity’ field to the OrderProduct table to represent how many of each Product item is in each Order for each Customer. As soon as this change is made, this whole 3-table relation becomes a set of EXPLICIT 1:m relations rather than a m:n relation with IMPLICIT 1:m relations.
Does this help…?
August 18th, 2008 at 9:55 pm@Steve,
i did not realize that other tbls have FK sorry about that …my bad…. but one thing is that if your tbls having no FK then you can delete all at on shot by doing like this: click on the TABLE and on the right hand side you will see a SUMMARY window where you can select multiple objects and delete hit delete button … oh this technique you can apply for views/SP also.
please see my question #2
thanks
August 18th, 2008 at 10:29 pmI do understand this for 1:m relationship,
Parent p = session.Load(typeof(Parent),parentid);
Child c = new Child();
c.parent = p;
p.Children.Add(c);
but in case as an example am adding both order and product at the same time mapping the id to the foreign key in the database mapping table.
you said:there is no need to explicitly perform any CRUD operations against the actual ‘mapping’ table.
at what point does create the mapping and how, i don’t get it.
thanks
August 18th, 2008 at 10:45 pm@Nisar:
No issue; that’s sort of what I thought re: the FK issues in SSMS.
Re: retrieving your data after the schema DROP, there really isn’t a good recommendation in this regard. DROP is (of course) a destructive operation and so running it will lose your data as well as your schema elements (tables, views, etc.).
In the case of a developer working with unit tests (as is the case here), this doesn’t matter since the testdata.xml file contains our persisted test data that the DatabaseUnitTestBase class’ methods will simply reload into the database when it comes time to run the actual unit tests against the DB.
In a real-world (production) situation with data you cared about you would (of course) never do what’s demonstrated here but for the support of the unit testing/development process its fine to just reload the data from the testdata.xml file that’s available in the code downloads for the session(s) since this is the data that the unit tests are ulitmately based upon.
August 18th, 2008 at 10:49 pmSteve,
what is your take on “Database-Driven Modeling” vs “Model-Driven Schemas” which one you preference?
thanks
August 19th, 2008 at 9:35 am@Nisar:
Snipped from an e-mail response I sent just last evening to another person who asked the same quesion…
–snip–
As for myself in this area, I tend to work in one of two ways (depending on the appropriate context):
1) use code-gen to slave the DTO + mapping files to the DB; in this ‘mode’ I use partial classes and/or construct a higher-order domain model to manipulate the DTOs as needed and when I make a DB schema change I completely regen the DTO + mapping files again to keep them in-sync. This tends to work in cases where the DB schema is set by an outside force/external constraints or already exists
2) hand-code the domain model and the mapping files and then use SchemaExport to push that to the DB initially; later I will make mods to both the domain + mapping files AND the db by hand to keep them in-sync (i.e., your suggestion of doing ShemaExport once). Later, if a significant refactor happens to the domain, I will consider re-running SchemaExport against the DB but I will often use a technique that actually ISN’T in the screencast: writing the DDL from SchemaExport to an external file (there is an override for the .Execute(…) method that takes a streamwriter object) and then clip out relevant parts of the captured DDL to paste into SSMS and then run ‘by hand’ after additional tweaking. Imperfect and error-prone (not easy to reproduce) but it works.
–snip–
Hope this helps and thanks for your continued feedback and interest~!
August 19th, 2008 at 12:58 pm@Lookman:
Inspect the Order.hbm.xml and Product.hbm.xml mapping files in the code download. Both of those contain the mapping that supports this; inspect the mapping of the [bag]…[/bag] element in both mapping files (square-brackets used here b/c Wordpress will strip out xml-style angle brackets).
Hope this helps.
-Steve B.
August 19th, 2008 at 8:46 pmHi Steve,
thanks for the great screen casts.
I just want to have some feedback regarding the column check.
I did test the column check using NHibernate 2.0 (the latest release… just yesterday) and guess what it worked just fine and it indeed generated the desirable DDL.
Cost REAL null check( Cost > 0) ,
thanks
August 25th, 2008 at 4:02 pmAli
@Ali:
Thanks for the update; nice to know that this is one of the 1.2 bugs fixed in 2.0~!.
August 25th, 2008 at 7:08 pmHi Steve, thanks for your ongoing great work! Truly enjoy every minute of your screen casts!
Boy, this “work around” for dealing with views is pretty whacked - hasn’t anyone on the NHibernate team every thought of introducing something like a “table-type” property on the tag, with values of “table” or “view” ?? Having to let NHibernate create a “wrong” table, then whacking it out again and having to write a SQL statement (which needs to be kept up and adapted every time I add a new field!) seems more like an awful hack than anything…….
C’mon - views ARE being used quite a bit in the database world - why can’t NHibernate support them in a better fashion? Has anything changed in the 2.0 code base in this regard?
August 31st, 2008 at 3:57 pmHi, Steve
First of all a BIG thanks for these videos, they are wonderful and really informative (I started using some tools after watching you efficiently working in VS2008 !!!). Any ways…
Having tried the SchemaExport class I am facing some problem. The Schemaexport class of mine somehow also creates a table called ProductOrder and fails with the following message.
TestCase ‘M:DataAccessTest.NHibernateDataProviderTest.CreateDatebase’
failed: There is already an object named ‘FK_ProductOrders’ in the database.
Could not create constraint. See previous errors.
NHibernate.HibernateException: There is already an object named ‘FK_ProductOrders’ in the database.
Could not create constraint. See previous errors. —> System.Data.SqlClient.SqlException: There is already an object named ‘FK_ProductOrders’ in the database.
Could not create constraint. See previous errors.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean format, Boolean throwOnError, TextWriter exportOutput, IDbCommand statement, String sql)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop, Boolean format, IDbConnection connection, TextWriter exportOutput)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop, Boolean format)
— End of inner exception stack trace —
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop, Boolean format)
C:\Development\RnD\DineAndDiscussNHibernate_Session9\DataAccessTest\NHibernateDataProviderTest.cs(28,0): at DataAccessTest.NHibernateDataProviderTest.CreateDatebase()
Am I missing something here ?
Thanks
September 1st, 2008 at 2:53 amI am sorry I forgot to paste the entire script. Here is the full SQL Script output.
***************************************************************************
Starting the MbUnit Test Execution
Exploring DataAccessTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
MbUnit 2.4.2.130 Addin
No tests found
alter table ProductOrder drop constraint FK_ProductOrders
alter table ProductOrder drop constraint FK_OrderProducts
alter table [Order] drop constraint FK_CustomerOrders
alter table viewCustomersWithYoyo drop constraint FKADD890B370C8035
alter table viewCustomersWithYoyo drop constraint FKADD890B313C3E8FD
alter table OrderProduct drop constraint FK_ProductOrders
alter table OrderProduct drop constraint FK_OrderProducts
if exists (select * from dbo.sysobjects where id = object_id(N’ProductOrder’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table ProductOrder
if exists (select * from dbo.sysobjects where id = object_id(N’Product’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table Product
if exists (select * from dbo.sysobjects where id = object_id(N’[Order]‘) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table [Order]
if exists (select * from dbo.sysobjects where id = object_id(N’viewCustomersWithYoyo’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table viewCustomersWithYoyo
if exists (select * from dbo.sysobjects where id = object_id(N’Customer’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table Customer
if exists (select * from dbo.sysobjects where id = object_id(N’OrderProduct’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1) drop table OrderProduct
create table ProductOrder (
Product INT not null,
[Order] INT not null
)
create table Product (
ProductId INT IDENTITY NOT NULL,
Name NVARCHAR(50) not null,
Cost REAL not null,
primary key (ProductId)
)
create table [Order] (
OrderId INT IDENTITY NOT NULL,
OrderDate DATETIME not null,
Customer INT not null,
primary key (OrderId)
)
create table viewCustomersWithYoyo (
CustomerId INT IDENTITY NOT NULL,
Firstname NVARCHAR(255) null,
Lastname NVARCHAR(255) null,
OrderId INT null,
OrderDate DATETIME null,
primary key (CustomerId)
)
create table Customer (
CustomerId INT IDENTITY NOT NULL,
Version INT not null,
Firstname NVARCHAR(50) null,
Lastname NVARCHAR(50) null,
primary key (CustomerId)
)
create table OrderProduct (
[Order] INT not null,
Product INT not null
)
alter table ProductOrder add constraint FK_ProductOrders foreign key (Product) references Product
alter table ProductOrder add constraint FK_OrderProducts foreign key ([Order]) references [Order]
alter table [Order] add constraint FK_CustomerOrders foreign key (Customer) references Customer
alter table viewCustomersWithYoyo add constraint FKADD890B370C8035 foreign key (CustomerId) references Customer
alter table viewCustomersWithYoyo add constraint FKADD890B313C3E8FD foreign key (OrderId) references [Order]
alter table OrderProduct add constraint FK_ProductOrders foreign key (Product) references Product
TestCase ‘M:DataAccessTest.NHibernateDataProviderTest.CreateDatebase’
failed: There is already an object named ‘FK_ProductOrders’ in the database.
Could not create constraint. See previous errors.
NHibernate.HibernateException: There is already an object named ‘FK_ProductOrders’ in the database.
Could not create constraint. See previous errors. —> System.Data.SqlClient.SqlException: There is already an object named ‘FK_ProductOrders’ in the database.
Could not create constraint. See previous errors.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean format, Boolean throwOnError, TextWriter exportOutput, IDbCommand statement, String sql)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop, Boolean format, IDbConnection connection, TextWriter exportOutput)
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop, Boolean format)
— End of inner exception stack trace —
at NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop, Boolean format)
C:\Development\RnD\DineAndDiscussNHibernate_Session9\DataAccessTest\NHibernateDataProviderTest.cs(28,0): at DataAccessTest.NHibernateDataProviderTest.CreateDatebase()
0 passed, 1 failed, 0 skipped, took 2.25 seconds.
***************************************************************************
Thanks
September 1st, 2008 at 2:57 amSorry Steve,
My Bad….I had accidently written following in Product.hbm.xml
……
September 1st, 2008 at 3:18 am@Marc:
Yes, I agree this seems like a hack — and I will admit that its one that I arrived at all by myself after some fiddling so for all I know there may indeed be a better way (though I am unaware of it myself).
To my knowledge, it doesn’t get any better in this area w/Nhib 2.0
September 1st, 2008 at 7:04 am@Mrunal:
Glad you got it solved~!
September 1st, 2008 at 7:04 amI am trying to download the screen cast but this link always times out. Interestingly, I am able to download other sessions with .avi extension for example Session 06. Can anyone please guide me.
September 17th, 2008 at 4:43 amI have managed to download it.
Thanks
September 17th, 2008 at 5:18 amSyed