środa, 3 czerwca 2009

@GeneratedValue oraz @SequenceGenerator - czyli sekwencje w hibernate

Tworzenie mojego projektu rozpocząłem od zdefiniowania bazy danych, tabel w bazie oraz odpowiednich relacji między tabelami, na koniec dodałem sekwencje. Wszystko wygląda pięknie, i tak w teorii działa.

Jak każdy programista jestem leniwy, zamiast tworzyć mapowania w moim IDE o wiele lepiej wygenerować je wprost z bazy. W IntelliJ robi się łatwo o ile znajdziemy odpowiednią opcję o czym pisałem jakiś czas temu.  Niestety szybko przekonałem się, że nie jest to idealne odwzorowanie moich tabel. Nie wiedzieć czemu IntelliJ zapomniał zaimportować sekwencje zadeklarowane w bazie, co skutkowało wystąpieniem szeregu błędów podczas prób zapisania danych w bazie, w szczególności wyróżnia się jeden:
org.springframework.web.util.NestedServletException: Request processing failed;
nested exception is org.springframework.orm.hibernate3.HibernateSystemException:
ids for this class must be manually assigned before calling save():
com.darekzon.coffeine.domain.Category;
nested exception is org.hibernate.id.IdentifierGenerationException:
ids for this class must be manually assigned before calling save():
com.darekzon.coffeine.domain.Category



Aby problemy przestały występować wystarczy z palca podpiąć sekwencje do konkretnego pola, robimy to przez zastosowanie adnotacji @GeneratedValue przy polu którego wartości mają być generowane przez sekwencje.

Kod wygląda następująco:
import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(schema = "public", name = "category")
public class Category implements Serializable {

private Long id;

@Id
@Column(name = "id", nullable = false, length = 19)
@SequenceGenerator(name = "category_seq", sequenceName = "category_id_seq")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "category_seq")
public Long getId() {
return id;
}

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

private String name;

@Basic
@Column(name = "name", nullable = false, length = 30)
public String getName() {
return name;
}

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

}

Pierwsze co robimy aby dodać generator to wskazanie która sekwencja generuje dane dla naszego pola, czyli adnotacja @SequenceGenerator,  podajemy dla niej dwie wartości, pierwsza to nazwa sekwencji jaka obowiązywać będzie w zakresie naszej klasy, natomiast druga to nazwa sekwencji w bazie.

Po skonfigurowaniu sekwencji mówimy hibernate-owi, że nasze -pole będzie otrzymywać automatycznie wartość korzystając z @GeneratedValue. Dla adnotacji @GeneratedValue musimy podać strategię tworzenia nowych wartości (z racji, że jest to sekwencja wybieramy SEQUENCE)  i oczywiście generator który te wartości będzie wskazywał, jako generator podajemy nazwę którą wcześniej wpisaliśmy w @SequenceGenerator.

Po tym zabiegu wszystko powinno działać jak trzeba.

Swoją drogą muszę się przestawić na tworzenie Entity Beanów a potem eksportowanie to do bazy danych, na pewno przyśpieszy to tworzenie projektu a przy okazji nie będę musiał tworzyć bazy danych (co uważam za zajęcie mało interesujące).

1 komentarz:

  1. Jedno ale:
    Jak nie dasz strategy = GenerationType.AUTO może się okazać, że Hibernate nie korzysta ze wskazanej sekwencji - i, z powodów, które są zupełnie niejasne i absolutnie tajemnicze, wartości klucza w tabeli zaczynają się od 5051 a sekwencja ani drgnie. Z drugiej strony wskazanie strategii AUTO, że ma korzystać z sekwencji użytkownika, a nie z ogólnej, na pierwszy rzut oka kompletnie nielogiczne, działa wyśmienicie.
    Czary...
    Pozdrawiam.
    Tarrin

    OdpowiedzUsuń