Hibernate i efektywne sekwencje

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.

VN:F [1.9.8_1114]
Ocena: 0 (liczba ocen: 0)

Autor: Daniel Ramotowski

Odpowiedz

(required)