Instrukcja laboratoryjna nr.4

Save this PDF as:
 WORD  PNG  TXT  JPG

Wielkość: px
Rozpocząć pokaz od strony:

Download "Instrukcja laboratoryjna nr.4"

Transkrypt

1 Języki programowania na platformie.net 2017/18 Instrukcja laboratoryjna nr.4 Biblioteka NHibernate Prowadzący: Tomasz Goluch Wersja: 2.0

2 I. Biblioteka NHibernate. Cel: Instalacja biblioteki NHibernate, utworzenie nowej bazy danych SqlServer CE oraz skonfigurowanie połączenia. Aby zainstalować bibliotekę NHibernate w projekcie najlepiej wykorzystać Nugeta (komenda: Install-Package <NazwaPakietu> [-Version <WersjaBiblioteki>] [-Project <NazwaProjektu>]) np.: Uwaga w celu instalacji najnowszej wersji biblioteki (aktualnie 5.0.0) wymagany jest.net w wersji W wyniku instalacji powinniśmy otrzymać następujące referencje: Dodać nową lokalną bazę danych: Project Add NewItem (Ctrl+Shift+A) Service-based Database. Następnie należy dodać plik konfiguracyjny nhibernate.cfg.xml: <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" > <session-factory name="nhibernate.test"> <property name="connection.driver_class"> NHibernate.Driver.Sql2008ClientDriver </property> <property name="connection.connection_string"> Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename= DataDirectory \WZGLEDNA_SCIEZKA_DO_BAz Y_DANYCH.mdf;Integrated Security=True </property> <property name="dialect">nhibernate.dialect.mssql2008dialect</property> <property name="show_sql">true</property> <mapping assembly="nazwa_assembly" /> </session-factory> </hibernate-configuration> Należy zwrócić uwagę czy: element <property name="connection.connection_string"> zawiera poprawny Connection string. Można go odnaleźć we właściwościach (okienko baz danych, nie projektu) pliku *.sdf Connection string i skopiować do powyższego pliku konfiguracyjnego, element <property name="show_sql"> ustawiony na true wypisuje w oknie konsoli zapytania bazodanowe, pozwala to na obserwację mechanizmu leniwego ładowania,

3 wartość atrybutu assembly elementu mapping to nazwa podzespołu w zawierającego mapowane klasy. Właściwości Build Action i Copy to Output Directory pliku konfiguracyjnego powinny być ustawione na Content i Copy Always. Wczytanie pliku konfiguracyjnego: Configuration cfg = new Configuration(); cfg.configure(); // domyślnie hibernate.cfg.xml w innym przypadku należy podać II. Sesja. Cel: Utworzenie sesji przy użyciu fabryki. Obiekt fabryki jest obiektem ciężkim i powinniśmy tworzyć tylko jeden dla danej konfiguracji. Pozwala on na tworzenie lekkich obiektów sesji, w ramach których będą wykonywane operacje bazodanowe. W celu wymuszenia zmian w bazie danych można wykorzystać metodę Flush(). ISessionFactory sf = cfg.buildsessionfactory(); using (ISession cs = sf.opensession()) { // Tutaj umieszczamy kod cs.flush(); // Kontynuujemy po utrwaleniu zmian w bazie } sf.close(); III. Trwałe obiekty. Cel: Tworzenie klas obiektów utrwalanych w bazie danych. Każdy utrwalany obiekt to zwykły obiekt klasy jednakże powinna spełniać kilka wymagań: powinna być publiczna aby skorzystać z leniwego ładowania, posiadać publiczny bezparametrowy konstruktor (może być domyślny 1 ), 1 W przypadku zaimplementowania dowolnego konstruktora, domyślny bezparametrowy konstruktor jest niedostępny i trzeba go zaimplementować explicite.

