Pokazywanie postów oznaczonych etykietą hibernate. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą hibernate. Pokaż wszystkie posty

niedziela, 17 stycznia 2010

SQuirreL SQL Hibernate plugin

SQuirreL SQL jest moim ulubionym klientem SQL. Umożliwia on również, wykonywanie zapytań HQL. Wtyczka udostępniające tę funkcjonalność dostępna jest w wersji 0.01, ale działa całkiem przyzwoicie. Podpowiada nazwy obiektów oraz ich właściwości. Umożliwia tłumaczenie zapytania HQL na zapytanie SQL oraz jego wykonanie.

Ponieważ sporo trudności przedstawia jej konfiguracja, opisze ją na przykładzie aplikacji wykorzystującej Spring Framework. Zakładam, że połączenie SQuirreL SQLa do bazy danych jest już skonfigurowane.

Wtyczka dystrybuowana jest wraz z instalacyjną wersją klienta SQuirreL SQL. Fakt jej zainstalowania można zweryfikować w menu Plugins/Summary.


Zgodnie z dokumentacją w pliku plugins/hibernate/readme.html wtyczce musimy dostarczyć obiekt klasy SessionFactoryImpl wskazując metodę o sygnaturze
public org.hibernate.impl.SessionFactoryImpl getSessionFactoryImpl()
która go udostępni.

Ponieważ moja aplikacja korzysta ze Springa, obiekt SessionFactoryImpl najlepiej pobrać z jego kontekstu. Klasa implementująca powyższą metodę wygląda zatem:

package pl.matt.hibernate;

