Anotace a Hibernate Aleš Nosek, Ondřej Vadinský, Daniel Krátký
Anotace v Javě Anotace jsou novinkou v Javy verze 5. Anotace umožňují doplnit kód Javy o dodatečné informace. Zapisují se přímo do zdrojového kódu programu a jsou v tomto podobné komentářům JavaDoc. Za předchůdce anotací můžeme považovat systém XDoclet. Ten k zápisu doplňujících informací používal komentáře JavaDoc. Oproti Javadocu jsou však anotace mnohem silnějším nástrojem. JavaDoc informace se totiž dají použít pouze v čase kompilace zdrojového kódu. Například slouží k vygenerování popisných XML souborů apod. Informace obsažené v anotacích lze oproti JavaDocu použít i za běhu programu. K přístupu k nim se používá mechanizmus reflexe. Ukázka použití anotací Tady je jednoduchá ukázka využití anotací za běhu programu. V anotaci Greet se uloží text pozdravu, který program vypíše: @Greet(text="Hi all") public class Main { public static void main(String[] args) { System.out.println(new Main().getClass().getAnnotation(Greet.class).text()); } } Jak je vidět tak anotace a jejich použití je velice snadné a může pomoci při vytváření vlastních aplikací. Jedno z použití může být například generování metody toString(). Kromě vlastních anotací existují můžeme využít i předdefinovaných anotací, které jsou součástí Java API: @Deprecated programátor by neměl takto označený element používat kompilátor vypíše varování „The method deprecatedMethod() from the type A is deprecated.“ @Override označená metoda má předefinovat metodu předka pokud to nedělá (chyba programátora) kompilace skončí chybou „method does not override or implement a method from a supertype“ @SupressWarning
pro daný element se nemá generovat specifický warning Životnost anotací Životnost anotace může být podle dokumentace nasledující: • • •
CLASS - anotace jsou uloženy v class souboru pomoci kompilátoru, ale nejsou ponechané virtuálním strojem pro přístup runtime. RUNTIME - to samé jako v předchozím případě, ale JVM tyto anotace poskytuje i pro potřeby runtime. SOURCE - pokud je definována tato životnost, tak je daná anotace kompilátorem “zničena”, proto není ani v class souboru a nemůže být tudíž ani přitomna runtime.
To jakou bude mít určitá anotace životnost se určuje jejím anotováním. (kromě tříd, metod, polí lze totiž anotovat i samotné anotace).
Objektově relační mapování Při implementaci objektově relačního mapování v Javě se používají tyto tři hlavní způsoby: 1. Použití externího souboru – jedná se obvykle o XML deskriptor obsahující metadata pro ORM mapování, nastavení databáze apod. Tyto soubory lze generovat i automaticky např. podle schematu přímo z databáze. Nevýhodou je, že jich může být někdy velký počet. 2. Použití JavaDoc komentářů – použijí se zvláště definované tvary komentářů, které se předzpracují pomocí nějakého nástroje. Například XDoclet projde zdrojové soubory a podle značek, které do nich programátor zapsal, vytvoří mapování pro databázi. Nevýhodou tohoto přístupu je nutnost jednoho kroku navíc při překladu aplikace. 3. Poslední možností je moderní přístup v podobě anotací. Zápis mapování se podobně jako u XDocletu provádí přímo do souboru se zdrojovým kódem. Při úpravách aplikace nebo refactoringu se alespoň snáze zachová aktuálnost mapování. Informace uložené v anotacích jsou přístupné za běhu programu pomocí mechanizmu reflexe. Není proto potřeba žádný externí XML soubor jako u prvního zmiňovaného přístupu. Navíc mají anotace podporu v různých nástrojích jakou jsou vývojová prostředí IntelliJ IDEA nebo Eclipse. Tyto IDE dokáží zvýrazňovat syntaxi nebo doplňovat kód.
Anotace v Hibernate Hibernate 3 umí anotace používat, informace pro OR mapování tedy nemusí být uloženy v konfiguračních souborech, ale mohou být přímo součástí zdrojového kódu programu. Hibernate vychází z anotací EJB, které v některých případech rozšiřuje. Anotace EJB se však stále ještě vyvíjí, proto se i zápis anotací pro Hibernate ještě může měnit. Pro použití anotací v Hibernate je potřeba mít Hibernate verze 3.2 a vyšší a samozřejmě Javu 5. K projektu se kromě standardních knohoven Hibernate přilinkuje knihovna hibernate-annotations.jar a dále lib/ejb3-persistence.jar (Java Persistence API). Zdrojový kód programu začíná získáním Hibernate session factory:
sessionFactory = new AnnotationConfiguration().buildSessionFactory(); Samozřejmě je stále nutný popisovač perzistentních tříd, který je obvykle uložen v souboru hibernate.cfg.xml:
<session-factory> <mapping class="PlaneType"/> <mapping class="ModelPlane"/>
Mapování persistentních tříd Persistentní třídy jsou tídy s privátními datovými atributy a příslušnými get/set metodami, které se ukládají do databáze. Get/set metody jsou důležité pro správnou funkčnost hibernate. Pokud není žádoucí, aby byly tyto metody veřejné (typicky atribut id), mohou být private. Anotace upravují mapování persistentních tříd do tabulek v databázi a jsou součástí zdrojového kódu dané třídy, jak je ukázáno v následujícím příkladu: @Entity public class ModelPlane { private Long id; @Id public Long getId() { return id; } public void setId(Long { this.id = id; } }
id)
Pro určení nepersistentních atributů slouží anotace @Transient. Takto označený atribut se nebude ukládat ani načítat z databáze. Všechny ostatní atributy jsou automaticky chápány jako persistentní. Anotace @Entity označuje persistentní třídu, třída se tedy bude ukládat do databáze.
@Id pak označuje primární klíč, který odlišuje jednotlivé záznamy v tabulce. Následuje příklad, který ilustruje způsob generování primárních klíčů. K tomuto účelu se využívá anotace @GeneratedValue následující anotaci @Id: @Id @GeneratedValue(strategy=GenerationType.AUTO) public Long getId() { return id; } GenerationType.AUTO deklaruje automatické generování primárního klíče. Persistentní třída může obsahovat anotaci @Table(name=“TABLE“). Tato anotace následuje @Entity a určuje jméno tabulky, do které se bude třída mapovat. Jednotlivé get metody může předcházet anotace @Column(name=“COLUMN“). Určuje jméno sloupce v tabulce, do kterého se bude ukládat hodnota atributu vraceného metodou get. Následující příklad vše ilustruje: @Entity @Table(name="T_MODEL_PLANE") public class ModelPlane { private Long id; private String name; @Id @Column(name="PLANE_ID") public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Column(name="PLANE_NAME") public String getName() { return name; } public void setName(String name) { this.name = name; } } Kromě vlastnosti name=“NAME“ je možné použít mnoho dalších, které upřesňují další vlastnosti daného sloupce. Výčet vlastností anotace @Column následuje:
@Column( name="columnName"; boolean unique() default false; boolean nullable() default true; boolean insertable() default true; boolean updatable() default true; String columnDefinition() default ""; String table() default ""; int length() default 255; int precision() default 0; int scale() default 0; ) Lze tedy ovlivnit, zda je sloupec unikátní, nulavatelný, zda do něj lze vkládat hodnoty, nebo zda lze hodnoty aktualizovat.
Mapování vztahů mezi tabulkami Mapování vztahů mezi tabulkami přes cizí klíče je velmi častý jev. Hibernate je pro tento případ velmi dobře a flexibilně připraven. Anotace pro základní vztahy jsou: @OneToOne @OneToMany @ManyToOne @ManyToMany Jednou z těchto anotací, uvedenou nad nadpisem get metody daného atributu informujeme Hibernate právě o tom, že jde o cizí klíč. Povinným údajem je potom informace o chování při kaskádových operacích, tj. při operacích s entitami zatíženými constraintami. Jako typy tohoto chování se uvádí výčtové typy třídy CascadeType: ALL, PERSIST, MERGE, REMOVE, REFRESH, DELETE, SAVE_UPDATE, REPLICATE, DELETE_ORPHAN, LOCK a EVICT K dané anotaci můžeme přidat informaci o daném sloupci pomocí anotace @JoinColumn(name="COLUMN") Čili ne pouze anotaci @Column, která se uvádí u obyčejných sloupců. Příklad: @Entity public class Flight implements Serializable { private Company company;
@ManyToOne( cascade = CascadeType.PERSIST, CascadeType.MERGE} ) @JoinColumn(name="COMP_ID") public Company getCompany() { return company; } } Tedy u get metody, která vrací instanci třídy Company, která je také persistentní, ale v tabulce entity Flight je uveden pouze její klíč. Tento klíč je definován ve sloupci COMP_ID. Pokud je tato get metoda zavolána, Hibernate podle cizího klíče nejdříve vyhledá příslušnou entitu, vytvoří instanci její třídy a naplní ji daty. Tuto instanci pak metoda vrací.
Named Query Named query slouží k definování vlastních SQL dotazů. Je zde tedy prostor pro přesažení standardního rámce nabízeného Hibernatem, možnost například vyhledávat v souvislostech z různých tabulek nesvázaných potřebnými constraintami (ať už z jakéhokoli důvodu). Named query se ukládá do třídy entity jako anotace třídy (tj. nad hlavičkou třídy) a sestává z těchto anotací: @NamedQueries @NamedQuery Jak je zřejmé na první pohled, první anotace uzavírá množinu těch druhých. Každá Named query pak musí mít jméno, podle kterého se volá a samotný dotaz. Příklad: @NamedQueries( { @NamedQuery( name="planeType.findById", query="select p from PlaneType p left join p.modelPlanes where id=:id" ), @NamedQuery( name="planeType.findAll", query="select p from PlaneType p" ), @NamedQuery(
fetch
name="planeType.delete", query="delete from PlaneType where
id=:id"
) } ) Jak je vidět, do Named query se dosazují různé parametry, určující výsledek dotazu. Strind planeId = "FOO" Query query = em.createNamedQuery("planeType.delete"); query.setParameter("id",planeId); List
- items = query.getResultList(); Kde "em" je javax.persistence.EntityManager. List výsledků bude v konkrétním případě mazání prázdný.
Seznam Literatury http://www.hibernate.org/hib_docs/annotations/reference/en/html_si ngle/ http://www.onjava.com/pub/a/onjava/2007/02/08/an-introduction-tohibernate-3-annotations.html http://www.scream.cz/2007/10/28/jemny-uvod-do-anotaci/ http://java.sun.com/j2se/1.5.0/docs/api/ http://en.wikipedia.org/wiki/Hibernate_%28Java%29