Archiv pro štítek: Java

Seznámení s Hibernate ORM

V tomto článku si popíšeme základy technologie Hibernate ORM pro komunikaci Javy s relačními databázemi. Osvěžíme si znalosti o tom, co je to ORM a jak se popisují vztahy mezi jednotlivými entitami v datovém modelu. Letmo se zmíníme, jaký vztah je mezi Java Persistence API (JPA) a Hibernate. Na závěr se ve stručnosti seznámíte s použitím Hibernate v Identity Manageru CzechIdM.

Hibernate ORM

Hibernate ORM je framework v jazyce Java, umožňující objektově-relační mapování (ORM).

ORM je programovací technika, která umožňuje ukládat data objektově orientovaných jazyků do relační databáze. Vybraným Java třídám pak přísluší databázové tabulky, přičemž javovské datové typy se konvertují na SQL datové typy. ORM zpracovává také vztahy mezi objekty, které do relačních databází ukládáme. V databázi jsou pak vztahy objektů reprezentovány spojováním tabulek a cizími klíči.

Hibernate implementuje Java Persistence API. Můžeme ho tedy používat buď specificky – tak, že voláme přímo jeho metody -, nebo obecně přes rozhraní definované JPA.

Hibernate podporuje mnoho databázových dialektů. Umožňuje pracovat například s MySQL (přičemž můžeme odlišit, zda používá engine InnoDB či MyISSAM), PostgreSQL, Oracle, Microsoft SQL Server aj.

Hibernate dále vyvíjí speciální jazyk pro psaní dotazů do databáze přímo z kódu, tzv. Hibernate Query Language (HQL). HQL je inspirován syntaxí SQL, takže je dobře použitelný pro každého, kdo zná základy SQL dotazů.

Entita v Javě

Entita je samostatný typ objektu, který má své specifické vlastnosti a jehož instance jsou jednoznačně identifikovatelné.

Pro Hibernate je entita jedna Java třída, jejíž instance se ukládají jako záznamy do jedné databázové tabulky. Každá instance entity má svůj identifikátor, který se obvykle použije jako primární klíč do tabulky.

Entitní třída musí mít konstruktor bez parametrů, protože pomocí něj Hibernate vytváří objekty při načítání z databáze. Členské proměnné třídy jsou zpravidla private, používají se pro ně standardně konstruované gettery a settery. Načítání hodnot členských proměnných z databáze se může provádět pomocí lazy-loading, proto se doporučuje přistupovat k proměnným pomocí getterů (které vynutí načtení celé hodnoty).

Persistence entit

Ukládání instancí entit do databáze se provádí dvojím způsobem, v závislosti na tom, zda používáme způsob specifický pro Hibernate, nebo jeho implementaci JPA.

V prvním případě si pomocí org.hibernate.SessionFactory (objekt, který se vyskytuje v jedné instanci pro celou aplikaci) vytvoříme objekt typu org.hibernate.Session, v rámci kterého provedeme „jednotku práce“ – jednu operaci v databázi:

org.hibernate.Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(nejakaEntita);
session.getTransaction().commit();
session.close();

V druhém případě použijeme javax.persistence.EntityManagerFactory, kterou dodává JPA v závislosti na konfiguraci naší aplikace, a pomocí ní vytvoříme objekt typu javax.persistence.EntityManager. Ten provádí jednotku práce podobně jako v předchozím způsobu:

javax.persistence.EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
entityManager.persist(nejakaEntita);
entityManager.getTransaction().commit();
entityManager.close();

Konfigurace

Základní konfigurační soubor pro Hibernate se nazývá hibernate.cfg.xml.

V tomto souboru definujeme na prvním místě dialekt databáze, aby Hibernate používal správné datové typy a správnou syntaxi SQL:

<hibernate-configuration>
  <session-factory>
    <property name=“dialect“>org.hibernate.dialect.MySQL5InnoDBDialect</property>

V konfiguračním souboru nesmí chybět identifikace spojení do databáze. Pokud zadáme všechny potřebné údaje pro navázání JDBC spojení, Hibernate si vytvoří JDBC connection pool, který mu bude poskytovat spojení tak, jak bude třeba. Místo toho můžeme zadat existující javax.sql.DataSource registrovaný v JNDI, tedy spojení, které je spravováno naším aplikačním serverem.

<property name=“connection.datasource“>java:/CzechIdMDatasource</property>

(Komunita Hibernate doporučuje používat pro produkční prostředí spíše tento způsob. Upozorňují, že správu vlastního JDBC connection pool ještě Hibernate nezvládá úplně bez chyb).