4 właściwości klasy powinny być wirtualne (NHibernate tworzy obiekty proxy wywodząc je z naszej klasy nadpisuje właściwości, więc muszą być wirtualne) powinna istnieć właściwość, która będzie identyfikatorem obiektu (tak jak id w bazie danych 2 ) public class Klasa { public Klasa() { } public virtual int id { get; set; } public virtual string data { get; set; } } Każdej klasie trwałych obiektów odpowiada plik konfiguracyjny.<nazwa_klasy>.hbm.xml (NAZWA_KLASY musi być zgodna z rzeczywistą nazwą klasy a nie z nazwą pliku w którym jest jej definicja). <?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="nazwa_property" column="nazwa_kolumny_w_bazie_danych" /> </class> </hibernate-mapping> id opisuje właściwość, która jest identyfikatorem obiektu o generator opisuje, w jaki sposób będą generowane wartości dla tworzonych elementów, poniżej omówiono tylko wybrane 3 : class="increment" generuje identyfikatory dowolnego typu integralnego (sbyte, byte, short, ushot, int, uint, ulong, char), które są unikalne tylko wtedy, gdy tylko jeden proces wstawia dane do tej tabeli, class="identity" wsparcie dla automatycznie generowanych wartości kolumny w DB2, MySQL, MS SQL Server, Sybase i HypersonicSQL (np. AUTO_INCREMENT w MySQL). Zwracany identyfikator jest konwertowany za pomocą Convert.ChangeType do właściwości dowolnego typu integralnego, class="sequence" generuje niepowtarzalną wartość wykorzystując konstrukcję SEQUENCE dostępną w bazach danych DB2, Interbase, Oracle, PostgreSQL i SAP DB. Konwersja identyfikatora identycznie jak w przypadku identity. 2 Dla NHibernate, dwa obiekty o tym samym identyfikatorze to te same id. W większości sytuacji biblioteka będzie nam gwarantowała, że jeżeli id dwóch obiektów jest takie samo, to jest to ten sam obiekt w pamięci. 3 Więcej informacji o sposobach generowania kluczy przez NHibernate można znaleźć tutaj: i tutaj:

5 class="hilo" wykorzystuje algorytm Hi/Lo do wydajnego generowania identyfikatorów dowolnego typu integralnego, class="native" identity, sequence albo hilo w zależności który sposób jest najbardziej natywny dla danej bazy danych. class="guid" wykorzystywany jest typ System.Guid class="guid.comb" jak powyżej tylko do generowania kluczy wykorzystywany jest algorytm opisany tutaj. property opisują właściwości klasy, zawierają między innymi takie atrybuty jak: o name odpowiada nazwie właściwości w klasie, o column (opcjonalny) odpowiada nazwie kolumny w bazie, domyślnie kolumna przyjmie nazwę właściwości, o type (opcjonalny) typ identyfikatora podany explicite. Lista dostępnych typów jest dostępna tutaj: Właściwość Build Action należy ustawić na Embbeded Resource ponieważ NHibernate przegląda osadzone zasoby w poszukiwaniu plików *.hbm.xml i pobiera z nich opisy obiektów. 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.). W celu automatycznej weryfikacji składni plików konfiguracyjnych możemy podać VS ich definicje. W tym celu należy kliknąć na odpowiednim pliku.xml i we właściwościach podać dla właściwości Schemas ścieżkę do odpowiedniego pliku.xsd znajdującego się w głównym katalogu NHibernate. Dla pliku konfiguracji NHibernate jest to nhibernate-configuration.xsd a dla plików mapowania nhibernate-mapping.xsd.

6 Tabele można utworzyć ręcznie (wtedy trzeba pamiętać o zgodności tabeli z opisem), gdy nie podamy parametru 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: new SchemaExport(cfg).Execute(false, true, false); cfg obiekt konfiguracji (musi być 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, uwaga! stare dane zostaną usunięte. new SchemaUpdate(cfg).Execute(false, true); parametry, poza ostatnim, identyczne jak w przypadku SchemaExport. Trwały obiekt może znajdować się w jednym z trzech stanów: 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 NazwaKlasy() (obiekt w stanie przejściowym) PersistClass pc = new PersistClass(); 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 go do obiektu) cs.save(pc); cs.save(pc, 1); Odczytanie obiektu z bazy: currentsession.get<typ>(id_obiektu_do_odczytania)