import org.hibernate.impl.SessionFactoryImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class SessionFactoryProvider {

public org.hibernate.impl.SessionFactoryImpl getSessionFactoryImpl() {
try {
ApplicationContext ac = new FileSystemXmlApplicationContext("//home/mateusz/priv/workspace/blog/resources/application-context.xml");
SessionFactoryImpl out = (SessionFactoryImpl) ac.getBean("sessionFactory");
return out;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

}

Blok try-catch jest zbędny, ale ułatwia diagnozowanie problemów przy uruchamianiu wtyczki pod warunkiem uruchomienia klienta SQuirreL SQL z wiersza poleceń.
Ścieżka do pliku ze Springowym kontekstem aplikacji poprzedzona jest podwójnym ukośnikiem, gdyż pierwszy z nich jest wycinany przez Springa.

To tyle jeżeli chodzi o kodowanie. Czas na wskazanie powyższej klasy we właściwościach SQuirreL SQLa. Otwieram zakładkę Hibernate z menu File/Global Preferencess, gdzie tworzę nową konfigurację przyciskiem New.

Wpisuję jej nazwę a następnie konfiguruję ścieżkę klas przyciskiem Add classpath entry. Wskazana ścieżka musi zawierać zarówno skompilowane klasy aplikacji jak i wymagane przez nią biblioteki.

Następnie zaznaczam opcję: Invoke the user defined provider method below i wskazuję napisaną niedawno klasę.


To tyle. Po naciśnięciu przycisku connect na zakładce Hibernate możemy już wykonywać zapytania HQL. Polecam zaznaczenie opcji Always format (formatowanie wynikowego SQLa) oraz Execute SQL (bez niej HQL zostanie jedynie przetłumaczony do SQLa) i można się już mierzyć z HQLem.

niedziela, 10 stycznia 2010

Hibernate NamingStrategy

Na jednym z ostatnich spotkań WJUGa pożaliłem się odrobinie osobie siedzącej po mojej prawej stronie na Hibernate. Były to żale bardziej estetyczne niż funkcjonalne a dotyczyły nazw generowanych przez to właśnie narzędzie ORM. Nie podobały mi się ani nazwy tabel ani kolumn ani kluczy obcych jakie nadaje Hibernate podczas tworzenia schematu bazy danych. Osoba słuchająca moich marudzeń powiedziała krótko i fachowo: "Obczaj naming strategy". Obczaiłem.

Domyślnie Hibernate tworzy nazwę tabeli taką samą jak nazwa klasy - encji, natomiast nazwę kolumny taką samą jak nazwa pola. Nazwy kluczy obcych generowane są w sposób nieokreślony: np. 'FK2F894B2DC512614D'.

Ja chciałbym, aby nazwy kolumn i tabel utrzymane były w konwencji z podkreśleniem (czyli np. user_data, default_color), nazwy tabel występowały w liczbie mnogiej, a nazwy kluczy obcych były jakkolwiek zrozumiałe (np. FK_EMP_COM dla klucza łączącego tabelę EMPLOYEES z COMPANIES).

Pierwsze spojrzenie na interfejs NamingStrategy pozbawiło mnie złudzeń. O czytelnych nazwach kluczy obcych mogę sobie pomarzyć. Wybrana strategia stosowana jest tylko do nazw kolumn i tabel. Dobre i to.

Szybki przegląd implementacji:

DefaultNamingStrategy
EJB3NamingStratefy
DefaultComponentSafeNamingStratefy
ImprovedNamingStrategy

wykazał, że trzy pierwsze odpadają w przedbiegach. Konwencja wielbłądzia zachowana. Z klasy UserData.java tworzy się tabela UserData.

Cała nadzieja w ImprovedNamingStrategy. Konwertuje ona nazwy na konwencję z podkreśleniem... i tyle. O liczbie mnogiej mogę póki co pomarzyć. Nie tylko z resztą ja więc chyba skończy się na napisaniu swojej implementacji.

Nazwy kluczy obcych pozostają nie ruszone. Nawet nie nagryzione. Jeżeli wiesz, jak można je zmienić, daj koniecznie znać.

niedziela, 12 kwietnia 2009

leniwe ładowanie właściwości w Hibernate

Święta, święta i... jeszcze został jeden dzień obżarstwa. Tu barszczyk, tam kiełbaska, mięsko, sałatka, mazurek. Wszystkiego trzeba spróbować. A gdyby tak jeść tylko to, co jest w danej chwili niezbędne? Po prostu nie przejadać się. Chyba byłoby zdrowiej...

To takie moje świąteczne przemyślenia. W programowaniu jednak jest trochę jak w życiu. Często mamy do czynienia z podobnymi sytuacjami. Nie zjadamy co prawda w naszych programach wszystkiego, co jest pod ręką, ale wyciągając z bazy danych obiekty przy użyciu narzędzi ORM (Object Relational Mapping), pobieramy je wyciągając wszystkie kolumny z bazy danych. Inicjujemy wszystkie, nawet te niepotrzebne w danej chwili właściwości obiektów.

O ile zazwyczaj nikomu to nie przeszkadza, o tyle w przypadku pobierania właściwości o dużych rozmiarach (np. duże obiekty binarne, tekstowe) może to się odbić czkawką i negatywnie wpłynąć na wydajność aplikacji.

Na szczęście Hibernate, będący chyba najpopularniejszym narzędziem mapowania obiektowo relacyjnego, umożliwia selektywne pobieranie właściwości obiektów. Wyboru, które kolumny pobieramy wyciągając obiekt z relacyjnej bazy danych dokonujemy za pomocą parametru fetch adnotacji @Basic. Jeżeli konfigurację przejścia obiektowo relacyjnego przechowujemy w plikach XML, za to zachowanie odpowiedzialny jest atrybut lazy znacznika property.

Domyślnie pobierane są wszystkie atrybuty obiektów, nie będące kolekcjami, co odpowiada ustawieniom:

@Basic(fetch = FetchType.EAGER)
oraz

<property lazy="false" />

Sprawdźmy więc w akcji, jak działa konfiguracja leniwego pobierania wybranych właściwości obiektów w Hibernate. Mój przykładowy program powstał na podstawie przykładów ze strony projektu Hibernate.

Pobierał będę encję Message

package pl.matt.model;

public class Message {
private Long id;
private String text;

Message() {}

public Message(String text) {
this.text = text;
}

public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}

public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}


konfigurację przejścia obiektowo relacyjnego zawarłem w pliku XML Message.hbm.xml


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class
name="pl.matt.model.Message"
table="MESSAGES">

<id
name="id"
column="MESSAGE_ID"
>
<generator class="native"/>
</id>

<property
name="text"
column="MESSAGE_TEXT"
lazy="false"
/>

</class>

</hibernate-mapping>


Na prosty program pobierający obiekt z bazy danych składa klasa Main

package pl.matt.main;

import org.hibernate.Session;
import org.hibernate.Transaction;

import pl.matt.model.Message;
import pl.matt.utils.HibernateUtil;

public class Main {

public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

Message message = (Message) session.get(Message.class, 1l);
System.out.println(message.getId());

tx.commit();
session.close();

HibernateUtil.shutdown();
}
}


Klasa narzędziowa HibernateUtil zawiera kilka metod ułatwiających pracę z Hibernate:

package pl.matt.utils;

import org.hibernate.*;
import org.hibernate.cfg.*;

/**
* Startup Hibernate and provide access to the singleton SessionFactory
*/
public class HibernateUtil {

private static SessionFactory sessionFactory;

static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}

public static SessionFactory getSessionFactory() {
// Alternatively, we could look up in JNDI here
return sessionFactory;
}

public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}


W pliku hibernate.cfg.xml konfiguruję połączenie z bazą danych HSQL oraz ustawiam wartość true parametru show_sql który umożliwia podglądanie zapytań SQL wykonywanych przez Hibernate.


<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>

<property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="hibernate.connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="hibernate.connection.username">sa</property>

<!-- SQL to stdout logging -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="use_sql_comments">true</property>

<property name="hibernate.hbm2ddl.auto">update</property>

<property name="dialect">org.hibernate.dialect.HSQLDialect</property>

<mapping resource="pl/matt/model/Message.hbm.xml"/>

</session-factory>
</hibernate-configuration>


Uruchamiam program i zgodnie z oczekiwaniami widzę zapytanie pobierające 2 właściwości z tabeli MESSAGES:

Hibernate:
/* load pl.matt.model.Message */ select
message0_.MESSAGE_ID as MESSAGE1_0_0_,
message0_.MESSAGE_TEXT as MESSAGE2_0_0_
from
MESSAGES message0_
where
message0_.MESSAGE_ID=?


zmieniam w pliku Message.hbm.xml wartość atrybutu lazy na true. Uruchamiam ponownie program i tym razem niezgodnie z oczekiwaniami widzę ponownie:

Hibernate:
/* load pl.matt.model.Message */ select
message0_.MESSAGE_ID as MESSAGE1_0_0_,
message0_.MESSAGE_TEXT as MESSAGE2_0_0_
from
MESSAGES message0_
where
message0_.MESSAGE_ID=?


Hmm... słabo.
Zagłębiłem się w dokumentację i już widzę, że nie będzie łatwo:

Note

To enable property level lazy fetching, your classes have to be instrumented: bytecode is added to the original one to enable such feature, please refer to the Hibernate reference documentation. If your classes are not instrumented, property level lazy loading is silently ignored.

Nawet nie wiedziałem, że jest taki czasownik instrument. W każdym razie moje ustawienia zostały po cichu zignorowane, jak to ładnie określiła dokumentacja Hibernate. Trzeba by to zmienić. Szperam więc dalej w poszukiwaniu sposobu zinstrumetowania klas.

Wykonać ten proces można antowym zadaniem:

<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
<classpath path="${build.dir}"/>
<classpath refid="project.classpath"/>
</taskdef>

<instrument verbose="true">
<fileset dir="${build.dir}/pl/matt/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>


W dokumentacji Hibernate jest mały błąd, gdyż klasa zadania instrument InstrumentTask nie znajduje się w pakiecie org.hibernate.tool.instrument tylko w org.hibernate.tool.instrument.cglib.

Jako, że powyższe zadanie zmienia kod plików *.class, muszę uruchamiać te skompilowane i zmienione przez ANTa pliki. Posłużę się do tego celu poniższym plikiem build.xml:

<project name="hibernateLazyBasic" default="compile" basedir=".">

<!-- Name of project and version -->
<property name="proj.name" value="hibernateLazyBasic"/>
<property name="proj.shortname" value="hibernateLazyBasic"/>
<property name="version" value="1.0"/>

<!-- Global properties for this build -->
<property name="database.dir" value="database"/>
<property name="src.java.dir" value="src/java"/>
<property name="lib.dir" value="lib"/>
<property name="build.dir" value="build"/>

<!-- Classpath declaration -->
<path id="project.classpath">
<fileset dir="${lib.dir}">
<include name="**/*.jar"/>
<include name="**/*.zip"/>
</fileset>
</path>

<!-- Useful shortcuts -->
<patternset id="meta.files">
<include name="**/*.xml"/>
<include name="**/*.properties"/>
</patternset>

<!-- Clean up -->
<target name="clean" description="Clean the build directory">
<delete dir="${build.dir}"/>
<mkdir dir="${build.dir}"/>
</target>

<!-- Compile Java source -->
<target name="compile">
<mkdir dir="${build.dir}"/>
<javac srcdir="${src.java.dir}"
destdir="${build.dir}"
classpathref="project.classpath"/>
</target>