Další důležitou vlastností je hbm2ddl.auto. Tato vlastnost určuje, co se děje s databázovým schématem ve chvíli vzniku a zániku SessionFactory, tj. při startu a skončení aplikace. Možné hodnoty jsou „update“ (při startu aktualizuje schéma na základě aktuálního nastavení), „validate“ (při startu ověří schéma, ale změny nedělá), „create“ (při startu vytvoří nové schéma, čímž přepíše původní data), „create-drop“ (při skončení smaže schéma, při startu ho znovu vytvoří).

V konfiguračním souboru jsou dále uvedeny odkazy na mapovací soubory.

Pokud používáme obecné rozhraní JPA, konfigurační soubor je META-INF/persistence.xml. Zde definujeme poskytovatele persistence, spojení do databáze a další konfigurační vlastnosti. Ty vlastnosti, které jsou určeny pro Hibernate, uvedeme s předponou „hibernate.“.

<persistence-unit name=“CzechIdM“>
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <jta-data-source>java:/CzechIdMDatasource</jta-data-source>
  <properties>
    <property name=“hibernate.dialect“ value=“org.hibernate.dialect.MySQL5InnoDBDialect“/>
    ...

Mapovací soubory (Hibernate)

V mapovacích souborech definujeme mapování entit na databázové tabulky. Nejlépe to ukážeme na příkladu:

<hibernate-mapping>
  <class name="org.jbpm.graph.def.Node" table="JBPM_NODE">
    <id name="id" column="ID_"><generator class="native" /></id>
    <property name="name" column="NAME_"/>
    <property name="description" column="DESCRIPTION_" type="longstring" length="4000"/>
    ...

Atribut „name“ u elementu „class“ obsahuje FQN (Fully qualified name) třídy, která je entitou. Atribut „table“ obsahuje název tabulky, kam se její instance budou ukládat.
Následuje výčet sloupců.

Element „id“ definuje sloupec, který unikátně identifikuje řádek v tabulce. Nemusí to být primární klíč, ale je to obvyklé. Uvnitř elementu „id“ může být ještě definována strategie generování identifikátorů.

Element <property name="description" column="DESCRIPTION_" type="longstring" length="4000"/> definuje proměnnou „description“ mapovanou na sloupec „DESCRIPTION_“, přičemž v atributu „type“ je uveden konvertor (Hibernate mapping type), který má Hibernate použít pro překlad z Java datového typu na databázový typ. Pokud není konvertor uveden, používá se Java reflection a defaultní mapování z javovských typů na databázové typy. Není-li uveden atribut „column“, použije se pro název sloupce přímo název proměnné.

Anotace (JPA)

Místo mapovacích souborů se dají použít anotace přímo u entitních tříd a jejich členských proměnných. Názvy anotací odpovídají názvům vlastností v mapovacích souborech. Entita pak vypadá například takto:

@Entity
@Table(name = "identities", uniqueConstraints = {
  @UniqueConstraint(columnNames = { "name" }) })
public class Identity extends ViewMainEntity {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "id")
  private int id;

  @SignedField
  @Index(name = "name_index")
  @Column(name = "name", length = NAME_LENGTH)
  private String name;
  ...

Výhodné je, že při použití anotací vidíme ze zdrojového kódu na první pohled, jakým způsobem se entita ukládá.

Anotace jsou součástí standardu JPA. Pokud bychom tedy chtěli změnit způsob persistence dat do databáze, nemusíme při používání anotací měnit nic v kódu.

Vztahy mezi entitami

Silným nástrojem Hiberate ORM je popis vztahů mezi entitami. Každé dvě entity spolu mohou být v nějakém vztahu (relaci), přičemž jedna strana je vlastníkem relace a druhou nazýváme „inverzní strana“.

Vlastník relace je zodpovědný za udržování konzistence v datech na obou stranách relace. Při definici relace určíme pomocí CascadeType, jaké typy operací se mají v rámci relace propagovat. Například CascadeType.REMOVE způsobuje, že smaže-li se jedna strana relace, smaže se i druhá strana.

V databázi se vztah entit realizuje cizími klíči mezi jednotlivými tabulkami, přičemž cizí klíče jsou vydefinované v tabulce vlastníka relace.

Základní typy relací jsou Many-to-one resp. One-to-many, One-to-one a Many-to-many. Každý vztah si nyní ukážeme na příkladu z datového modelu CzechIdM spolu s ukázkami kódu.

Many-to-one & One-to-many

