Kamil Kliczbor @ asptip.net

29Sep/130

Strategie pozyskiwania encji w NHibernate

NhLogoWhite64_med

Wprowadzenie

Istnieje wiele sposobów, na podstawie których NHibernate uzyskuje dostęp do powiązanych encji lub kolekcji encji. Prawidłowe zarządzanie strategiami wczytywania powiązanych obiektów umożliwia w wielu przypadkach poprawienie wydajności aplikacji. Deklaracja sposobu dostępu do encji może być podana w metadanych pliku mapowań (*.hbm.xml) lub nadpisana w zapytaniach HQL lub Criteria.

31Jan/110

101 przykładów zapytań w NHibernate 3.x – HQL, Criteria API, QueryOver i Linq – Dzielenie danych

Wprowadzenie

Wpis ten jest częścią artykułu poświęconemu wykonywaniu zapytań w NHibernate przy użyciu różnych sposobów jakie udostępnia ten mapper - HQL, Criteria API, QueryOver i Linq.

Część ta opisuje stosowanie dzielenie danych, tj otrzymywanie części wyników zapytania w określonych porcjach. W MS-SQLu można te zagadnienia zrealizować odpowiednio przy użyciu instrukcji

  • TOP - pobiera określoną liczbę pierwszych wyników zapytania
  • TOP wraz z ROW_NUMBER OVER (ORDER BY ...) - umożliwia pobranie "strony" wyników

Spis zapytań

30Jan/111

101 przykładów zapytań w NHibernate 3.x – HQL, Criteria API, QueryOver i Linq – Złączenia

Wprowadzenie

Wpis ten jest częścią artykułu poświęconemu wykonywaniu zapytań w NHibernate przy użyciu różnych sposobów jakie udostępnia ten mapper - HQL, Criteria API, QueryOver i Linq.

Część ta opisuje łączenie tabel przy użyciu klauzuli JOIN.

Spis zapytań

24Jan/111

101 przykładów zapytań w NHibernate 3.x – HQL, Criteria API, QueryOver i Linq – Projekcje

Wprowadzenie

Wpis ten jest częścią artykułu poświęconemu wykonywaniu zapytań w NHibernate przy użyciu różnych sposobów jakie udostępnia ten mapper - HQL, Criteria API, QueryOver i Linq.

Część ta opisuje stosowanie projekcji w zapytaniach. Jeżeli piszesz instrukcję SELECT i chcesz określić jakie kolumny lub jakie przekształcenia na kolumnach chcesz wykonać, możesz użyć składni SQL-a w następujący sposób:

SELECT NazwaKolumny, funkcja_przekształcająca(NazwaKolumny) FROM NazwaTabeli

Ponieważ dane uzyskiwane w wyniku wykonania zapytania prawie zawsze różnią się od danych mapowanych do encji, stosuje się klasy transformujące wyniki na obiekty DTO. W przypadku HQl-a, Criteria API stosuje się konstrukcję SetResultTransformer(Transformers.AliasToBean(typeof(NazwaKlasyDto))), w przypadku QueryOver - TransformUsing().

Spis zapytań

6Jan/112

101 przykładów zapytań w NHibernate 3.x – HQL, Criteria API, QueryOver i Linq – Sortowanie

Wprowadzenie

Wpis ten jest częścią artykułu poświęconemu wykonywaniu zapytań w NHibernate przy użyciu różnych sposobów jakie udostępnia ten mapper - HQL, Criteria API, QueryOver i Linq.

Część ta opisuje stosowanie sortowania danych w zapytaniach. Jeżeli piszesz instrukcję SELECT zachodzi czasem konieczność uporządkowania danych, które są zwracane w wyniku zapytania. Ogólnie rzecz biorąc stosujemy w SQL słowo ORDER BY wraz z opcjonalnym ASC|DESC oznaczającym kierunek sortowania. Koncepcję użycia przedstawiam poniżej:

SELECT Co FROM NazwaObiektu ORDER BY NazwaColumny [ASC|DESC], NazwaInnejKolumny [ASC|DESC]

Spis zapytań

31Dec/100

101 przykładów zapytań w NHibernate 3.x – HQL, Criteria API, QueryOver i Linq – Restrykcje

Wprowadzenie

Wpis ten jest częścią artykułu poświęconemu wykonywaniu zapytań w NHibernate przy użyciu różnych sposobów jakie udostępnia ten mapper - HQL, Criteria API, QueryOver i Linq.

