Hibernate: Czy klasa @Entity do poprawnego działania musi posiadać konstruktor oraz settery dla pól?

Jeśli chodzi o konstruktor, to sprawa jest dość prosta. Standard JPA 2.0 wymaga bezparametrowego konstruktora. Jeśli mamy w klasie zdefiniowany konstruktor parametrowy, to musimy również wprost zdefiniować ten bezparametrowy. Jeżeli natomiast nie mamy żadnego konstruktora parametrowego, to nie musimy się o nic martwić – kompilator stworzy za nas konstruktor domyślny.

Jeśli chodzi o settery, to sprawa jest nieco bardziej skomplikowana. Wszystko zależy od tego, jakiej strategii dostępu do pól będzie używał Hibernate, a decydujemy o tym my – programiści, choć praca z ludźmi pokazuje, że często nieświadomie.

Zazwyczaj adnotację @Id umieszczamy nad polem klasy związanym z kluczem głównym:

@Entity
public class Person {

    @Id
    @GeneratedValue
    private long id;

    private String lastName;
    
    public String getLastName() {
        return lastName;
    }
    
}

W takim wypadku, Hibernate do odczytywania i zapisywania wartości atrybutów naszej klasy, będzie używał refleksji. Nie potrzebujemy więc definiować getterów oraz setterów dla pól (choć oczywiście jeśli ich potrzebujemy, to możemy to zrobić). Tę strategię możemy kojarzyć z pojęciem field-based access.

Jeśli jednak adnotację @Id umieścilibyśmy nad metodą getId(), a nie nad samym polem id, to settery będą wymagane dla wszystkich pól, które posiadają gettery. Jeśli bowiem chcemy mieć możliwość odczytywać wartość jakiegoś pola, to Hibernate musi mieć możliwość zapisania w nim jakiejś wartości. Tę strategię możemy kojarzyć z pojęciem property-based access.

@Entity
public class Person {

    private long id;

    private String lastName;

    @Id
    @GeneratedValue
    public long getId() {
        return id;
    }

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

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    
}

Jeśli w powyższym przykładzie nie zapewnilibyśmy settera dla pola lastName, to podczas startu aplikacji napotkalibyśmy następujący wyjątek:

  • org.hibernate.PropertyNotFoundException: Could not locate setter method for property [pl.rekrutacjajava.rj.person.Person#lastName].

Przy użyciu odpowiednich adnotacji, możemy teoretycznie mieszać obie powyższe strategie, aby dla jednych pól stosować pierwszą, a dla innych drugą. Wprowadzanie takiego braku spójności rzadko będzie jednak dobrym pomysłem.

 

PS. Odpowiedź na to pytanie dotyczy ściśle Hibernate i jego wymagań. Jeśli pytanie miałoby bardziej ogólny wydźwięk, to warto pamiętać, że brak getterów i setterów może mieć swoje konsekwencje również w innych miejscach, np. przy serializacji/deserializacji JSONa w Springowym controllerze.