lunedì 18 agosto 2014

Hibernate e SQLite

Vai all'indice

Negli esempi precedenti per comodità ho utilizzato Apache Derby, tuttavia Hibernate funziona altrettanto bene con qualsiasi DB semplicemente modificando i parametri di configurazione e la dependency del file POM di Maven. Un caso interessante da trattare è sicuramente l'integrazione di Hibernate con Sqlite.



Molto spesso durante lo sviluppo di un progetto può nascere la necessità di gestire piccole quantità di dati, tali da non giustificare l’uso di un database. La soluzione sicuramente più immediata consiste nell’utilizzo di file di testo o XML come base di dati. Una soluzione alternativa, e a mio avviso più efficace, è nell’uso di SQLite.
SQLite è una piccola libreria  che implementa un vero e proprio motore di database le cui caratteristiche  sono:

  • la semplicità: nessun bisogno di installazione e configurazione;
  • la compattezza: un intero database è contenuto in un unico file, in questo modo è possibile inserire il file direttamente all'interno dell'applicazione stessa!
  • il supporto degli standard: SQLite implementa quasi completamente il linguaggio SQL;
  • la velocità;
  • il costo: GRATUITO.


Per la creazione del database ho scaricato SQLiteStudio, un semplice tool scaricabile dal sito http://sqlitestudio.pl e che non richiede installazione (esiste anche un plugin di Firefox che fa lo stesso lavoro).
Ho creato un nuovo database l'ho chiamato mydb, quindi ho definito la solita tabella "utente" secondo la struttura descritta dall'immagine sottostante.




A questo punto ho ottenuto un file mydb.db, che troviamo nella cartella dove ho posizionato SQLiteStudio e che contiene il DB appena definito.

Metto da parte il file contenente il db appena creato, apro Eclipse e creo un nuovo progetto Maven seguendo questo post.
Finora nel file POM ho inserito la dependency per il caricamento del driver Apache Derby, ora per il caricamento della libreria JDBC di SQLite definisco la dependency:


        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.7.2</version>
        </dependency>


Creo il file Utente.java all'interno della cartella jar e all'interno definisco l'entità Utente.


package jar;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

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

@Id
private Integer userid;
private String nome;
private String cognome;

public Utente() {

}

public Utente(Integer userid, String nome, String cognome) {
this.userid = userid;
this.nome = nome;
this.cognome = cognome;
}

// Definisco getter e setter

}


Nella cartella resources definisco il file 'hibernate.cfg.xml'.

<?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>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="dialect">org.hibernate.dialect.SQLiteDialect</property>
<property name="connection.driver_class">org.sqlite.JDBC</property>
<property name="connection.url">jdbc:sqlite:mydb.db</property>
<property name="connection.username"></property>
<property name="connection.password"></property>

<property name="hibernate.hbm2ddl.auto">update</property>

<mapping class="jar.Utente"/>
</session-factory>
</hibernate-configuration>


Ho creato il pacchetto 'org.hibernate.dialect' nel progetto ed ho inserito il file SQLiteDialect, il file dialect è utilizzato da Hibernate per creare query SQL per lo specifico database, il file è scaricabile da diverse fonti nel web.

      /*
 * The author disclaims copyright to this source code.  In place of
 * a legal notice, here is a blessing:
 *
 *    May you do good and not evil.
 *    May you find forgiveness for yourself and forgive others.
 *    May you share freely, never taking more than you give.
 *
 */
package org.hibernate.dialect;

import java.sql.Types;

import org.hibernate.dialect.function.AbstractAnsiTrimEmulationFunction;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.type.StandardBasicTypes;

