Jednym z ogromnych plusów wykorzystywania w swoim projekcie biblioteki Hibernate jest możliwość użycia języka zapytań HQL. Jego wielką zaletą jest niezależność zapytań od wykorzystania konkretnego systemu zarządzania bazą danych. Niestety ta zaleta bywa równocześnie wadą w sytuacji, w której chcielibyśmy wywołać jakąś funkcję specyficzną dla danego dostawcy. Na całe szczęście, dzięki dialektom, nie trzeba tworzyć natywnych zapytań, żeby móc korzystać z możliwości naszego dostawcy bazy danych.

Przykładowo, mając schemat bazy danych złożony z następujących tabel:

create table if not exists person(
	id integer primary key not null auto_increment,
	first_name varchar(256) not null,
	last_name varchar(256) not null
);
create table if not exists phone(
	id integer primary key not null auto_increment,
	number varchar(16) not null,
	type varchar(16) not null,
	person_id integer not null,
	foreign key (person_id) references person(id)
);

może przytrafić się nam potrzeba zwrócenia zgrupowanego wyniku z listą osób oraz listą telefonów połączonych za pomocą przecinków. Korzystając z natywnego zapytania (tu i w dalszej części artykułu przyjmijmy, że korzystamy z bazy MySQL) możliwe jest wykorzystanie funkcji group_concat:

select first_name, last_name, group_concat(type, ': ', number separator ', ')
from person
join phone on phone.person_id = person.id
group by person.id;

W pierwszej kolejności należy stworzyć odpowiednię implementację dialektu, najlepiej rozszerzając istniejący dialekt:

import org.hibernate.dialect.MySQLDialect;

public class ExtendedMySQLDialect extends MySQLDialect {
	(...)
}

Kolejnym krokiem będzie dodanie konstruktora bezparametrowego, który wywoła bazowy konstruktor i zarejestruje dodatkową funkcję:

public ExtendedMySQLDialect() {
	super();
	registerFunction("group_concat", new VarArgsSQLFunction("group_concat(", ",", " separator ', ')"));
}

W związku z tym, że funkcja group_concat przyjmuje zmienną liczbę argumentów, wykorzystałem obiekt klasy org.hibernate.dialect.function.VarArgsSQLFunction, który jest dużo bardziej generyczny w stosunku do org.hibernate.dialect.function.StandardSQLFunction. Pozwala on na określenie początku funkcji (czyli nazwa z nawiasem otwierającym), separatora argumentów (czyli przecinek) oraz zakończenia (czyli separator dla funkcji i nawias zamykający).

Aby móc skorzystać z nowo utworzonego dialektu, wystarczy określić go w konfiguracji Hibernate:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="hibernate.dialect">pl.wercia.example.dialect.ExtendedMySQLDialect</property>
		(...)
	</session-factory>
</hibernate-configuration>

W efekcie, dopuszczalne będzie teraz użycie funkcji group_concat w zapytaniach HQL:

SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
try {
	List<?> results = session.createQuery("select person.firstName, person.lastName,"
			+ " group_concat(phone.type, ': ', phone.number) from Person as person"
			+ " join person.phones as phone group by person.id").list();
	(...)
}
finally {
	session.close();
}

Oczywiście nic nie stoi na przeszkodzie, aby zarejestrować w ten sposób inne funkcje dostępne w używanej bazie danych. Włącznie ze zdefiniowanymi samodzielnie (np. za pomocą PL/SQL).

Przykładowy projekt: