Eines ORM
Per què necessitem ORM?
ORM = Object-Relational Mapping = Mapatge d'objectes relacional
A una aplicació tenim un model de domini: un model que representa els conceptes claus del domini d'un problema, i que conté tant dades com comportaments. Aquestes dades (Objecte) poden necessitar ser emmagatzemades (Relacional), i llavors necessitem un model de persistència.
Problema: la representació de dades en memòria (Objectes) i en BBDD (relacional) són molt diferents.
Aspectes a considerar per fer el mapatge:
- Les representacions en memòria són més flexibles.
- Si es fan canvis a una banda o altra, s'han de sincronitzar.
- Les dades a la BBDD poden ser modificades per moltes persones alhora.
- Les transaccions no poden estar obertes mentre les dades estan en memòria.
És poc factible dissenyar les dades en memòria sense pensar en la seva persistència, per tant és convenient dissenyar un model de domini més relacional. Fins i tot, per certes aplicacions, es pot tenir a memòria el model relacional.
Patrons de mapatge
Utilitzant patrons de disseny, podem resoldre situacions habituals que es donen a la programació de BBDD.
- Data Access Object: un objecte que proporciona una interfície abstracta a algun tipus de base de dades, i que permet aïllar la capa d'aplicació / negoci de la capa de persistència.
- Active Record: un objecte que embolica una fila en una taula o vista de la base de dades, encapsula l'accés a la base de dades i afegeix lògica de domini en aquestes dades.
Solucions de mapatge
JDBC és el model de persistència bàsic a Java. En funció de la mida i el tipus de projecte és possible que necessitem ajuda per implementar aspectes recurrents al codi:
- Operacions CRUD: si necessitem fer CRUD per moltes taules, seria una feina molt feixuga.
- Generació de queries SQL: ens facilita tenir queries universals ben formades (dialectes).
- Gestió de transaccions: gestió per threads de transaccions i connexions a la BBDD.
- Control de concurrència (optimista, pessimista): mecanismes amb versions/timestamps o amb bloqueig de files.
Totes aquestes qüestions ens poden portar a escollir una llibreria o un framework.
Alguns interessants implementats amb Java:
- Apache commons DBUtils és una llibreria lliure molt lleugera que facilita l'ús de JDBC.
- ActiveJDBC està basat en el patró Active Record. És molt lleugera, i només necessita una petita instrumentació que genera uns arxius a partir de la configuració. Utilitza les metadates de la BD.
- Java Object Oriented Querying (jOOQ) és una llibreria que implementa el patró Active Record i està orientat a construir queries mitjançant unes classes que es generen a partir de l'esquema d'una BD.
- Java Persistence API (JPA) és una especificació estàndard que utilitza metadates (anotacions o fitxers XML) per fer el mapatge, i un llenguatge específic de queries (JPQL). Té una implementació de referència (EclipseLink) i una altra popular (Hibernate).
Java Persistence API (JPA)
L'API de persistència de Java proporciona als desenvolupadors de Java una facilitat d'assignació d'objectes / relacions per gestionar dades relacionals en aplicacions Java. La persistència de Java es compon de quatre àrees:
- L'API de persistència de Java
- El llenguatge de consulta (JPQL)
- L'API Criteria de persistència de Java (alternativa a JPQL)
- Metadades de mapatge objectes / relacionals (Java annotations i/o XML metadata)
Avantatges d'utilitzar JPA versus JDBC:
- Permet escriure menys codi i menys SQL (CRUD)
- Permet definir relacions entre objectes
- El codi es portable per qualsevol BD (dialectes)
Desavantatges:
- És un nou entorn molt més pesat que JDBC.
- Requereix aprendre una API i un nou llenguatge de consulta (JPQL)
- No és una solució per totes les situacions. Per exemple: si hi ha queries SQL complexes, amb moltes tables implicades, no és tan òptim.
Relacions Many-to-One i One-to-Many
A una relació bidireccional com la de Employee/Department es poden veure dos mapeigos:
- des de Employee a Department és Many-to-One
- des de Department a Employee és One-to-Many
Many-to-One
<many-to-one name="camp" target-entity="Objecte" >
<join-column name="columna"/>
</many-to-one>
On camp és el camp/columna són els camps de l'objecte/taula que ens porten a l'entitat Objecte.
One-to-Many
<one-to-many name="camp1" target-entity="Objecte" mapped-by="camp2" />
On camp1 (habitualment un nom plural) és el camp de l'objecte que ens porta a Objecte, i camp2 el camp de Objecte que fa la relació de tornada.
Fetch Types
Suposem que tenim una entitat arrel, que té una sèrie d'entitats relacionades mitjançant Many-to-One, o One-to-Many.
El "fetch type" defineix què farà JPA per obtenir les dades d'una entitat relacionada amb una altra. Tenim dues opcions:
- EAGER: intentar obtenir les entitats relacionades alhora que obtenim l'entitat arrel. Permet obtenir totes les dades de cop, però no té sentit si tenim una relació amb molts resultats.
- LAZY: obtenir les entitats relacionades quan es necessitin. Provoca queries addicionals, però té sentit si la relació torna molts resultats.
Hi ha un valor per defecte segons el tipus de mapeig:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
Resolent situacions amb el LAZY
Ens pot aparèixer una LazyInitializationException (Hibernate) indicant que no hem pogut obtenir alguna entitat relacionada.
Podem utilitzar JOIN FETCH a la query JPQL per forçar la lectura de les entitats relacionades.
Una alternativa també és utilitzar DTOs (Data Transfer Objects) en lloc d'entitats, que no requereixen que s'obtingui cap relació relacionada.
Píndoles
Netbeans
Per tenir l'ajuda de la documentació a Netbeans (Ctrl+Space), afegiu el javadoc ("Attach Javadoc") corresponent a la versió instal.lada (javax.persistence-api-2.2-javadoc.jar).