Simple NHibernate Example, Part 3: Initial Repository Implementation
Repositories are our central access point to pre-existing domain objects. Repositories simplify the management of the object’s life cycle and decouple the domain from data access technologies and strategies. Finally, repositories are domain level, communicating to other domain objects how certain objects can be accessed.
I will begin my initial repository implementation by exploring how to save (register) a NEW domain object with our repository, thus giving that object Identity within the context of our domain.
Let’s say that we have a new auto Dealer object. The auto dealer’s information has been entered into the domain object and we are not ready to persist that information into the domain.
Our initial specifications for the repository are the following. For this initial implementation, I am going to use a mocked interface representing the repository. This will allow us to explore the interactions between the domain object and the Repository interface. Eventually, we will replace this mocked interface code with the real NHibernate implementation.
1 using Foo.Domain;
2 using Foo.Domain.Repositories;
3 using NUnit.Framework;
4 using Rhino.Mocks;
5
6 namespace Specifications.Foo.Domain
7 {
8 [TestFixture]
9 public class WhenSavingANewDealerToTheDealerRepository
10 {
11 private const int MOCK_DB_ID = 1;
12 private Dealer dealer;
13 private MockRepository mocks;
14 private DealerRepository dealerRepository;
15
16 [SetUp]
17 public void SetUpContext()
18 {
19 dealer = new Dealer();
20 mocks = new MockRepository();
21 dealerRepository = mocks.CreateMock<DealerRepository>();
22 }
23
24 [Test]
25 public void ShouldPopulateAnIdForTheDealerAfterSaving()
26 {
27 SetupMockExpectations();
28
29 Assert.AreEqual(0, dealer.Id, “The dealer’s ID should not be set before the save.”);
30 Dealer savedDealer = dealerRepository.Save(dealer);
31
32 Assert.IsNotNull(savedDealer, “A saved dealer reference should be returned from the Repository.”);
33 Assert.AreNotEqual(dealer, savedDealer, “The saved dealer reference should be different from the one passed into the Save.”);
34 Assert.AreEqual(MOCK_DB_ID, savedDealer.Id, “A system ID should be generated for the newly saved dealer.”);
35 mocks.VerifyAll();
36 }
37
38 private void SetupMockExpectations()
39 {
40 Dealer dealerToReturnInExpectedCall = SetupExpectationThatACallToSaveWillReturnANewDealerReference();
41 SetupExpectationThatTheIdOnTheNewDealerReferenceWillBeSet(dealerToReturnInExpectedCall);
42 mocks.ReplayAll();
43 }
44
45 private static void SetupExpectationThatTheIdOnTheNewDealerReferenceWillBeSet(Dealer dealerToReturnInExpectedCall)
46 {
47 Expect.Call(dealerToReturnInExpectedCall.Id).Return(MOCK_DB_ID);
48 }
49
50 private Dealer SetupExpectationThatACallToSaveWillReturnANewDealerReference()
51 {
52 Dealer dealerToReturnInExpectedCall = mocks.PartialMock<Dealer>();
53 Expect.Call(dealerRepository.Save(dealer)).Return(dealerToReturnInExpectedCall);
54 return dealerToReturnInExpectedCall;
55 }
56 }
57 }
As an explanation:
- I am going to make a technical implementation decision (let me know if this is wrong) and return a new Dealer object reference from the save call (line 30). Simliar to NHibernate, I believe, this will allow for the return of the proxied reference, which is managed by NHibernate (mocked on lines 52 and 53). _This is a significant point and I hope to have some time to verify this in another post.
_
* The Dealer.Id property cannot be set externally (please review my previous post on Identity), so on line 47, I used Rhino mocks to partially mock the interface and return a value for that property.
* On line 33, the expectation is meant to show that the original reference to the Dealer domain object is not the same as the new “proxied” version.</UL>
This defines the basic specification for the interface:
<DIV>
<PRE><SPAN>namespace</SPAN> Foo.Domain.Repositories</PRE>
<PRE>{</PRE>
<PRE> <SPAN>public</SPAN> <SPAN>interface</SPAN> <SPAN>DealerRepository</SPAN></PRE>
<PRE> {</PRE>
<PRE> <SPAN>Dealer</SPAN> Save(<SPAN>Dealer</SPAN> dealer);</PRE>
<PRE> }</PRE>
<PRE>}<BR /></PRE>
<PRE> </PRE>
</DIV>
Time to begin implementation of the repository interface with NHibernate. I’ll create an NHibernate mapping file to begin with.
Assume that our database (in Sql Server 2005) and Dealer table with a DealerId column are in place. Also, this example uses <A href="http://www.hibernate.org/6.html" target="_blank">NHibernate 1.2.0CR1</A> released on 2/22/07. I <A href="http://sourceforge.net/project/showfiles.php?group_id=73818&package_id=73969" target="_blank">downloaded</A> the latest version for some of the newest functionality, including generics and null types.
The table definition:
<IMG src="http://lh3.google.com/image/nmontalvo/Rf4B-3jpLZI/AAAAAAAAABw/sfyLZohRjjU/DealerTable.jpg" border="0" />
The simplest NHibernate mapping file to match our specifications will look like this:
<DIV>
<SPAN><?</SPAN><SPAN>xml</SPAN><SPAN> </SPAN><SPAN>version</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>1.0</SPAN><SPAN>“</SPAN><SPAN> </SPAN><SPAN>encoding</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>utf-8</SPAN><SPAN>“</SPAN><SPAN> ?><BR /><</SPAN><SPAN>hibernate-mapping</SPAN><SPAN> </SPAN><SPAN>xmlns</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>urn:nhibernate-mapping-2.0</SPAN><SPAN>“</SPAN><SPAN>><BR /> <</SPAN><SPAN>class</SPAN><SPAN> </SPAN><SPAN>name</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>Foo.Domain.Dealer, Foo.Domain</SPAN><SPAN>“</SPAN><SPAN> </SPAN><SPAN>table</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>Dealer</SPAN><SPAN>“</SPAN><SPAN>><BR /> <</SPAN><SPAN>id</SPAN><SPAN> </SPAN><SPAN>name</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>Id</SPAN><SPAN>“</SPAN><SPAN> </SPAN><SPAN>column</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>DealerId</SPAN><SPAN>“</SPAN><SPAN>><BR /> <</SPAN><SPAN>generator</SPAN><SPAN> </SPAN><SPAN>class</SPAN><SPAN>=</SPAN><SPAN>“</SPAN><SPAN>assigned</SPAN><SPAN>“</SPAN><SPAN> /><BR /> </</SPAN><SPAN>id</SPAN><SPAN>><BR /> </</SPAN><SPAN>class</SPAN><SPAN>><BR /></</SPAN><SPAN>hibernate-mapping</SPAN><SPAN>></SPAN> <BR /><BR />
</DIV>
Basically, all this says is to map the **Dealer** domain property, **Id**, to the column **DealerId** in the database table **Dealer**. It also says to utilize the database table’s identity capabilities to generate the unique ID for the domain object.
Here is how I’ve structured the project. The Dealer mapping file is called **Dealer.hbm.xml**. There are references to the project containing the domain and the repository interface. There is also a reference to the NHibernate.dll file. The DealerRepository listed below is implementing the DealerRepository interface from the domain (code coming up shortly):
<IMG src="http://lh5.google.com/image/nmontalvo/Rf4LkXjpLcI/AAAAAAAAACI/Bzwct9WjaEY/DataProjectExample.jpg" border="0" />
And be sure to include the Dealer.hbm.xml as an **embedded resource**:
<IMG src="http://lh6.google.com/image/nmontalvo/Rf4D2njpLbI/AAAAAAAAACA/0TWvlTQcSv0/DealerXmlFile.jpg" border="0" />
I’ve jumped the gun a bit, so in the next posting, I’ll begin with writing NHibernate unit tests that interface with the database. The process is somewhat involved for the initial implementation, but once setup, it should be reusable for all database unit tests going forward.</p>