Tecnologia

TypeORM vs JPA

INTRODUZIONE

L’oggetto di studio del seguente articolo, verte su due importanti tecnologie per la gestione della persistenza dei dati in applicazioni Java e TypeScript: JPA (Java Persistence API) e TypeORM. Le seguenti tecnologie sono utilizzate per semplificare l’accesso e la manipolazione dei dati in un database, ma sono utilizzate in stack tecnologici diversi. Inoltre, il loro uso è frequente nelle operazioni CRUD (Create, Read, Update, Delete). Esamineremo in dettaglio ciascuna delle tecnologie sopra citate.

JPA (JAVA PERSISTENCE API)

JPA (Java Persistence API) è uno standard Java per la gestione della persistenza dei dati in applicazioni Java. Consente agli sviluppatori di interagire con un database relazionale utilizzando un approccio orientato agli oggetti. JPA offre un’astrazione della persistenza dei dati, permettendo agli sviluppatori di scrivere codice Java senza doversi preoccupare dei dettagli specifici del database sottostante.

CARATTERISTICHE PRINCIPALI DI JPA

  1. Oggetti persistenti: Con JPA, è possibile rappresentare gli oggetti Java come entità persistenti. Queste entità sono associate a tabelle nel database e possono essere create, lette, aggiornate e eliminate utilizzando operazioni CRUD (Create, Read, Update, Delete).
  2. Mapping oggetti-relazionali: JPA fornisce meccanismi per mappare le entità Java alle tabelle del database. È possibile definire queste associazioni utilizzando annotazioni o file di configurazione XML.
  3. Query JPQL: JPA supporta JPQL (Java Persistence Query Language), un linguaggio di query simile a SQL ma orientato agli oggetti. JPQL permette di eseguire query complesse sulle entità persistenti senza dover scrivere SQL diretto.
  4. Transazioni: JPA gestisce le transazioni in modo trasparente. È possibile definire transazioni per garantire l’integrità dei dati e il controllo della concorrenza.
  5. Cache dei dati: JPA fornisce una cache dei dati di primo livello che può migliorare le prestazioni dell’applicazione evitando accessi ripetuti al database per oggetti già caricati in memoria.
  6. Supporto per diversi provider: JPA è uno standard e offre il supporto per diversi provider di persistenza, tra cui Hibernate, EclipseLink e altri. Questo significa che è possibile scegliere il provider più adatto alle esigenze del progetto.

ESEMPIO DI UTILIZZO DI JPA

Ecco un esempio semplice di utilizzo di JPA per definire un’entità:

@Entity

public class Prodotto {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String nome;

    private double prezzo;

    // Getter e setter

}

TYPEORM

TypeORM è un ORM (Object-Relational Mapping) per applicazioni Node.js e TypeScript. A differenza di JPA, che è specifico per Java, TypeORM è progettato per lavorare con applicazioni JavaScript/TypeScript e offre un approccio simile per la gestione della persistenza dei dati.

CARATTERISTICHE PRINCIPALI DI TYPEORM

  1. Supporto per TypeScript: TypeORM è scritto in TypeScript e offre un’esperienza di sviluppo fortemente tipizzata per applicazioni TypeScript.
  2. Decoratori TypeScript: TypeORM utilizza decoratori TypeScript per definire le entità e le relazioni tra di esse. Questi decoratori semplificano la definizione delle entità e dei loro mapping.
  3. Query Builder e Query Language: TypeORM offre un Query Builder che consente di costruire query in modo programmatico. Inoltre, è possibile utilizzare il proprio linguaggio di query SQL o un linguaggio simile a SQL per eseguire query complesse.
  4. Supporto per più database: TypeORM supporta diversi database relazionali, tra cui PostgreSQL, MySQL, MariaDB, SQLite e Microsoft SQL Server.
  5. Migrazioni automatiche: TypeORM consente di gestire le migrazioni del database in modo automatico, semplificando la gestione degli schemi del database durante lo sviluppo e il rilascio dell’applicazione.

ESEMPIO DI UTILIZZO DI TYPEORM

Ecco un esempio di utilizzo di TypeORM per definire un’entità:

import { Entity, PrimaryGeneratedColumn, Column, EntityManager, Connection } from 'typeorm';

@Entity()
class Prodotto {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  nome: string;

  @Column('double')
  prezzo: number;

}

PRIMARY KEY

Nei database relazionali, una primary key identifica univocamente la riga di una tabella. Sia JPA che TypeORM, come ovvio che sia, gestiscono il concetto di primary key. Di seguito vedremo alcune loro caratteristiche e differenze.

PRIMARY KEY JPA