<!-- Copy metadata to build classpath -->
<target name="copymetafiles">
<mkdir dir="${build.dir}"/>
<copy todir="${build.dir}">
<fileset dir="${src.java.dir}">
<patternset refid="meta.files"/>
</fileset>
</copy>
</target>

<target name="run-instrumented" depends="clean, instrument, copymetafiles">
<java fork="true"
classname="pl.matt.main.Main"
classpathref="project.classpath">
<classpath path="${build.dir}"/>
</java>
</target>

<target name="run" depends="clean, compile, copymetafiles">
<java fork="true"
classname="pl.matt.main.Main"
classpathref="project.classpath">
<classpath path="${build.dir}"/>
</java>
</target>

<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.cglib.InstrumentTask">
<classpath path="${build.dir}"/>
<classpath refid="project.classpath"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${build.dir}/pl/matt/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>

</project>


zadanie o nazwie run-instrumented kompiluje, przeprowadza instrumentację klas z pakiedu pl.matt.model i uruchamia program.

Po jego wykonaniu, otrzymuję na konsoli:

[java] Hibernate:
[java] /* load pl.matt.model.Message */ select
[java] message0_.MESSAGE_ID as MESSAGE1_0_0_
[java] from
[java] MESSAGES message0_
[java] where
[java] message0_.MESSAGE_ID=?


Ładowane jest tylko pole ID z tabeli MESSAGES, czyli zgodnie z oczekiwaniami. Kolumna MESSAGE_TEXT pobierana jest z bazy osobnym zapytaniem:

[java] Hibernate:
[java] /* sequential select
[java] pl.matt.model.Message */ select
[java] message_.MESSAGE_TEXT as MESSAGE2_0_
[java] from
[java] MESSAGES message_
[java] where
[java] message_.MESSAGE_ID=?

dopiero wtedy, kiedy będzie potrzebna.

Generalnie wszystko działa, tylko trzeba odrobinę zmienić proces kompilacji programu.
Kod źródłowy przedstawiający powyższe rozwiązanie znajdziesz tutaj

a że święta już w połowie, pozostaje mi życzyć Wam mokrego dyngusa.

czwartek, 16 października 2008

Moc dialektów Hibernate

Od czasu do czasu mam przyjemność przeprowadzania rozmów rekrutacyjnych z kandydatami do pracy. Osobom, które deklarują znajomość Oracle PL/SQLa i przebrną przez pierwsze pytanie dotyczące sekwencji (a 2/3 znawców PL/SQLa nie wie, co to jest sekwencja) zadaję następujące zadanko:

Przedstawiam encję Osoby:


i proszę o napisanie zapytania pokazującego 3 najmniej zarabiające osoby. Jeżeli kandydat zna również MySQL, proszę o napisanie tego zapytania w 2 wersjach. Bajer polega na tym, że w MySQLu do ograniczenia listy wyników do trzech wystarczy wykorzystać klauzulę limit:

SELECT * FROM OSOBY ORDER BY OSOBY.PENSJA LIMIT 3;


podczas gdy w Oracle takiej magicznej klauzuli nie ma. Jest tam za to możliwość wyświetlenia wierszy o określonym numerze (indeksie):

SELECT * FROM OSOBY WHERE NUMROW <= 3 ORDER BY OSOBY.PENSJA;

ale numer wiersza jest sprzed sortowania, zatem powyższe zapytanie wyświetli trzech pierwszych użytkowników posortowanych po wielkości pensji. Aby osiągnąć zamierzony cel, trzeba skorzystać z podzapytania:

SELECT * FROM (SELECT * FROM OSOBY OSOBY ORDER BY OSOBY.PENSJA ) WHERE NUMROW <= 3;

Zapytanie o to samo, a jednak inaczej skonstruowane (a niby SQL to język deklaratywny).
Zaczęło mnie zastanawiać, co na to Hibernate. Czy jest na tyle mądry, że potrafi to samo zapytanie HQLowe zapisać na 2 zupełnie różne sposoby? Czy jego dialekty, ograniczają się jedynie do podmiany słów kluczowych i nazw typów, czy są mechanizmem dużo potężniejszym? Przekonajmy się.

Na bazie aplikacji Hibernate Getting Started - Hello World stworzyłem prosty projekt w środowisku Eclipsie. Skasowałem pliki Message.java oraz Message.hbm.xml.

Stworzyłem za to klasę Person:

public class Person {
private Long id;
private String name;
private int salary;

public Person() {

}

public Person(String text, int salary) {
this.name = text;
this.salary = salary;
}

public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}
public void setName(String text) {
this.name = text;
}

