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
Fluent NHibernatessa mäppäykset tehdään erityisillä luokilla ClassMap
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
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
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");
}
}
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.
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
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.
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);
}