Nelle entità JPA, le chiavi primarie possono essere di diversi tipi: interi, stringhe o persino oggetti embedded. Con l’annotazione @Id, indentificheremo il campo di chiave primaria. JPA offre diverse strategie di generazione automatica delle chiavi primarie, come l’auto-incremento, tramite l’annotazione @GeneratedValue, per database come MySQL. 

@Entity
public class Book {

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;
  
  private String title;
  private Float price;
  private String description;
  private String isbn;
  private Integer nbOfPage;
  private Boolean illustrations;
  
  //Constructor, getters, setters
}

PRIMARY KEY TYPEORM

TypeORM di solito richiede l’uso di un identificatore numerico, come un intero o un numero a virgola mobile, come chiave primaria. Tuttavia, è possibile utilizzare anche UUID o altre stringhe univoche. Anche TypeORM supporta la generazione automatica delle chiavi primarie, ma la configurazione potrebbe variare leggermente in base al tipo di database. Si può utilizzare il decoratore @PrimaryGeneratedColumn per dichiarare una colonna auto-incrementante.

@Entity()
export class User {

  @PrimaryGeneratedColumn()
  id: number
  
}
@Entity()
export class User {

  @PrimaryColumn()
  id: number
  
}

MAPPATURE DELLE COLONNE DI UNA TABELLA

Sia in JPA che in TypeORM si utilizza l’annotazione (decoratore in TypeORM) @Column per poter mappare le colonne di una tabella di database

@Entity
public class Book { 

  @Id
  @GeneratedValue(strategy GenerationType.AUTO) 
  private Long id;
  
  @Column(name = 'book_title', nullable = false, updatable = false) 
  private String title;   
  private Float price; 
  
  @Column(length = 2000) 
  private String description; 
  private String isbn; 
  
  @Column(name = 'nb_ef_page', nullable = false) 
  private Integer nbOfPage; 
  private Boolean illustrations; 
  
  // Constructors, getters, setters 
}
@Entity("users") 
export class User {

  @Column({ primary: true }) 
  id: number 
  
  @Column({ type: "varchar", length: 200, unique: true }) 
  firstName: string
  
  @Column({ nullable: true }) 
  lastName: string
  
  @Column({ default: false }) 
  isActive: boolean
  
} 

Come possiamo leggere dalla figura 4 e dalla figura 5, non ci sono importanti differenze per quanto riguarda l’utilizzo di @Column, se non nella sintassi: in TypeORM si ha una sintassi TypeScript oriented, invece in JPA una sintassi Java oriented.

MAPPATURE DELLE RELAZIONI

Per quanto riguarda la gestione delle foreign key e la mappatura delle relazioni, JPA offre alcune annotazioni come @OneToOne, @OneToMany e @ManyToOne che, in egual modo ma definiti come decoratori, offre anche TypeORM. La differenza tra i due sta sempre nella sintassi: Java, lato JPA; TypeScript, lato TypeORM. 

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm" 
import { User } from "./User"

@Entity()
export class Photo { 

  @PrimaryGeneratedColumn() 
  id: number
  
  @Column() 
  url: string
  
  @ManyToOne((type) -> User, (user) => user.photos) 
  user: User
  
}
import { Entity, OneToOne, JoinColumn } from "typeorm"
import { Profile } from "./Profile"
 
@Entity() 
export class User { 
  
  @OneToOne((type) => Profile, (profile) => profile.user) 
  @JoinColumn() 
  profile: Profile 
  
}
@Entity
@Table(name = "users") 
public class User { 
  
  @Id 
  @GeneratedValue(strategy = GenerationType.AUTO) 
  @Column(name = "id") 
  private Long id; 
  
  //... 
  
  @OneToOne(cascade = CascadeType.ALL) 
  @JoinColumn(name = "address_id", referencedColumnName = "id") 
  private Address address;
  
  // getters and setters
}

Come si vede, in entrambi i casi, si utilizza @JoinColumn che per entrambi contrassegna una colonna come colonna di join per un’associazione di entità o un insieme di elementi.

CONCLUSIONE

JPA e TypeORM sono due tecnologie ORM potenti utilizzate per semplificare la gestione della persistenza dei dati in applicazioni Java e JavaScript/TypeScript. Entrambe offrono un’astrazione della persistenza dei dati e consentono di lavorare con il database utilizzando oggetti invece di query SQL dirette. La scelta tra JPA e TypeORM dipenderà, ovviamente, dalle esigenze specifiche del progetto e dallo stack tecnologico utilizzato. Entrambe le tecnologie offrono un modo efficace per gestire i dati in applicazioni moderne.

BIBLIOGRAFIA

Goncalves A., Beginning Java™ EE 6 Platform with GlassFish 3, Second Edition, United States, Apress, 2010.

Decorator Reference | TypeORM