7 PersistClass pc2 = cs.get<persistclass>(1); jeżeli obiekt nie istnieje w bazie, zostanie zwrócone null jeżeli obiekt był już wcześniej odczytany, otrzymamy referencję na niego: PersistClass pc = new PersistClass(); cs.save(pc, 1); PersistClass pc2 = cs.get<persistclass>(0); Console.WriteLine(pc.Equals(pc2)); // "true" - pc2 wskazuje na ten sam obiekt co pc usunięcie: cs.delete(pc); // obiekt, nie jego id! aktualizacja: cs.save(pc); cs.update(pc); Transakcje: mechanizm jak w SQL ITransaction t = null; t = cs.begintransaction(); możemy też jawnie zdefiniować poziom izolacji transakcji: t = cs.begintransaction(system.data.isolationlevel.<isolationlevel>) zatwierdzenie/odwołanie transakcji: t.commit(); t.rollback(); IV. Zawieranie obiektów i kolekcji Cel: Utrwalanie złożonych obiektów i kolekcji oraz wykorzystanie funkcji leniwego ładowania.. Obiekty mogą zawierać inne obiekty, a dokładnie referencje do niech. Jest to reprezentowane w bazie danych przy pomocy relacji jeden do wielu, oraz w pliku mapującym przy pomocy odpowiedniego elementu many-to-one: <many-to-one name="nazwa_obiektu_zawieranej_klasy" column="nazwa_tabeli_zawieranej_klasy" cascade="all" /> atrybuty jak w przypadku elementu property, atrybut cascade opisuje operacje na powiązanych obiektach: o all zapisuje/aktualizuje/usuwa obiekt bieżący i te, na które ma referencje, o save-update po referencjach przenoszą się tylko operacje save i update, o delete tylko operacje usuwania,

8 o none żadne operacje. np. dla kodu: PersistClass pc = new PersistClass(); pc.ac = new AggregatedClass(); cs.save(pc); dla cascade="none" lub "delete" utrwalona zostanie tylko klasa pc (pc.ac musimy utrwalić sami). W pozostałych przypadkach zostanie zapisane w bazie zarówno pc jak i pc.ac. Jeżeli ręcznie tworzymy bazę, należy dodać kolumnę, która będzie przechowywała identyfikator obiektu, na który trzymamy referencję (agregowanego). W celu zaobserwowania obserwacja leniwego ładowania należy: w pliku konfiguracyjnym ustawić: <property name="show_sql">true</property>. utworzyć obiekt A i B, powiązany z nim (A ma referencję na B) w nowej sesji (np. nowe uruchomienie programu): odczytać z bazy A, wypisać zawartość pól (innych niż referencja) wypisać zawartość pól obiektu, do którego mamy referencję obiekt B zostanie odczytany z bazy dopiero wówczas, gdy będziemy próbowali wypisać jego pola (dopiero przed drugim wypisaniem); do tego czasu B istnieje jako proxy. V. Dziedziczenie. Cel: Realizacja paradygmatu mechanizmu dziedziczenia przy pomocy NHibernate.. Sposoby realizacji dziedziczenia: model tabela na klasę: o cała klasa i jej potomne przechowywane są w jednej tabeli. Klasy rozróżniane są po discriminator-value, króra przechowywana jest w osobnej kolumnie, o kolumny nie mogą mieć ograniczenia not null, o tabela powinna posiadać kolumny dla wszystkich podklas, <class name="ipayment" table="payment"> <id name="id" type="int64" column="payment_id"> <generator class="native"/> </id> <discriminator column="payment_type" type="string"/> <property name="amount" column="amount"/> <subclass name="creditcardpayment" discriminator-value="credit"> </subclass> <subclass name="cashpayment" discriminator-value="cash"> </subclass> <subclass name="chequepayment" discriminator-value="cheque"> </subclass> </class>