Situace: Každá identita patří právě do jedné organizace, přičemž do každé organizace může patřit více identit.

  • Entita Identity – vlastník relace
    @ManyToOne
    @JoinColumn(name = "home_organisation")
    private Organisation homeOrganisation;
  • Entita Organisation
    @OneToMany(mappedBy = "homeOrganisation", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
    private List<Identity> identities;
    

Vazba: V databázové tabulce „identities“ je vytvořen speciální sloupec „home_organisation“, který obsahuje identifikátor (primární klíč) domovské organizace identity.

Pokud bychom na straně organizace nepotřebovali znát seznam identit, které do ní patří, mohli bychom úplně vynechat proměnnou „identities“ a jakýkoliv odkaz na to, že mezi entitami Identity a Organisation je nějaký vztah. Relaci udržuje entita Identity. CascadeType bychom pak samozřejmě museli přidat do kódu k identitě.

One-to-one

Situace: Každá identita má právě jednu konfiguraci.

  • Entita Identity – vlastník relace
    @OneToOne(optional = false, cascade = CascadeType.ALL)
    @JoinColumn(name = "configuration")
    private IdentityConfigurations identityConfigurations;
    
  • Entita IdentityConfigurations
    @OneToOne(mappedBy = "identityConfigurations")
    private Identity identity;
    

Vazba: V databázové tabulce „identities“ je vytvořen speciální sloupec „configuration“, který obsahuje primární klíč konfigurace identity z tabulky „identity_configurations“.

Opět platí to, že kdyby entita IdentityConfigurations nepotřebovala znát referenci na identitu, k níž patří, můžeme celý atribut vynechat.

Při relaci One-to-one je možné definovat mapování speciálně pomocí primárního klíče. K tomu stačí použít anotaci @PrimaryKeyJoinColumn namísto @JoinColumn(name = ...). Pokud instance obou entit mají stejný identifikátor, Hibernate dokáže entity spojovat na základě tohoto identifikátoru. Výhodou je, že v databázové tabulce vlastníka relace není třeba vytvářet navíc žádný sloupec. Nevýhodou naopak to, že identifikátor instancí pak nelze snadno měnit, protože se vyskytuje ve více tabulkách.

Many-to-many

Situace: Jedna role může opravňovat uživatele k účtům na více koncových systémech (schématech). K jednomu schématu může opravňovat více rolí.

  • Entita Role – vlastník relace
    @ManyToMany
    @JoinTable(name = "roles_schemas", 
    		joinColumns = @JoinColumn(name = "role"),
    		inverseJoinColumns = @JoinColumn(name = "resource_schema"))
    private List<Schema> schemas;
  • Entita Schema
    @ManyToMany(mappedBy = "schemas")
    private List<Role> roles;

Vazba: Vytvoří se nová databázová tabulka „roles_schemas“, která udržuje informace o mapování rolí a schémat. Sloupec „role“ zde obsahuje identifikátor role a sloupec „resource_schema“ obsahuje identifikátor schématu (obojí jsou primární klíče).

Hibernate a CzechIdM

Identity Manager CzechIdM používá Hibernate ORM pro persistenci veškerých dat do databáze.

Standardní entity, které jsou součástí zdrojového kódu CzechIdM (identity, organizace, role, systémy,…), používají JPA a anotace. Konfigurační soubor pro persistenci entit je umístěn v CzechIdM-ejb.jar/META-INF/persistence.xml. Pokud bychom se v budoucnu rozhodli přejít na jiný ORM framework, ve zdrojovém kódu by to znamenalo žádné, nebo jen minimální úpravy.

CzechIdM používá jako workflow engine technologii jBPM. Tato technologie ukládá svá data do relační databáze také pomocí Hibernate ORM, přičemž používá přímo specifické metody Hibernate. Konfigurační soubor pro jBPM je umístěn v CzechIdM-ejb.jar/jbpm.hibernate.cfg/hibernate.cfg.mysql.xml a jednotlivé mapovací soubory pro entity jBPM jsou umístěny v knihovně jbpm-jpdl.jar. Výhodou tohoto přístupu je, že jsme mohli provést drobné změny v konfiguraci ukládání entit jBPM do databáze, aniž bychom museli znovu sestavovat celou knihovnu jBPM ze zdrojových kódů.

Spojení CzechIdM s databází udržuje aplikační server JBoss. V obou konfiguračních souborech je tedy uveden odkaz na tentýž datasource (java:/CzechIdMDatasource). Můžeme tedy konfigurovat či optimalizovat spojení s databází pro oba persistentní moduly zároveň.

Závěr

Tento článek obsahuje základy použití technologie Hibernate ORM zejména z pohledu Java vývojáře. Zdaleka jsme nepokryli všechny otázky, které by zvídavého čtenáře napadly, ale nic vám nebrání zaslat je na můj e-mail alena.peterova@bcvsolutions.eu!

Optimalizace jBPM enginu – uchovávání informací o workflow v databázi

jbpm

V CzechIdM využíváme workflow pro vykonávání aplikační logiky systému. Všechna tato workflow běží na enginu jBPM. O každém běžícím workflow si jBPM v standardně uchovává spousty informací jako jsou proměnné, aktuální stav apod. Tyto informace si ukládá do databáze a zůstávají v ní i poté, co dané workflow skončí, což v určitých případech způsobuje nárůst velikosti databáze. Proto jsme hledali způsob optimalizace jBPM.

Dosud jsme v případě velkého nárůstu databáze řešili ručním promazáním příslušných jBPM tabulek. Nyní jsme ho ale vyřešili sofistikovanějším způsobem, o kterém se právě dozvíte v tomto článku.

Pokračování textu

Jak zprovoznit prostředí pro vývoj portletů a jak vytvořit portlet pro Liferay

V tomto článku najdete podrobný návod, jak si zprovoznit prostředí pro vývoj portletů s pomocí nástrojů společnosti Liferay, a jak vytvořit jednoduchý portlet. Celé zprovoznění nezabere více než jednu hodinu. Na konci budete mít k dispozici pískoviště pro zkoušení všech svých nápadů týkajících se (nejen) portletů a Liferay portálu.

Pokračování textu

Kariéra: Hledáme kolegu Java vývojáře

Stále rozšiřujeme náš tým. Momentálně hledáme Java vývojáře, kteří s námi budou implementovat Identity Management CzechIdM pro naše zákazníky. Ve spolupráci s dalšími členy týmu budou pomáhat při analýze, návrhu a vývoji SW; rozšiřovat jeho funkce na přání zákazníka. Každého proškolíme, je ale potřeba znalost programovacího jazyka Java.

Pokračování textu

CzechIdM – Java – E-maily s přílohou

Minulý týden jsem naučil náš Identity Manager CzechIdM odesílat e-maily s přílohami. Situací, ve kterých se to může hodit, je celá řada: předvyplněný formulář v PDF k tisku, konfigurační soubor k nově zřízené VPN, certifikát nebo třeba aktuální verze telefonního seznamu. V článku vás rychle s novou funkcí seznámím, naučím vás ji používat a společně se podíváme, jak CzechIdM tvoří instanci třídy MimeMessage, která se pro odesílání e-mailu z Javy standardně používá.

email

Pokračování textu

JavaDoc tipy & triky

Každý se s tím zajisté již mnohokrát setkal. Pročítat si cizí kód a dokonce i vlastní bývá velice obtížné. Nikdy nevíme co se programátorům honilo hlavou, když psali zrovna takovou podmínku. Co je vedlo k tomu, že je cyklus omezený právě takhle? Spousty podobných otázek si kladl každý z nás. Já sám jsem se v takové situaci ocitl přesně ve chvíli, kdy jsem se stal novým členem týmu BCVsolutions s.r.o. Již v prvních dnech jsem si musel pročítat stovky řádků kódu a ještě  rozsáhlejší dokumentaci.

sourceCode

Pokračování textu

Propojení Javy s C++

Jako Java programátor se čas od času potýkám s následující situací – mám k dispozici program, který bych chtěl použít v rámci svého vlastního projektu, ale brání mi v tom jedna malá drobnost – tento program je napsaný v C++. Varianty použít pouze jediný programovací jazyk (tj. celé to napsat v Javě nebo C++) jsou mimo diskusi, obě části už jsou dosti rozsáhlé a znamenalo by to začít v novém jazyce od nuly. Přitom jediné co by stačilo, je spuštění jedné nebo dvou funkcí napsaných v C++ programu z mého Javovského kódu.

Java umožňuje pomocí JNI (Java Native Interface) volání nativních metod ze sdílených knihoven. V tomto článku je popsán jednoduchý způsob použití prográmku SWIG (Simplified Wrapper and Interface Generator) pro automatické vygenerování všech potřebných komponent pro vytvoření sdílené knihovny a jejího použití v Javě.

java-logo
Pokračování textu

Identity konektor pro Alfresco – SOAP nebo RESTful?

V tomto příspěvku si ukážeme, jakým způsobem lze připojit systém Alfresco k našemu Identity Manageru CzechIdM. Dopředu prozradím, že si k tomuto účelu navrhneme a naimplementujeme speciální Identity connector. Nebudeme si zde podrobně rozebírat implementaci konetoru jako například v případě Universal SSH konektoru, ale podíváme se na tento problém z poněkud jiného pohledu. Ukážeme si, jak postupujeme při zákazníkově požadavku na připojení nějakého nového systému k CzechIdM a jak jsme postupovali v tomto případě.

Pokračování textu