Część ta opisuje stosowanie restrykcji w zapytaniach. Jeżeli piszesz instrukcję SELECT i chcesz określić w niej warunek, musisz użyć słowa kluczowego WHERE w podstawowej formule w następujący sposób:

SELECT NazwyKolumn FROM NazwaTabeli WHERE Warunek

Wyrażenia używane w warunku są budowane przy użyciu operatorów algebraicznych, logicznych i ciągów znakowych. W tym wpisie zostały przedstawione przykłady użycia klauzuli WHERE.

Spis zapytań

27Dec/102

101 przykładów zapytań w NHibernate 3.x – HQL, Criteria API, QueryOver i Linq.

Wprowadzenie

Postanowiłem stworzyć ten artykuł ponieważ nie mogłem odnaleźć w necie całościowego kompendium wiedzy w temacie odpytywania bazy danych przez NHibernate. Ponieważ NHibernate udostępnia wiele sposobów zadawania pytań, moim celem jest umieszczenie ich w jednym miejscu. Wśród nich wyróżnić można HQL, criteria query API wraz z rozszerzeniem Lambda Extensions, QueryOver oraz Linq for NHibernate (NHLQ). Jednym z moich założeń jest stworzenie zbioru zapytań na coś w rodzaju 101 Linq Samples. Nie wydaje mi się, żebym był w stanie odwzorować jeden do jednego to, co znajduje się na MSDNie, ale przynajmniej będę miał materiał referencyjny do którego będę mógł się odnieść.

Zapytania będę dodawać sukcesywnie oraz uaktualniać do nich odnośniki.

Wymagania

Aby móc wykonywać w miarę sensowne zapytania postanowiłem zamapować ulepszoną wersję Northwinda, którą opisałem i umieściłem w poprzednim poście. Oczywiście jeżeli chodzi o wymagane oprogramowanie jest to co najmniej Visual Studio 2008 oraz MS-SQL 2005.

Podział zapytań

  1. Restrykcje (klauzula WHERE)
    • Prosty przykład
    • Wyrażenia <, <=, ==, !=, >, >=
    • Wyrażenie Between
    • Wyrażenie In
    • Wyrażenie Like
    • Wyrażenie Like bez uwzględniania wielkości liter
    • Wyrażenie Is Null
    • Wyrażenie Not
    • Wyrażenie Exists / IsNotEmpty
    • Wyrażenie Not Exists / IsEmpty
    • Łączenie wyrażeń (koniunkcja i alternatywa - AND i OR)
    • Wyrażenia z aliasem
  2. Projekcje
    • Prosty przykład
    • Funkcje agregujące (MAX, AVG)
    • Wywołanie funkcji MS-SQL ROUND
    • Funkcja COUNT(*)
    • Funkcja COUNT(NazwaKolumny)
    • Funkcja COUNT DISTINCT
  3. Sortowanie (klauzula ORDER BY)
    • Prosty przykład
    • Sortowanie po długości wartości
    • Sortowanie po kilku kolumnach
    • Sortowanie po kilku kolumnach przez alias (różne tabele)
  4. Złączenia (klauzula JOIN)

    • Złączenie przez CreateCriteria()
    • Złączenie przez alias
    • Definiowanie typu złączenia
  5. Dzielenie danych
    • Pobranie pierwszych wyników - instrukcja TOP
    • Pobranie określonej "strony" - instrukcja TOP i ROW_NUMBER OVER
  6. Podzapytania
  7. Grupowanie

Projekt

Do pobrania już wkrótce!

Bibliografia

27Dec/100

NHibernate 3.x i FluentNHibernate: Płynne mapowanie – Północny wiatr (Northwind)

Wprowadzenie

Celem niniejszego wpisu jest wykonanie mapowania bazy danych Northwind przy użyciu ORM-a jakim jest NHibernate w wersji 3.x. Dodatkowo, zamiast pisać xml-e konfiguracyjne posłużymy się FluentNHibernate. Model domenowy będzie stanowić całkiem niezłe podwaliny pod serię artykułów na temat pisania dedykowanych zapytań, które chcę popełnić w niedalekiej przyszłości. Za zgodą Davy Briona zmieniłem mapowania lekko zmodyfikowanej, myślę że również oklepanej bazy danych Northwind. Oryginalny projekt wraz z objaśnieniami można znaleźć pod adresem http://davybrion.com/blog/2007/07/nhibernate-mapping-examples/.

W międzyczasie chciałem iść troszeczkę na łatwiznę i zabawić się z generatorem MyGeneration. Niestety do Fluenta jeszcze nie ma szablonu :(. Wprawdzie do mapowań w xmlu dobry jest szablon o nazwie L-99, jednakże przerobienie go na wersję generującą płynne mapowania byłoby czasochłonne, więc odłożę tą pracę  na później :)