public int getSalary() {
return salary;
}

public void setSalary(int salary) {
this.salary = salary;
}

}

oraz plik Person.hbm.xml odzwierciedlający zawartość klasy Person w bazie danych:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="hello.Person" table="PEOPLE">

<id name="id" column="PERSON_ID">
<generator class="native">
</generator>
</id>

<property name="name" column="NAME" type="string" />

<property name="salary" column="SALARY" type="integer" />

</class>

</hibernate-mapping>

Skonfigurowałem w pliku hibernate.cfg.xml połączenie z bazą danych MySQL:

<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>

<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

<property name="hibernate.connection.username">test</property>
<property name="hibernate.connection.password">test</property>

<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>

<!-- SQL to stdout logging -->
<property name="show_sql">true</property>

<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="hello/Person.hbm.xml" />
</session-factory>
</hibernate-configuration>

Dzięki parametrowi
<property name="hibernate.hbm2ddl.auto">update</property>
schemat bazy danych zostanie dostosowany do aplikacji (zostaną utworzone lub zmodyfikowane odpowiednie tabele) przy jej starcie.
Natomiast parametr
<property name="show_sql">true</property>
pozwala wyświetla w konsoli zapytań SQL, które wykonuje Hibernate.

Parametr
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
określa tzw. dialekt Hibernate, który odpowiada za tłumaczenie zapytań Hibernate na zapytania SQLowe zrozumiałe dla konkretnego systemu zarządzania bazami danych.

Do tego zmieniłem zawartość metody main() klasy HelloWorld:

public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Collection people = session.createCriteria(Person.class).list();
if (people.size() < 5) {
Transaction tx = session.beginTransaction();

Person p1 = new Person("Adam", 100);
Person p2 = new Person("Marek", 200);
Person p3 = new Person("Adrian", 150);
Person p4 = new Person("Konrad", 180);
Person p5 = new Person("Romek", 120);

session.persist(p1);
session.persist(p2);
session.persist(p3);
session.persist(p4);
session.persist(p5);

tx.commit();
}

Query query = session.createQuery("FROM Person p ORDER BY p.salary").setMaxResults(3);
List list = query.list();
for (Person person : list) {
System.out.println(person.getName() + " " + person.getSalary());
}

session.close();
HibernateUtil.shutdown();
}

Na początek prostsze zadanie - zapytanie do bazy MySQL. Odpalenie aplikacji kończy się spodziewanym wynikiem:

Hibernate: select person0_.PERSON_ID as PERSON_ID0_, person0_.NAME as NAME0_,
person0_.SALARY as SALARY0_ from PEOPLE person0_ order by person0_.SALARY limit ?
Adam 100
Romek 120
Adrian 150

Czas więc na uruchomienie aplikacji z bazą Oracle. Na początku, trzeba zmienić odrobinę plik Person.hbm.xml tak, aby unikalny identyfikator osoby generowany był z sekwencji:

<id name="id" column="PERSON_ID">
<generator class="sequence">
<param name="sequence">people_seq</param>
</generator>
</id>

oraz plik hibernate.cfg.xml, konfigurując połączenie z bazą Oracle:

<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@127.0.0.1:1521/xe</property>
<property name="dialect">org.hibernate.dialect.OracleDialect</property>

<property name="hibernate.connection.username">test</property>
<property name="hibernate.connection.password">test</property>


<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>

<!-- SQL to stdout logging -->
<property name="show_sql">true</property>

<property name="hibernate.hbm2ddl.auto">update</property>

<mapping resource="hello/Person.hbm.xml" />

</session-factory>
</hibernate-configuration>

Uruchamiam aplikację i ...

Hibernate: select * from
( select person0_.PERSON_ID as PERSON_ID0_, person0_.NAME as NAME0_,
person0_.SALARY as SALARY0_ from PEOPLE person0_ order by person0_.SALARY )
where rownum <= ?
Adam 100
Romek 120
Adrian 150

Czyli bez niespodzianki. Dialekty Hibernate są więc dość potężnym mechanizmem. Pozwalają nie tylko na zmianę pojedynczych słów kluczowych, ale także konstrukcji całych zapytań. Trudno się zresztą dziwić. Hibernate to już bardzo dopracowany produkt.

Niech no mi się teraz nawinie ktoś na rozmowie rekrutacyjnej, ze znajomością Oracle PL/SQLa i Hibernate...