1. Pobranie i instalacja NHibernate - nhforge.org -> download, rozpakować, i zapamiętać, gdzie jest (<katalog>) - nhforge.org -> getting started, manual 2. Użycie w projekcie: - dodanie referencji - <katalog>\Required_Bins\NHibernate.dll - <katalog>\Required_For_LazyLoading\Spring\NHibernate.ByteCode.Spring.dll (do obsługi lazy loading) - ustawienie opcji projektu (architektura celu: x86) (na x86-64 jest problem z ADO) - przestrzenie nazw: - using NHibernate; - using NHibernate.Cfg; - dodanie pliku konfiguracyjnego - <katalog>\Configuration_Templates -> można wybrać odpowiedni dla bazy - w naszym przypadku: kod poniżej (dodać do projektu w nhibernate.cfg.xml) - ustawić property connection.connection_string - ustawić property proxyfactory.factory_class na Spring (zmienić LinFu na Spring) - property show_sql – jeżeli ma wartość true, to na konsoli będą wyświetlane wszystkie zapytania do bazy (ustawić, żeby zaobserwować leniwe ładowanie) - property query.substitutions – możliwość podmiany wartości w zapytaniach - property connection.release_mode – zarządzanie połączeniem z bazą (ADO) (bez tego, mogą czasem wyskakiwać wyjątki) - dodać jako ostatnie property: mapping_assembly – nazwa assembly w którym będą zapisywane klasy - ustawienie we właściwościach: copy to output: always (zawsze kopiuj plik to katalogu z binarką) <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> </configSections> <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2"> <session-factory> <property name="dialect">NHibernate.Dialect.MsSqlCeDialect</property> <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property> <property name="connection.connection_string">TU WPISAC CONNECTION STRING</property> <property name="connection.driver_class">NHibernate.Driver.SqlServerCeDriver</property> <property name="proxyfactory.factory_class"> NHibernate.ByteCode.Spring.ProxyFactoryFactory, NHibernate.ByteCode.Spring </property> <property name="show_sql">False</property> <property name="connection.release_mode">on_close</property> <mapping assembly="TU WPISAC NAZWE ASSEMBLY" /> </session-factory> </hibernate-configuration> </configuration> - - utworzenie bazy danych (w SqlServer CE) - dodanie referencji: System.Data.SqlServerCe.dll - program files [x86]->MS SQL Server Compact Edition\v3.5\desktop\... - properties -> copy local ustawić na true - projekt -> add item -> local database - we właściwościach (okienko baz danych, nie projektu) *.sdf -> Connection string -> skopiować do pliku konfiguracyjnego utworzenie konfiguracji (w main-ie): Configuration cfg; cfg = new Configuration(); cfg.Configure(); // szuka w lokalnym katalogu “hibernate.cfg.xml” //LUB:cfg.Configure("hibernate.cfg.xml"); <- z nazwą pliku z konfiguracją - utworzenie fabryki sesji: - fabryka sesji jest „ciężkim” obiektem – tworzyć tylko jedną na konfigurację ISessionFactory sessionFactory; sessionFactory = cfg.BuildSessionFactory(); ... sessionFactory.Close(); - utworzenie sesji - sesja jest „lekka” - operacje na bazie będą wykonywane jako metody sesji - metoda Flush() – wymuszenie zapamiętania w bazie wszystkich zmian (podobnie jak fflush na plikach) ISession currentSession; currentSession = sessionFactory.OpenSession(); ... currentSession.Flush() currentSession.Close(); 3. Trwała klasa: - definicja kodu klasy - klasa publiczna (bez tego nie skorzystamy z leniwego ładowania) - publiczny bezparametrowy konstruktor (może być domyślny) - wirtualne properties (hibernate tworzy obiekty proxy wywodząc je z naszej klasy – nadpisuje properties, więc muszą być wirtualne) - powinna mieć property, które będzie identyfikatorem obiektu (tak jak id w bazie danych) - dla NHibernate, dwa obiekty o tym samym identyfikatorze to te same id; biblioteka będzie nam gwarantowała (w większości sytuacji – o tym później) że jeżeli id dwóch obiektów jest takie samo, to jest to ten sam obiekt (w pamięci) class Klasa { public Klasa() {} public virtual int id { get; set; } public virtual string data { get; set; } } - powiązanie klasy z jej bazodanową reprezentacją – plik hbm (nazwa_klasy.hbm.xml) - właściwości -> build action: embedded resource – hibernate przegląda osadzone zasoby w poszukiwaniu plików *.hbm.xml i pobiera z nich opisy obiektów) - plik ten opisuje klasę: nazwa klasy, assembly w którym się znajduje, nazwa tabeli w bazie danych, w której będą przechowywane dane oraz właściwości, które mają być pamiętane <?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NAZWA NAMESPACE" assembly="NAZWA ASSEMBLY"> <class name="NAZWA KLASY" table="NAZWA TABELI W BAZIE DANYCH"> <id name="id"><generator class="native" /></id> <property name="data" column="napis"/> </class> </hibernate-mapping> - węzeł id – opisuje właściwość, która jest identyfikatorem obiektu - węzeł generator – opisuje, w jaki sposób będą generowane wartości dla tworzonych elementów - klasa assigned – użytkownik jest odpowiedzialny za przydzielanie identyfikatorów - klasa native – „autoincrement” właściwy dla bazy danych (identity/sequence/hilo) - increment – działa tylko, gdy jeden proces równocześnie wstawia dane - identity – identity columns in DB2, MySQL, MS SQL Server and Sybase - - 4. - sequence – sequence in DB2, PostgreSQL, Oracle or a generator in Firebird - ... - typowe parametry: - name – nazwa property w klasie - column (opcjonalne) – nazwa kolumny w tabeli, w której property zostanie zachowana - type (opcjonalne) – typ identyfikatora węzły property – opisują pozostałe właściwości - każdą właściwość, która ma być zachowana, należy opisać - typowe parametry: - name (wymagane) – nazwa property w klasie - column (opcjonalne) – nazwa kolumny w tabeli, w której property zostanie zachowana - type (opcjonalne) – typ właściwości - NHibernate jest w stanie zapamiętać, bez dodatkowego wysiłku z naszej strony, wszystko, co jest serializowalne (ISerializable) – byle był w stanie przełożyć to na zawartość kolumny w bazie (dla ISerializable potrzebna jest kolumna typu varbinary/blob/itp.) tabela w bazie danych - tabele można utworzyć ręcznie (wtedy trzeba pamiętać o zgodności tabeli z opisem) - gdy nie podamy parametry column, to kolumna musi nazywać się tak samo jak właściwość - typy kolumn powinny być zgodne z typami properties „w rozsądnym zakresie” - biblioteka jest w stanie przechowywać np. liczbę w kolumnie typu varchar, ale w drugą stronę sobie nie poradzi - automatyczne tworzenie/aktualizacja tabel: - namespace NHibernate.Tool.hbm2ddl - new SchemaExport(cfg).Execute(false, true, false); - cfg – obiekt konfiguracji (już skonfigurowany) - pierwszy parametr – czy wyświetlać na konsolę utworzony opis (create-y) - drugi parametr – czy rzeczywiście wykonać ddl na bazie - trzeci parametr – jeżeli true, to będą wykonane tylko drop-y (usunięcie tabel, bez tworzenia) - np. false, true, false – utwórz na nowo wszystkie tabele - np. false, true, true – tylko usuń tabele (np. po wykonaniu testów) - np. true, false, false – wyświetl, to co byłoby zrobione, ale nic nie wykonuj - stare dane zostaną usunięte! - new SchemaUpdate(cfg).Execute(false, true); - parametry takie jak pierwsze dwa parametry SchemaExport Podstawowe operacje: - 3 stany obiektu - przejściowy – nie ma przydzielonego trwałego identyfikatora (np. zaraz po utworzeniu obiektu, kiedy nie został jeszcze zapisany w bazie) - trwały – ma trwały identyfikator i może być już powiązany z wierszem w bazie danych; przypisany jest do istniejącej, otwartej sesji; w tym stanie gwarantowane jest, że jeżeli dwa obiekty mają takie samo id, to jest to ten sam obiekt „w pamięci”; w tym stanie operacje na obiekcie będą „automagicznie” utrwalane w bazie danych - odłączony – ma przypisany trwały identyfikator i być może wiersz w bazie, ale sesja w której istniał została zamknięta lub obiekt został przeserializowany do innego procesu; nie mamy gwarancji z drugiego punktu - utworzenie obiektu: tak, jak wszystkie obiekty – new NazwaTypu() (obiekt w stanie przejściowym) - zapisanie w bazie: (obiekt staje się trwały) - currentSession.Save(obiekt) - currentSession.Save(obiekt, id_dla_niego) (jeżeli id jest assigned i jeszcze nie przypisaliśmy id do obiektu) - odczytanie obiektu z bazy - currentSession.Get<Typ>(id_obiektu_do_odczytania) - np Klasa k = currentSession.Get<Klasa>(10); - jeżeli obiekt nie istnieje w bazie, zostanie zwrócone null - jeżeli obiekt był już wcześniej odczytany, otrzymamy referencję na niego: k = s.Get<Klasa>(1); Console.WriteLine("k.d = " + k.data); // wypisze „abc” k.data = "def"; Klasa k2 = s.Get<Klasa>(1); Console.WriteLine("k2.d = " + k.data); // wypisze „def” - 5. // k2 jest tym samym obiektem co k usunięcie: currentSession.Delete(obiekt) (obiekt, nie jego id !) Update / SaveOrUpdate transakcje - mechanizm jak w SQL ITransaction t = currentSession.BeginTransaction(); //LUB:ITransaction t = // currentSession.BeginTransaction(System.Data.IsolationLevel.*); ... t.Commit(); t.Rollback(); Zapytania i HQL - interfejs IQuery IQuery q = currentSession.CreateQuery("from Klasa k"); // Klasa: nazwa TYPU! IEnumerable<Test> data; // IList<Klasa> data; data = q.Enumerable<Klasa>(); // data = q.List<Klasa>(); foreach(Klasa v in data) Console.WriteLine("Klasa({0},\”{1}\”)", v.id, v.data); - parametrem do CreateQuery jest zapytanie w języku HQL - HQL to taki SQL, w którym zamiast nazw tabel i kolumn mamy typy (klasy) i ich pola - np. „from Klasa k where k.id < 10” – „select *” można pominąć - obsługuje większość konstrukcji z SQL: - case insensitive (poza nazwami klas) - from Namespace.Class - from Namespace.Class as c – alias - from Namespace.Class c – alias j.w. - from A, B – iloczyn kartezjański - inner join - left outer join - right outer join - where – warunki (nazwy pól klas a nie kolumn!) - is null - is not null - between ‘A’ and ‘B’ - in ( ‘Foo’, ‘Bar’, ‘Baz’) - select ... from ... – wybrane pola z klasy – wartością elementami kolekcji będą: - jeżeli wybieramy tylko jedno pole – obiekty typu tego pola - jeżeli wybieramy kilka pol – będą „siedzieć” w tablicy obiektów - IList<object []> data = ..CreateQuery(„select id, data from Klasa k”).List<object []>(); - (int)(data[0][0]) = id, (string)(data[0][1]) = data - parametry zapytania - pozycyjne: ? („where k.id=?”) - nazwane: :nazwa (dwukropek nazwa) („where dana=:param”) - IQuery::SetString/SetInt32/SetParameter/Set...(nazwa_lub_numer, obiekt) - nazwa_lub_numer to numer parametru pozycyjnego (liczba, licząc od 0) lub nazwa parametru nazwanego (string) - obiekt – wartość do nadania parametrowi - IQuery::SetFirstResult(numer) – numer pierwszego zwróconego wiersza - IQuery::SetMaxResults(liczba) – ustawienie maksymalnej liczby zwróconych wierszy