Hibernate i efektywne sekwencje

post_img

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.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *