lunedì 21 luglio 2014

Hibernate relazione uno-a-molti

Vai all'indice

Questo post illustra come Hibernate gestisce la relazione uno-a-molti tra due tabelle. In questo tipo di relazione un record di una tabella è correlato a più record di una seconda tabella, ma i record della seconda tabella sono correlati a un solo record della prima. In questo specifico esempio, che riparte dall'applicazione creata nel post precedente, un utente (pensiamo ad un impiegato di un ufficio) può appartenere ad un solo ufficio, mentre ogni ufficio è formato da molti utenti. Le tabelle utilizzate sono Utente ed Ufficio già definite nel post introduttivo.

Di seguito modifico l'entità Utente.java dell'esempio precedente per la gestione dell'Ufficio e definisco una nuova entità Ufficio.java.

package jar;

import java.sql.Date;
import java.util.HashSet; 
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.ManyToOne;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumns;
import javax.persistence.JoinColumn;
import javax.persistence.Table;

@Entity
@Table(name="utente")
public class Utente {

  @Id
  @GeneratedValue
  @Column(name="userid")
  private int userId;
  
  @Column(name="nome")
  private String nome;
  
  @Column(name="cognome")
  private String cognome;
  
  @Column(name="ruolo")
  private String ruolo;
  
  @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "utente")
    @JoinColumn(name="userid")
  private Dettaglioutente dettaglioutente;
    
    @ManyToOne
    @JoinColumn(name="ufficioId", 
                insertable=false, updatable=false, 
                nullable=false)
    private Ufficio ufficio;
    
  public Utente() {  
  }
  
  public Utente(String nome, String cognome, String ruolo) {
    this.nome = nome;
    this.cognome = cognome;
    this.ruolo = ruolo;
    
  }
  
  // Metodi Getter e Setter

  public int getuserId() {
    return userId;
  }

  public void setuserId(int userId) {
    this.userId = userId;
  }

  public String getNome() {
    return nome;
  }

  public void setNome(String nome) {
    this.nome = nome;
  }

  public String getCognome() {
    return cognome;
  }

  public void setCognome(String cognome) {
    this.cognome = cognome;
  }

  public String getRuolo() {
    return ruolo;
  }

  public void setRuolo(String ruolo) {
    this.ruolo = ruolo;
  }

  public Dettaglioutente getDettaglioutente() {
    return dettaglioutente;
  }

  public void setDettaglioutente(Dettaglioutente dettaglioutente) {
    this.dettaglioutente = dettaglioutente;
  }
  
  public Ufficio getUfficio() {
    return ufficio;
  }

  public void setUfficio(Ufficio ufficio) {
    this.ufficio = ufficio;
  }
  
  
}


package jar;

import java.util.HashSet;
import java.util.List;


import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;

import org.hibernate.annotations.IndexColumn;

@Entity
@Table(name="UFFICIO")
public class Ufficio {

    @Id
    @GeneratedValue
    @Column(name="ufficioid")
    private int ufficioId;
     
    @Column(name="nomeufficio")
    private String nomeUfficio;
  
    @OneToMany(cascade={CascadeType.ALL})
    @JoinColumn(name="ufficioid")
    @IndexColumn(name="iduf")
    private Set<Utente> utenti = new HashSet<Utente>();
    
  public Ufficio() {
  }
    
    public Ufficio(String nome) {
    this.nomeUfficio = nome;
  }
    
  // Metodi Getter e Setter

  public int getUfficioId() {
    return ufficioId;
  }

  public void setUfficioId(int ufficioId) {
    this.ufficioId = ufficioId;
  }

  public String getNomeUfficio() {
    return nomeUfficio;
  }

  public void setNomeUfficio(String nomeUfficio) {
    this.nomeUfficio = nomeUfficio;
  }

  public Set<Utente> getUtenti() {
    return utenti;
  }

  public void setUtenti(Set<Utente> utenti) {
    this.utenti = utenti;
  }
    
}

L'annotazione @ManyToOne definisce l'associazione della classe Utente nella classe di entità Ufficio, viceversa per l'annotazione @OneToMany.
In questo caso specifico l’associazione è bidirezionale, quindi lato Ufficio sarà uno a molti (@OneToMany) mentre lato Utente molti a uno (@ManyToOne). Un'associazione bidirezionale consente la navigazione partendo da entrambe le entità.
In java si fa uso delle classi Set o List per mappare tra loro le entità.
L'annotazione @JoinColumn viene utilizzata per specificare quale colonna viene mappata per collegare le due entità.
La colonna IDUF, definita nella tabella utente, memorizza il valore dell'indice per mantenere l'ordine con cui gli utenti vengono mappati nell'ufficio.
E' importante menzionare l'annotazione fetch= FetchType.Lazy. Questa opzione assicura che gli oggetti figlio non vengano caricati a meno che non siano esplicitamente invocati dall'applicazione chiamando il metodo getUtente() sul padre, cioè sull'oggetto Ufficio. Al contrario specificando fetch= FetchType.Eager, Hibernate caricherà i figli quando sarà caricato il padre: quest'ultima opzione, nel caso di database più complessi con molte tabelle e relazioni,  comporta una sensibile riduzione delle performance.

Quindi modifico il file: /src/main/resources/hibernate.cfg.xml mappando Ufficio.jar:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
  <!-- DB connection settings -->
  <property name="connection.driver_class">org.apache.derby.jdbc.ClientDriver</property>
  <property name="connection.url">jdbc:derby://localhost:1527/derby/derbyDB</property>
  <property name="connection.username">tst</property>
  <property name="connection.password">test</property>

  <property name="connection.pool_size">1</property>
  <property name="dialect">org.hibernate.dialect.DerbyDialect</property>
  <property name="current_session_context_class">thread</property>
  <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
  <property name="show_sql">true</property>
  <property name="hbm2ddl.auto">validate</property>

  <mapping class="jar.Utente"/>
  <mapping class="jar.Dettaglioutente"/>
  <mapping class="jar.Ufficio"/>

</session-factory>
</hibernate-configuration>

La classe HibernateUtil rimane invariata.
La classe Main.java esegue le operazioni di CRUD (Inserimento, lettura e cancellazione). E' possibile eseguire l'applicazione cliccando col tasto destro su questa classe, quindi selezionare Run as > Java Application. Il risultato dell'elaborazione è visibile nella scheda console di Eclipse.

package jar;

import java.sql.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.HibernateException;

public class Main {

  @SuppressWarnings("unchecked")
  public static void main(String[] args) {

    System.out.println("Hibernate Test");
    
    SessionFactory sf = HibernateUtil.getSessionFactory();

    int iduff = 0;
    
    // Inserimento
    Session session = sf.openSession();
    session.beginTransaction();  
    try {
      Ufficio uff1 = new Ufficio();
      uff1.setNomeUfficio("Vendite");  
      Utente ute1 = new Utente("Franco", "Neri", "Utente");
      Utente ute2 = new Utente("Giovanni", "Bianchi", "Admin");      
          uff1.getUtenti().add(ute1);
          uff1.getUtenti().add(ute2);
        iduff = uff1.getUfficioId();
        session.save(uff1);  
        iduff = uff1.getUfficioId();
      session.getTransaction().commit();
    }catch (HibernateException e) {
      session.getTransaction().rollback();
      System.out.println("Anomalia su inserimento ");
          e.printStackTrace();
    } finally {
      session.close();
    }
    
    Lista(); 

    session = sf.openSession();
    session.beginTransaction();      
    try {  
      Ufficio uffx = (Ufficio) session.load(Ufficio.class, iduff);
      List<Utente> utes = session.createQuery("from Utente").list();
      for (Utente utex : utes) {
        uffx.getUtenti().remove(utex);
        session.delete(utex);
      }   
      session.delete(uffx);
      session.getTransaction().commit();  
    }catch (HibernateException e) {
      session.getTransaction().rollback();
      System.out.println("Anomalia su inserimento ");
          e.printStackTrace();
    } finally {
       session.close();
    }
     
     Lista();     

  }

  @SuppressWarnings("unchecked")
  public static void Lista() {
    Session session = HibernateUtil.getSessionFactory().openSession();
    session.beginTransaction();  
    System.out.println("Lista utenti ufficio");
    List<Utente> uts = session.createQuery("from Utente").list();
    for (Utente ut1 : uts) {
      System.out.println(ut1.getNome()  + " , "
        + ut1.getCognome() + ", "
        + ut1.getRuolo()+ ", " 
        + ut1.getUfficio().getNomeUfficio()
            );
    }
    session.close();
  }
}


Di seguito riporto l'alberatura del progetto.





Hai apprezzato questo post? Conferma le mie competenze o scrivi una segnalazione sul mio profilo Linkedin!