9 model tabela na podklasę: o pola, o które klasa bazowa jest rozszerzona przechowywane są w osobnej tabeli, o tabela ta zawiera dodatkowe pole identyfikator obiektu bazowego, o obiekt klasy potomnej przechowywany jest częściowo w tabeli bazowej (pola klasy bazowej), a częściowo w dodatkowej tabeli; identyfikator wiąże oba wiersze, <class name="ipayment" table="payment"> <id name="id" type="int64" column="payment_id"> <generator class="native"/> </id> <property name="amount" column="amount"/> <joined-subclass name="creditcardpayment" table="credit_payment"> <key column="payment_id"/> </joined-subclass> <joined-subclass name="cashpayment" table="cash_payment"> <key column="payment_id"/> </joined-subclass> <joined-subclass name="chequepayment" table="cheque_payment"> <key column="payment_id"/> </joined-subclass> </class> model tabela na konkretną klasę: o każda klasa posiada swoją tabelę, o każda tabela musi zawierać wszystkie swoje właściwości, o kolumny przechowujące pola klasy bazowej muszą nazywać się tak samo we wszystkich tabelach. <class name="payment"> <id name="id" type="int64" column="payment_id"> <generator class="sequence"/> </id> <property name="amount" column="amount"/> <union-subclass name="creditcardpayment" table="credit_payment"> <property name="creditcardtype" column="cctype"/> </union-subclass> <union-subclass name="cashpayment" table="cash_payment"> </union-subclass> <union-subclass name="chequepayment" table="cheque_payment"> </union-subclass> </class>

10 VI. Kolekcje Cel: Utrwalanie kolekcji.. NHibernate pozwala na utrwalanie całych kolekcji. Aktualnie wspierane są tylko kolekcje generyczne. Dostępne są różne elementy set, list, map, bag, array, primitive-array odpowiadające odpowiednim kolekcjom.net. Mapowanie wymaga zachowania pewnych reguł: pola w klasie powinny być typu interfejsowego ( IList, ISet, IDictionary, IDictionary<>, itp.) obiekty powinny być utworzone w konstruktorze: public PersistClass() { IntSet = new HashSet<int>(); } public virtual ISet<int> IntSet { get; set; } lub jako wartość początkowa: public virtual ISet<int> IntSet { get; set; } = new HashSet<int>(); NHibernate podczas zapisywania podmienia zawartość pola na swoją implementację, więc nie będzie poprawne wywołanie: HashSet s = klasa.dane; kolekcje poza ISet<int> posiadają indeks liczbę typu Int32 lub dowolnego typu (w przypadku słowników). Należy to uwzględnić ręcznie tworząc tabele. Kolekcja przechowywana jest w osobnej tabeli, której kolumny zawierają: o daną o indeks (poza ISet<int>) o klucz/identyfikator obiektu, do którego należą Mapowanie listy typów prostych: <list name="nazwa_property" table="nazwa_tabeli" > <key column = "KOLUMNA_KLUCZY"/> <index column="kolumna_indeksów"/> <element column="kolumna_danych" type="typ_danych "/> </list> Mapowanie zbioru typów złożonych: <set name="nazwa_property" table="nazwa_tabeli" > <key column = "KOLUMNA_KLUCZY"/> <one-to-many class = "NAZWA_KLASY"/> </set> Słownik IDictionary<string, int>: public virtual IDictionary<string, int> DataDict { get; set; } = new Dictionary<string, int>(); <map name="nazwa_property " table="nazwa_tabeli"> <key column="kolumna_kluczy" /> <index column=" KOLUMNA_INDEKSÓW" type="string" /> <element column="kolumna_danych" type="int32" />

