NHibernate on javan Hibernatesta portattu ORM-ratkaisu. NHibernatessa mäppäykset tehdään xml-tiedostoilla, mutta Fluent NHibernate tarjoaa vaihtoehtoisen, koodiin perustuvan mäppäyksen. Tämän tekniikan etuna on mm. suoraviivaisuus sekä useiden kirjoitusvirheiden eliminointi.

Fluent NHibernaten käyttöönotto

Mäppäykset

Jotta luokan mäppäys toimii odotetusti, tarvitsee pari seikkaa ottaa huomioon

Fluent NHibernatessa mäppäykset tehdään erityisillä luokilla ClassMap ja SubclassMap.

Yksinkertainen mäppäys

Luokka, jolla on muutama ominaisuus
public class Tuote
{
	// Id-ominaisuus
	public virtual int Id { get; private set; }
	// muita ominaisuuksia
	public virtual string Nimi { get; set; }
	public virtual decimal Hinta { get; set; }
}

Mäppäämistä varten tehdään uusi luokka, joka periytyy ClassMap -luokasta ja T korvataan mäpättävällä luokalla. Mäppäykset tehdään konstruktorissa

public class TuoteMap : ClassMap<Tuote>
{
	public TuoteMap()
	{
		Id(x => x.Id);
		Map(x => x.Nimi);
		Map(x => x.Hinta);
	}
}

Yllä on kaksi mäppäykseen tarkoitettua metodia Id mäppää identityn (yleensä auto increment, eli tietokannassa automaattisesti kasvava numero) Map mäppää ominaisuuden tietokannan sarakkeeseen

Monen suhde yhteen ja yhden suhde moneen

Monen suhde yhteen ja yhden suhde moneen -relaatiot ovat saman asian eri puolet. Esimerkiksi monta tuotetta voisi kuulua yhteen tuoteryhmään ja toiselta puolelta katsottuna yhdellä tuoteryhmällä olisi monta tuotetta.

Lisätään tuoteryhmä-luokka

public class Tuoteryhma
{
	// identity
	public virtual int Id { get; private set: }
	public virtual string Nimi { get; set; }
	// kokoelma tuotteita
	public virtual IList<Tuote> Tuotteet { get; set; }
}

ja lisätään tuote-luokkaan tuoteryhmä

public class Tuote
{
	// Id-ominaisuus
	public virtual int Id { get; private set; }
	// muita ominaisuuksia
	public virtual string Nimi { get; set; }
	public virtual decimal Hinta { get; set; }
	// tuoteryhmä
	public virtual Tuoteryhma Tuoteryhma { get; set; }
}

Nyt tuoteryhmän mäppäysluokassa käytetään HasMany-metodia määrittelemään, että tuotteita voi olla monta

public class TuoteryhmaMap : ClassMap<Tuoteryhma>
{
	public TuoteryhmaMap()
	{
		Id(x => x.Id);
		Map(x => x.Nimi);
		// monta tuotetta
		HasMany(x => x.Tuotteet).Inverse();
	}
}

HasMany:n jälkeen tuleva Inverse tarkoittaa, että tuote on vastuussa tallennuksestaan eli tuotteisiin tehtyjä muutoksia ei tallenneta, kun tuoteryhmä tallennetaan vaan ne pitää tallentaa erikseen.

Lisätään tuotteen mäppäykseen viittaus (References-metodi) tuoteryhmään ja määritellään erikseen sarakkeen nimi

public class TuoteMap : ClassMap<Tuote>
{
	public TuoteMap()
	{
		Id(x => x.Id);
		Map(x => x.Nimi);
		Map(x => x.Hinta);
		// viittaa yhteen tuoteryhmään
		// tallennetaan tuoteryhmän id sarakkeeseen TuoteryhmaId
		References(x => x.Tuoteryhma, "TuoteryhmaId");
	}
}

Monen suhde moneen

Jos muutetaan edellä ollutta esimerkkiä siten, että tuote voisikin kuulua useampaan tuoteryhmään, saadaan monen suhde moneen -relaatio. Eli yksi tuote voi kuulua moneen tuoteryhmään ja yhdellä tuoteryhmällä voi olla monta tuotetta.

Muutetaan tuote-luokkaa

