Kurs Hibernate – część 2

Zapraszam na szybki kurs Hibernate. Wspólnie stworzymy cztery proste aplikacje bazodanowe w języku Java wykorzystujące mapowanie obiektowo-relacyjne (Object-Relational Mapping, ORM) za pomocą frameworku Hibernate. W drugiej części kursu Hibernate mapowanie relacji między obiektami języka Java na tabele w bazie danych.

Dla znudzonych: Jeśli chcesz szybko uruchomić opisywaną w tej części kursu Hibernate aplikację bazodanową, pobierz plik .zip wskazany na końcu tego tekstu, a następnie zajrzyj do mojego artykułu o importowaniu istniejącego projektu z plikiem budowania Apache Ant do Eclipse.

Mapowanie relacji w Hibernate

W pierwszej części kursu Hibernate utworzyliśmy prostą klasę Autor opisującą autorów książek i określiliśmy mapowanie obiektowo-relacyjne między klasą Autor a tabelą AUTOR. W tej części kursu Hibernate dodamy klasę języka Java opisującą książki. Klasę nazwiemy Ksiazka i zapiszemy oczywiście w pliku Ksiazka.java. Książki będą opisywane przez pola: id zawierające unikalny identyfikator książki, tytul zawierające tytuł książki oraz autor zawierające autora książki. Oprócz pól klasa będzie zawierać konstruktor bezparametrowy oraz gettery i settery do wszystkich pól. Dokładny kod całej klasy przedstawiam poniżej.

package pl.iprogramujesz.bazydanych.hibernate;

public class Ksiazka {
	private Long id;
	private String tytul;
	private Autor autor;

	public Ksiazka() {
	}

	public Long getId() {
		return id;
	}

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

	public String getTytul() {
		return tytul;
	}

	public void setTytul(String tytul) {
		this.tytul = tytul;
	}

	public Autor getAutor() {
		return autor;
	}

	public void setAutor(Autor autor) {
		this.autor = autor;
	}
}

Podobnie jak w pierszej części kursu Hibernate, obiekty klasy Ksiazka będziemy przechowywać w bazie danych. Utworzymy w tym celu tabelę KSIAZKA o strukturze odpowiadającej zawartości klasy Ksiazka, czyli zawierającą kolumny ID, TYTUL i AUTOR. Kolumna AUTOR będzie zawierać identyfikator autora książki (klucz główny tabeli AUTOR). Mapowanie obiektowo-relacyjne między klasą Ksiazka a tabelą KSIAZKA określimy w pliku Ksiazka.hbm.xml o zawartości przedstawionej poniżej.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="pl.iprogramujesz.bazydanych.hibernate">

	<class name="Ksiazka" table="KSIAZKA">

		<id name="id" column="ID">
			<generator class="native"/>
		</id>

		<property name="tytul" column="TYTUL"/>

		<many-to-one name="autor" column="AUTOR"/>

	</class>

</hibernate-mapping>

W przeciwieństwie do poprzedniej części kursu Hibernate, gdzie wszystkie pola mapowanej klasy Autor były typu prostego, w tym przykładzie występuje pole autor klasy Ksiazka, które jest obiektem klasy Autor. Reprezentuje to zależność między klasami Ksiazka i Autor w języku Java oraz odpowiadającą jej zależność między tabelami KSIAZKA i AUTOR w bazie danych. W Hibernate do mapowania zależności tego typu służy element <many-to-one>. Parametr name zawiera nazwę pola autor mapowanej klasy Ksiazka, a parametr column nazwę odpowiadającej mu kolumny AUTOR tabeli KSIAZKA zawierającej klucz obcy tabeli opisującej klasę pola autor (czyli klucz obcy tabelki AUTOR).

Dzięki takiemu plikowi Ksiazka.hbm.xml framework Hibernate będzie umiał poprawnie pobierać obiekty klasy Ksiazka z tabeli KSIAZKA w bazie danych wraz z odpowiednim obiektem klasy Autor i poprawnie je zapisywać do bazy danych.