11 </map> NAZWA_PROPERTY nazwa wirtualnej właściwości w klasie reprezentującej kolekcję.net, NAZWA_TABELI nazwa tabeli, w której będą przechowywane dane kolekcji, KOLUMNA_KLUCZY nazwa kolumny w tabeli NAZWA_TABELI, przechowującej identyfikator obiektu, do którego wiersz należy, KOLUMNA_INDEKSÓW nazwa kolumny w tabeli NAZWA_TABELI, przechowującej indeks wartości kolekcji indeksowanych takich jak list i map (dla węzłów innych niż map typ powinien być Int32), KOLUMNA_DANYCH to nazwa kolumny w tabeli NAZWA_TABELI, przechowującej daną przypisaną do klucza W przypadku słowników jeżeli indeks jest obiektem, zamiast elementu index należy użyć elementu index-many-to-many, jeżeli wartość jest obiektem, zamiast elementu element należy użyć elementu many-to-one. VII. Zapytania HQL 4. Cel: Zapoznanie z językiem zapytań Hibernate Query Language. HQL jest językiem zapytań podobnym do SQL, obsługuje większość konstrukcji z SQL, jednak jest to język w pełni obiektowy i rozumie takie pojęcia jak dziedziczenie, polimorfizm i asocjacja. Jest case insensitive oprócz nazw klas i właściwości które muszą być pisane z uwzględnieniem wielkości liter. IQuery q = cs.createquery("from PersistClass k"); // Klasa: nazwa TYPU! //IEnumerable<PersistClass> data = q.enumerable<persistclass>(); //IList<PersistClass> data = q.list<persistclass>(); ICollection<PersistClass> data = q.list<persistclass>(); foreach (PersistClass v in data) Console.WriteLine("PersistClass({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ąć Konstrukcje: FROM Namespace.Class from Class c alias From Class as c j.w. from A, B iloczyn kartezjański lub "cross" join from A as a, B as b j.w inner join 4 Więcej szczegółowych informacji o składni HQL można znaleźć tutaj:

12 left outer join right outer join where a.data = b. Data warunki (nazwy pól klas a nie kolumn!) where a.data like 'a_%_%' % - zero lub więcej znaków, _ - jeden znak 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ą: o jeżeli wybieramy tylko jedno pole obiekty typu tego pola, o jeżeli wybieramy kilka poł będą siedzieć w tabeli obiektów. IList<object[]> datatab = cs.createquery("select id, data from PersistClass pk").list<object[]>();int id = (int)datatab[0][0]; string data = (string)datatab[0][1]; parametry zapytania: o pozycyjne:? ("where k.id=?") o nazwane: :nazwa (dwukropek nazwa) ("where dana=:param") IQuery::SetString/SetInt32/SetParameter/Set(nazwa_lub_numer, obiekt) o nazwa_lub_numer to numer parametru pozycyjnego (liczba, licząc od 0) lub nazwa parametru nazwanego (string) o obiekt wartość do nadania parametrowi IQuery::SetFirstResult(numer) numer pierwszego zwróconego wiersza IQuery::SetMaxResults(liczba) ustawienie maksymalnej liczby zwróconych wierszy Wykorzystanie kolekcji w HQL: "from Klasa k where k[ klucz ] = 5" VIII. Zapytania SQL 5. Cel: Zapoznanie z wykorzystaniem natywnego języka zapytań SQL w NHibernate. Realizacja zapytań SQL jest kontrolowana przez interfejs ISQLQuery, który jest uzyskiwany przez wywołanie metody ISession.CreateSQLQuery(), której parametrem jest zapytanie w SQL. Wynik zapytania należy opisać: ISQLQuery::AddScalar(nazwa, typ) nazwa alias kolumny wyniku (string) typ typ wyniku (NHibernateUtil.[String Int32 itd.]) ISQLQuery::List() (nie ma enumerable): IList l = query.list() 5 Więcej o Native SQL dla NHibernate tutaj:

13 wynik lista obiektów (jeżeli pojedyncza kolumna) lub lista tablic obiektów (jak przy zapytaniach HQL) jeżeli chcemy otrzymać obiekty "SELECT {t.*} from tabela t": o tabela nazwa tabeli, w której przechowywane są obiekty o {t.*} oznacza wszystkie pola klasy z indywidualnymi polami jest problem trzeba odpowiednio aliasować kolumny, tworzyć dodatkowe opisujące typ itp. spojrzeć na rzeczywiste zapytanie (<property name="show_sql">true</property>) ISQLQuery sq = cs.createsqlquery("select id, info FROM persistclassindb").addscalar("id", NHibernateUtil.Int32).AddScalar("info", NHibernateUtil.String); IList e = q.list(); foreach (object[] v in e) Console.WriteLine("{0} {1}", v[0], v[1]); Obiekty też należy opisać: ISQLQuery::AddEntity(alias_kolumny, typ_obiektu) ISQLQuery sq = cs.createsqlquery("select { pk.*} from persistclassindb pk").addentity("pk", typeof(persistclass)); IList<PersistClass> f = q.list<persistclass>(); foreach (var v in f) Console.WriteLine("{0} {1}", v.id, v.data); Odczytanie klasy zawierającej kolekcję: ISQLQuery sq = cs.createsqlquery("select {k.*} from tab k, dict di where k.id = di.id").addentity("k", typeof(persistclass)); IList<PersistClass> f = q.list<persistclass>(); foreach (var v in f) { Console.WriteLine("{0}", v.id); if (v.dict.containskey("ef")) Console.WriteLine(" ef = {0}", v.dict["ef"]); } Trzeba ręcznie powiązać dane z tabeli z elementami kolekcji. Powyższy przykład nie jest w pełni funkcjonalny NHibernate nie wie, że elementy słownika przeczytał już z bazy danych przy dostępie do niego wystąpi dodatkowe zapytanie do bazy. Aby poinformować NHibernate o odczytanych danych, trzeba dodatkowo opisać połączenie: AddJoin("nazwa_aliasu_tabeli_kolekcji", "alias_tabeli_obiektu.nazwa_pola_kolekcji") ISQLQuery sq = cs.createsqlquery("select {k.*}, {di.*} from tab k, dict di where k.id = di.id").addentity("k", typeof(persistclass)).addjoin("di", "k.dict");

14 IX. Błędy. Cel: Informacja i najczęściej spotykanych błędach podczas związanych z NHibernate. No persister for:<nazwa_assembly>.<nazwa_klasy> plik mapujący <NAZWA_KLASY>.hbm.xml nie istnieje lub ma złą nazwę możliwe, że nie jest dołączany jako zasób osadzony (Properties BuildAction: Embedded Resource). Could not compile the mapping document: <NAZWA_ASSEMBLY>.<NAZWA_ KLASY >.hbm.xml najprawdopodobniej nazwie pliku mapującego nie odpowiada, żadna klasa (liczy się nazwa obiektu a nie pliku). Invalid Cast (check your mapping for property type mismatches); setter of <NAZWA_ASSEMBLY>.<NAZWA_ KLASY> błąd rzutowania, należy sprawdzić czy wszystkie właściwiści klasy są poprawnie zdefiniowane w odpowiadającym jej pliku *.nhm.xml. could not insert: [<NAZWA_ASSEMBLY>.<NAZWA_KLASY>][SQL: INSERT INTO <NAZWA_TABELI_W_BAZIE_DANYCH> (<NAZWA_KOLUMNY_W_BAZIE_DANYCH>) VALUES (?); select SCOPE_IDENTITY()] możliwe, że nie istnieje wymagana tabela, możliwy brak wywołania new SchemaExport(cfg).Execute( ). <NAZWA_ASSEMBLY>.<NAZWA_KLASY>: method get_<nazwa_property> should be 'public/protected virtual' or 'protected internal virtual' property mapowane przez NHibernate muszą być virtual.