Do napisania kilku słów na temat optymalizacji używania generatorów w Hibernate 3 natchnął mnie kolega z pracy Krzysiek – prawdziwy specjalista Oracle i tropiciel motocyklistów w białych pantoflach
(użyłem określenia “tropiciel”, w celu podkreślenia swobodnego charakteru tej publikacji i zwiększenia percepcji treści merytorycznych wśród czytelników; ma to oczywiście związek z pewnym zabawnym wydarzeniem ale nie o tym będzie tutaj mowa).
Kiedyś pracowaliśmy z Krzyśkiem przy tym samym projekcie, który intensywnie wykorzystywał JPA w implementacji Hibernate 3 oraz bazę danych Oracle.
Kolega zwrócił właśnie uwagę na mało wydajny sposób w jaki Hibernate wstawia rekordy do bazy danych:
- wykonuje polecenie select, które pobiera identyfikator z sekwencji,
- wstawia właściwy rekord z pobranym wcześniej identyfikatorem.
Krzysiek był na tyle szybki, że zaraz po tym przedstawił propozycję rozwiązania problemu: zastosowanie strategii generowania identyfikatorów org.hibernate.id.SequenceIdentityGenerator.
Co to jest i jak wpływa na wydajność?
Jest to specyficzna dla Hibernate implementacja obsługi sekwencji bazodanowych, która wykorzystuje funkcjonalność sterowników JDBC zgodnych ze specyfikacja 3.0 (charakterystyczna funkcja getGeneratedKeys). Dzięki jej zastosowaniu Hibernate jest w stanie wstawić rekord do bazy danych w połączeniu z wygenerowaniem identyfikatora i tym samym wykonać 50% operacji mniej ![]()
Ograniczeniem zastosowania tego typu sekwencji jest wsparcie wspomnianej już specyfikacji JDBC 3.0 ze strony używanego sterownika.
Aby cieszyć się w pełni wydajnym wstawianiem danych, należy zadeklarować generator używając adnotacji @GenericGenerator. Stanowi ona rozszerzenie Hibernate i nie wchodzi w skład specyfikacji JPA. Adnotację możemy zastosować na pakiecie (w package-info.java), klasie lub atrybucie klasy.
Przykładowa deklaracja generatora może wyglądać następująco:
package pl.atena;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
@Entity
@Table(name="TBL_TEST")
public class Test {
@Id
@GeneratedValue(generator="jdbc3_generator")
@org.hibernate.annotations.GenericGenerator(
name="jdbc3_generator",
strategy="org.hibernate.id.SequenceIdentityGenerator"
)
@Column(name="ID")
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Jeżeli zależy nam na kodzie, który ma być przenośny miedzy kilkoma bazami danych, generator zoptymalizowaną pod kątem JDBC 3.0 należy umieścić w kodzie klasy (nie na poziomie pakietu). Tak skonstruowany kod w łatwy sposób nadpiszemy za pomocą pliku orm.xml np. przywracając domyślny mechanizm generowania sekwencji .
Przykład:
<?xml version="1.0" encoding="UTF-8"?>
<entity class="pl.atena.Test" metadata-complete="false">
<attributes>
<id name="id">
<generated-value strategy="AUTO" />
</id>
</attributes>
</entity>
Przedstawione informację są kwintesencją całego rozwiązania. Zachęcam osoby ciekawskie do zrobienia testów wydajności, ja tymczasem życząc wszystkim Wesołych Świąt oddaję się tropieniu tematów do kolejnych publikacji.
Autor: Daniel Ramotowski