Warto byłoby jeszcze zmienić klasę Autor, tak aby dla obiektu tej klasy można było łatwo otrzymać zbiór powiązanych z nim obiektów klasy Ksiazka. Dokładny kod całej klasy można znaleźć w pliku .zip umieszczonym na końcu tego tekstu, a poniżej przedstawiam wprowadzone zmiany.

...
public class Autor {
	...
	private Set<Ksiazka> ksiazki = new HashSet<Ksiazka>();

	...

	public Set<Ksiazka> getKsiazki() {
		return ksiazki;
	}

	public void setKsiazki(Set<Ksiazka> ksiazki) {
		this.ksiazki = ksiazki;
	}
}

Odpowiednio musimy też zmienić mapowanie obiektowo-relacyjne w pliku Autor.hbm.xml. Poniżej przedstawiam wprowadzone zmiany.

...
	<class name="Autor" table="AUTOR">

		...

		<set name="ksiazki" table="KSIAZKA" inverse="true">
			<key column="AUTOR"/>
			<one-to-many class="Ksiazka"/>
		</set>

	</class>
...

Szkielet aplikacji bazodanowej z Hibernate

Konfiguracja Hibernate w pliku hibernate.cfg.xml nie wymaga zmian, podobnie jak klasa pomocnicza HibernateUtil. Zmienimy natomiast główną część aplikacji z poprzedniej części kursu Hibernate zamieniając klasę KursHibernate01 na klasę KursHibernate02 dokładnie przedstawioną poniżej.

package pl.iprogramujesz.bazydanych.hibernate;

import java.util.List;

import org.hibernate.Session;

public class TestHibernate02 {

	public static void printAutorzy() {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		List<Autor> autorzy = session.createQuery("from Autor").list();
		for(Autor a : autorzy) {
			System.out.println(a.getId() + "\t" + a.getImie() + "\t" + a.getNazwisko() + "\tksiazki:");
			for(Ksiazka k : a.getKsiazki()) {
				System.out.println("\t" + k.getId() + "\t" + k.getTytul());
			}
		}

		session.getTransaction().commit();
	}

	public static void printKsiazki() {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		List<Ksiazka> ksiazki = session.createQuery("from Ksiazka").list();
		for(Ksiazka k : ksiazki) {
			System.out.println(k.getId() + "\t" + k.getTytul() + "\t" + k.getAutor().getImie() + "\t" + k.getAutor().getNazwisko());
		}

		session.getTransaction().commit();
	}

	public static void main(String[] args) {
		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		Autor autor = new Autor();
		autor.setImie("Henryk");
		autor.setNazwisko("Sienkiewicz");
		session.save(autor);

		Ksiazka ksiazka = new Ksiazka();
		ksiazka.setTytul("W pustyni i w puszczy");
		ksiazka.setAutor(autor);
		session.save(ksiazka);

		session.getTransaction().commit();

		System.out.println("Test Hibernate 02");

		System.out.println("\nAutorzy:");
		printAutorzy();

		System.out.println("\nKsiazki:");
		printKsiazki();

		HibernateUtil.getSessionFactory().close();
	}
}

Uruchomienie aplikacji bazodanowej z Hibernate

Aplikacja opisana w tej części kursu Hibernate składa się z plików opisanych we wcześniejszych sekcjach: Autor.java, Autor.hbm.xml, Ksiazka.java, Ksiazka.hbm.xml, hibernate.cfg.xml, HibernateUtil.java, KursHibernate01.java.

Jako podsumowanie tej części kursu Hibernate, przygotowałem plik .zip zawierający całość aplikacji bazodanowej z Hibernate z mapowaniem relacji między obiektami. Jeśli nie wiesz jak szybko uruchomić pobraną aplikację przeczytaj mój artykuł o importowaniu istniejącego projektu z plikiem budowania Apache Ant do Eclipse.