Wymagania

Do przebrnięcia przez artykuł potrzebne będzie Visual Studio co najmniej 2008 oraz co najmniej Ms Sql Server Express 2005.

Mapujemy!

W poniższych kilku krokach postaram się w miarę zwięźle przedstawić procedurę przenoszenia modelu relacyjnego z bazy danych na encje w postaci mapowań do NHibernate. Ponieważ projekt Davy'ego był dla starszej wersji NHibernate (1.2), wypadałoby uaktualnić ją do nowszej - 3.x

  1. Źródła do NHibernate wraz z towarzyszącymi bibliotekami log4neta oraz  proxy znajdują się w repozytorium na sourceforgu: http://sourceforge.net/projects/nhibernate/files/NHibernate/3.0.0.GA/.
  2. FluentNHibernate dostarcza już pierwsze binarki dla wersji 3.x, które dostępne są na stronie projektu: http://fluentnhibernate.org/downloads.
  3. Łączymy się z bazą danych i wykonujemy skrypt tworzący schemat bazy danych wraz ze stowarzyszonymi elementami (skrypt northwind_create_script.sql).
  4. Otwieramy projekt Davy'ego i modyfikujemy go:
    • Usuwamy wszystkie pliki mapowań (z rozszerzeniem *.hbm.xml), albo ustawiamy we wszystkich "Build Action" na "None"
    • Konfigurujemy ustawienia połączenia z bazą danych
    • W projekcie Domain dodajemy katalog Mappings oraz tworzymy odpowiednie mapowania.
  5. W temacie mapowań nie będę się rozpisywał. Zachęcam do przeglądnięcia dokumentacji Fluenta. W celu weryfikacji możemy ewentualnie porównać pliki hbm-ów wygenerowanych przez Fluenta  z tymi Davy'a.
  6. Uruchamiamy testy.

Warto wspomnieć również o przyjętych konwencjach Fluenta. Można zdefiniować, aby domyślnie każdy identyfikator encji miał jeden spójny sposób mapowania. Realizuje się to poprzez implementację interfejsu IIdConvention , a następnie wskazanie na tą konwencję podczas konfiguracji. Oczywiście nic nie stoi na przeszkodzie, aby konwencję nadpisać w mapowaniu konkretnej encji.

public class IdentityConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        instance.Access.ReadOnlyPropertyThroughLowerCaseField(LowerCasePrefix.Underscore);
        instance.UnsavedValue("-1");
        instance.GeneratedBy.Native();
    }
}

Gotowy projekt jest do pobrania tutaj.

IIdConvention
6Jul/100

PLS-00201: identifier must be declared