public class SQLiteDialect extends Dialect {
  public SQLiteDialect() {
    registerColumnType(Types.BIT, "boolean");
    registerColumnType(Types.TINYINT, "tinyint");
    registerColumnType(Types.SMALLINT, "smallint");
    registerColumnType(Types.INTEGER, "integer");
    registerColumnType(Types.BIGINT, "bigint");
    registerColumnType(Types.FLOAT, "float");
    registerColumnType(Types.REAL, "real");
    registerColumnType(Types.DOUBLE, "double");
    registerColumnType(Types.NUMERIC, "numeric($p, $s)");
    registerColumnType(Types.DECIMAL, "decimal");
    registerColumnType(Types.CHAR, "char");
    registerColumnType(Types.VARCHAR, "varchar($l)");
    registerColumnType(Types.LONGVARCHAR, "longvarchar");
    registerColumnType(Types.DATE, "date");
    registerColumnType(Types.TIME, "time");
    registerColumnType(Types.TIMESTAMP, "datetime");
    registerColumnType(Types.BINARY, "blob");
    registerColumnType(Types.VARBINARY, "blob");
    registerColumnType(Types.LONGVARBINARY, "blob");
    registerColumnType(Types.BLOB, "blob");
    registerColumnType(Types.CLOB, "clob");
    registerColumnType(Types.BOOLEAN, "boolean");

    //registerFunction( "abs", new StandardSQLFunction("abs") );
    registerFunction( "concat", new VarArgsSQLFunction(StandardBasicTypes.STRING, "", "||", "") );
    //registerFunction( "length", new StandardSQLFunction("length", StandardBasicTypes.LONG) );
    //registerFunction( "lower", new StandardSQLFunction("lower") );
    registerFunction( "mod", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "?1 % ?2" ) );
    registerFunction( "quote", new StandardSQLFunction("quote", StandardBasicTypes.STRING) );
    registerFunction( "random", new NoArgSQLFunction("random", StandardBasicTypes.INTEGER) );
    registerFunction( "round", new StandardSQLFunction("round") );
    registerFunction( "substr", new StandardSQLFunction("substr", StandardBasicTypes.STRING) );
    registerFunction( "substring", new SQLFunctionTemplate( StandardBasicTypes.STRING, "substr(?1, ?2, ?3)" ) );
    registerFunction( "trim", new AbstractAnsiTrimEmulationFunction() {
        protected SQLFunction resolveBothSpaceTrimFunction() {
          return new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?1)");
        }

        protected SQLFunction resolveBothSpaceTrimFromFunction() {
          return new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?2)");
        }

        protected SQLFunction resolveLeadingSpaceTrimFunction() {
          return new SQLFunctionTemplate(StandardBasicTypes.STRING, "ltrim(?1)");
        }

        protected SQLFunction resolveTrailingSpaceTrimFunction() {
          return new SQLFunctionTemplate(StandardBasicTypes.STRING, "rtrim(?1)");
        }

        protected SQLFunction resolveBothTrimFunction() {
          return new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?1, ?2)");
        }

        protected SQLFunction resolveLeadingTrimFunction() {
          return new SQLFunctionTemplate(StandardBasicTypes.STRING, "ltrim(?1, ?2)");
        }

        protected SQLFunction resolveTrailingTrimFunction() {
          return new SQLFunctionTemplate(StandardBasicTypes.STRING, "rtrim(?1, ?2)");
        }
    } );
    //registerFunction( "upper", new StandardSQLFunction("upper") );
  }

  public boolean supportsIdentityColumns() {
    return true;
  }

  /*
  public boolean supportsInsertSelectIdentity() {
    return true; // As specify in NHibernate dialect
  }
  */

  public boolean hasDataTypeInIdentityColumn() {
    return false; // As specify in NHibernate dialect
  }

  /*
  public String appendIdentitySelectToInsert(String insertString) {
    return new StringBuffer(insertString.length()+30). // As specify in NHibernate dialect
      append(insertString).
      append("; ").append(getIdentitySelectString()).
      toString();
  }
  */

  public String getIdentityColumnString() {
    // return "integer primary key autoincrement";
    return "integer";
  }

  public String getIdentitySelectString() {
    return "select last_insert_rowid()";
  }

  public boolean supportsLimit() {
    return true;
  }

  public boolean bindLimitParametersInReverseOrder() {
    return true;
  }

  protected String getLimitString(String query, boolean hasOffset) {
    return new StringBuffer(query.length()+20).
      append(query).
      append(hasOffset ? " limit ? offset ?" : " limit ?").
      toString();
  }

  public boolean supportsTemporaryTables() {
    return true;
  }

  public String getCreateTemporaryTableString() {
    return "create temporary table if not exists";
  }

  public boolean dropTemporaryTableAfterUse() {
    return true; // TODO Validate
  }

  public boolean supportsCurrentTimestampSelection() {
    return true;
  }

  public boolean isCurrentTimestampSelectStringCallable() {
    return false;
  }

  public String getCurrentTimestampSelectString() {
    return "select current_timestamp";
  }

  public boolean supportsUnionAll() {
    return true;
  }

  public boolean hasAlterTable() {
    return false; // As specify in NHibernate dialect
  }

  public boolean dropConstraints() {
    return false;
  }

  /*
  public String getAddColumnString() {
    return "add column";
  }
  */

  public String getForUpdateString() {
    return "";
  }

  public boolean supportsOuterJoinForUpdate() {
    return false;
  }

  public String getDropForeignKeyString() {
    throw new UnsupportedOperationException("No drop foreign key syntax supported by SQLiteDialect");
  }

  public String getAddForeignKeyConstraintString(String constraintName,
      String[] foreignKey, String referencedTable, String[] primaryKey,
      boolean referencesPrimaryKey) {
    throw new UnsupportedOperationException("No add foreign key syntax supported by SQLiteDialect");
  }

  public String getAddPrimaryKeyConstraintString(String constraintName) {
    throw new UnsupportedOperationException("No add primary key syntax supported by SQLiteDialect");
  }

  public boolean supportsIfExistsBeforeTableName() {
    return true;
  }

  public boolean supportsCascadeDelete() {
    return true;
  }

  /* not case insensitive for unicode characters by default (ICU extension needed)
  public boolean supportsCaseInsensitiveLike() {
    return true;
  }
  */

  public boolean supportsTupleDistinctCounts() {
    return false;
  }

  public String getSelectGUIDString() {
    return "select hex(randomblob(16))";
  }
}