public class Tuote
{
	public virtual int Id { get; private set; }
	public virtual string Nimi { get; set; }
	public virtual decimal Hinta { get; set; }
	// nyt onkin monta tuoteryhmää
	public virtual IList<Tuoteryhma> Tuoteryhmat { get; set; }
}

Nyt tuotteen mäppäyksessä References pitää vaihtaa HasManyToMany:yn

public class TuoteMap : ClassMap<Tuote>
{
	public TuoteMap()
	{
		Id(x => x.Id);
		Map(x => x.Nimi);
		Map(x => x.Hinta);
		// monta tuoryhmää
		HasManyToMany(x => x.Tuoteryhmat).Table("TuoteryhmanTuotteet").ParentKeyColumn("TuoteId").ChildKeyColumn("TuoteryhmaId");
	}
}

Table määrittelee ManyToMany:yn tarvittavan taulun nimen ParentKeyColumn määrittelee sarakkeen nimen omalle id:lle ChildKeyColumn määrittelee sarakkeen nimen toisen osapuolen id:lle

Tuoteryhmä-luokastakin HasMany pitää muuttaa HasManyToMany:yn

public class TuoteryhmaMap : ClassMap<Tuoteryhma>
{
	public TuoteryhmaMap()
	{
		Id(x => x.Id);
		Map(x => x.Nimi);
		// monta tuotetta
		HasManyToMany(x => x.Tuotteet).Table("TuoteryhmanTuotteet").ParentKeyColumn("TuoteryhmaId").ChildKeyColumn("TuoteId");
	}
}

HasManyToMany:n kummankin puolen mäppäys on melkein samanlainen; taulun nimi on sama, mutta ParentKeyColumn ja ChildKeyColumn vaihtuvat keskenään.

Komponentit

Komponentit ovat uudelleenkäytettäviä osia, joilla voi niputtaa joukon tietoja ilman, että tarvitsee luoda erillistä taulua. Esim. osoite voisi olla järkevä komponentti ja henkilöllä voisi olla kaksi osoitetta: työ- ja kotiosoite.

Tehdään osoitteelle luokka

public class Osoite
{
	public string Katuosoite { get; set; }
	public string Postinumero { get; set; }
	public string Postitoimipaikka { get; set; }
}

ja mäppäys-luokka (tällä kertaa periytyy ComponentMap -luokasta)

public class OsoiteMap : ComponentMap<Osoite>
{
	public OsoiteMap()
	{
		Map(x => x.Katuosoite);
		Map(x => x.Postinumero);
		Map(x => x.Postitoimipaikka);
	}
}

Nyt jos henkilöllä on kaksi osoitetta

public class Henkilo
{
	public Osoite Kotiosoite { get; set; }
	public Osoite TyoOsoite { get; set; }
}

voidaan ne mäpätä (käyttäen Component-metodia)

public class HenkiloMap : ClassMap<Henkilo>
{
	public HenkiloMap()
	{
		Component(x => x.Kotiosoite).ColumnPrefix("Koti_");
		Component(x => x.TyoOsoite).ColumnPrefix("Tyo_");
	}
}

Koska osoitteita on kaksi, täytyy sarakkeille määritellä etuliitteet (Koti_ ja Tyo_) ColumnPrefix-metodilla.

Konfigurointi

Konfigurointi suoritetaan Fluently.Configure -metodilla, joka palauttaa ISessionFactory-objektin.

Esim. käytetään MS SQL Server 2008:aa ja haetaan sille ConnectionString config-tiedostosta (web.config / app.config) nimellä connstr

Fluently.Configure()
	.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("connstr")))
	.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Tuote>())
	.BuildSessionFactory();

Jos haluaa hienosäätää tuota prosessia, voi asetukset "paljastaa" ExposeConfiguration-metodilla.

Paljastetaan asetukset BuildSchema-metodille, jossa määrätään luomaan taulut tietokantaan

// metodi, joka luo SessionFactory:n
public static ISessionFactory CreateSessionFactory()
{
	return Fluently.Configure()
		.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("connstr")))
		.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Tuote>())
		.ExposeConfiguration(BuildSchema)
		.BuildSessionFactory();
}
private static void BuildSchema(Configuration config)
{
	// Tämä luo taulut tietokantaan
	new SchemaExport(config).Create(false, true);
}