Przyczyny powstania błędu o kodzie PLS-00201: identifier must be declared mogą być różne. Sprawdzenie następujących kroków może przyczynić się do znalezienia rozwiązania (http://www.dbforums.com/oracle/1207142-pls-00201-identifier-ordsys-ordimage-must-declared.html):

Cause:
An attempt was made to reference either an undeclared variable, exception, procedure, or other item, or an item to which no privilege was granted or an item to which privilege was granted only through a role.

Action:

Check your spelling and declaration of the referenced name.

Verify that the declaration for the referenced item is placed correctly in the block structure.

If the referenced item is indeed declared but you do not have privileges to refer to that item, for security reasons, you will be notified only that the item is not declared.

If the referenced item is indeed declared and you believe that you have privileges to refer to that item, check the privileges; if the privileges were granted only via a role, then this is expected and documented behavior.

Stored objects (packages, procedures, functions, triggers, views) run in the security domain of the object owner with no roles enabled except PUBLIC. Again, you will be notified only that the item was not declared.

Najczęściej pomaga zdefiniowanie (publicznego) synonimu przy użyciu magicznego zaklęcia:

create or replace public synonym nazwa for schemat.nazwa;
23Mar/100

Generowanie identyfikatora Guid dla Oracle

Próba przeniesienia procedur i tabel z MS-SQLa do Oracla nie zawsze musi byc prosta. Wynika to z faktu, że nie wszystkie typy Oracle są wprost kompatybilne z typami oferowanymi przez engine MSSQLa. Poniższe przykłady mają na celu ułatwienie prób konwersji typu Guid obecnego w MSSQLu oraz platformie .NET, na typ oferowany przez Oracle. Owym typem jest po prostu typ RAW, którego długość wynosi 16 bajtów, czyli ostatecznie RAW(16). Konstrukcja .NETowego Guida określa sztywny format, z jakim mamy do czynienia, tj. nawiasy sześcienne, myślnik oraz grupy liczb zapisanych w formacie heksagonalnym. Więcej o jego budowie można doczytać na stronach MSDNu.
W przypadku Oracle, zapisywane dane są wartościami heksadecymalnymi, bez dodatkowych ubarwień, tj. myśników czy też nawiasów.

Przykład 1. Kod wrzucający wartość .NETowego Guida do Oraclowego Raw-a.

public static void InsertGuid(Guid id)
{
 using (OracleConnection connection = new OracleConnection(ConnectionString))
 {
 const string Query = @"INSERT INTO MESSAGE (MessageId) VALUES (:id);";

 OracleCommand cmd = new OracleCommand(Query);
 cmd.CommandType = CommandType.Text;
 cmd.Connection = connection;
 cmd.Parameters.Add(":id", OracleType.Raw, 16).Value = id.ToByteArray();

 try
 {
 connection.Open();
 cmd.ExecuteNonQuery();
 }
 catch (OracleException exception)
 {
 throw  new InvalidOperationException("Couldn't insert the id", exception);
 }
 finally
 {
 connection.Close();
 }
 }
}

Wyjaśnienie:
W powyższym fragmencie kodu mamy do czynienia ze statyczną metodą, pobierającą Guid jako parametr. Dalej tworzymy połączenie do bazy danych przy użyciu Oracle providera. Nastepnie do obiektu typu OracleCommand dodajemy treść zapytania oraz przypisujemy paramter ":id", którego wartość pobierana jest z Guida jako tablica bajtów. Kolejnym i ostatnim krokiem jest wykonanie ExecuteNonQuery, której celem jest uruchomienie zapytania.

Przykład 2. Metoda pobierająca dane z z bazy.

public static Guid GetGuid(int length)
{
 Guid guid = Guid.Empty;

 using (OracleConnection connection = new OracleConnection(ConnectionString))
 {
 const string Query = @"SELECT TOP 1 MessageId WHERE length = :length;";

 OracleCommand cmd = new OracleCommand(Query);
 cmd.CommandType = CommandType.Text;
 cmd.Connection = connection;
 cmd.Parameters.Add(":length", OracleType.Number).Value = length;

 try
 {
 connection.Open();
 using(OracleDataReader reader = cmd.ExecuteReader())
 {
 reader.Read();
 guid = new Guid((byte[])reader[0]);
 }
 }
 catch (OracleException exception)
 {
 throw  new InvalidOperationException("Couldn't get the message", exception);
 }
 finally
 {
 connection.Close();
 }
 }

 return guid;
}

Wyjaśnienie:
Przykład bazujący na tym, ze zaznaczamy jedną wartość Guid dla wiadomości (z tąd też top 1 w zapytaniu). Oczywiście może to być zupełnie inne i dowolne zapytanie. Celem tego bloku kodu jest pokazanie w jaki sposób można pobrać taki identyfikator. Ciekawostką jest to, że korzystamy z OracleDatReadera, a nie używamy ExecuteScalar().

Przykład 3. Insert rekordu z wygenerowaniem unikalnego identyfikatora.
Dla ms-sqla blok t-sqlowy wyglądałby tak:

DECLARE @MessageId uniqueidentifier;
SET @MessageId  = NEWID();
INSERT INTO Message (MessageId)
VALUES (@MessageId);
SELECT @MessageId;

Dla Oracle razem z "opakowaniem" procedura wygląda tak:

public static Guid InsertEmptyMessage()
{
 using (OracleConnection connection = new OracleConnection(ConnectionString))
 {
 const string Query = @"declare r_id raw(16); " +
 "begin :r_id := SYS_GUID; " +
 "INSERT INTO MESSAGE (MessageId) " +
 "VALUES (:r_id); end;";

 OracleCommand cmd = new OracleCommand(Query);
 cmd.CommandType = CommandType.Text;
 cmd.Connection = connection;
 cmd.Parameters.Add(":r_id", OracleType.Raw, 16).Direction = ParameterDirection.Output;

 try
 {
 connection.Open();
 cmd.ExecuteNonQuery();
 object guid = cmd.Parameters[0].Value;
 return new Guid((byte[])guid);
 }
 catch (OracleException)
 {
 throw;
 }
 finally
 {
 connection.Close();
 }
 }

 return Guid.Empty;
}
Tagged as: , No Comments