Ora creo la classe HibernateUtil che mi aiuta a creare la SessionFactory dal file di configurazione di Hibernate. Il SessionFactory viene utilizzato per ottenere le istanze delle sessioni che rappresentano i singoli processi.

package jar;

import java.util.Properties; import org.hibernate.SessionFactory;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.hibernate.HibernateException;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtil {

  
  private static SessionFactory sessionFactory = null;  
  private static ServiceRegistry serviceRegistry = null;

    public static SessionFactory getSessionFactory() {

      try {  
        Configuration configuration = new Configuration();  
        configuration.configure();  
        
        Properties properties = configuration.getProperties();
      
        serviceRegistry = new ServiceRegistryBuilder().applySettings(properties).buildServiceRegistry();          
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);  
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
      
      return sessionFactory; 
    }
}


A questo punto riprendo il file mydb.db creato in precedenza e lo inserisco direttamente all'interno dell'applicazione con un semplice copia-incolla sull'icona di progetto.

La classe Main.java esegue le operazioni di CRUD. Come visto in precedenza tramite questa classe è possibile eseguire il progetto.


package jar;

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

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
public class Main {

   @SuppressWarnings("unchecked")
   public static void main(String[] args) {
      SessionFactory sessionFactory = HibernateUtil.configureSessionFactory();
      Session session = null;
      Transaction tx=null;
      
      try {

         session = sessionFactory.openSession();
         tx = session.beginTransaction();

       // Ripulisco il database
         List<Utente> UtenteList = session.createQuery("from Utente").list();
         for (Utente Utex : UtenteList) {
            session.delete(Utex);
         }         
         
       // Verifico sia vuoto
         for (Utente Utente : UtenteList) {
            System.out.println("Utente Id: " + Utente.getUserid() + " | Name:"  + Utente.getNome() + " | Email:" + Utente.getCognome());
         }            

         // Creo le entità Utente che salverò nel database sqlite
         Utente myUtente = new Utente(11, "Pippo", "Pippo");
         Utente yourUtente = new Utente(12, "Pluto", "Pluto");
         Utente herUtente = new Utente(13, "Paperino", "Paperino");
         
         // Salvo nel database
         session.save(myUtente);
         session.save(yourUtente);
         session.save(herUtente);   
         
         UtenteList = session.createQuery("from Utente").list();         
         // Fetching sui dati salvati
         for (Utente Utente : UtenteList) {
            System.out.println("Utente Id: " + Utente.getUserid() + " | Name:"  + Utente.getNome() + " | Email:" + Utente.getCognome());
         }         
         
         // Aggiornamento
         myUtente.setNome("Gino");
         
         session.merge(myUtente);
         
         // Cancellazione    
         session.delete(herUtente);
         
         // Eseguo un'altra estrazione 
         UtenteList = session.createQuery("from Utente").list();
         
         for (Utente Utente : UtenteList) {
            System.out.println("$$$ Id: " + Utente.getUserid() + " | Name:"  + Utente.getNome() + " | Email:" + Utente.getCognome());
         }
         
         // Commit.
         session.flush();
         tx.commit();
   } catch (Exception ex) {
      ex.printStackTrace();
      
      // Rollback.
      tx.rollback();
   } finally{
      if(session != null) {
         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!