Archive for the ‘ Database ’ Category

Shutdown HSQLDB

Ultimamente sto usando Hypersonic SQL DB in un software che sto scrivendo. Il driver utilizzato è “embedded” ovvero non è presente un server in attesa di ricevere comandi SQL, ma il database è un file gestito direttamente dal driver di hypersonic.

Con questo tipo di accesso però, avevo notato perdite di dati sul database, non tutto infatti diventava persistente e, certo di non aver scritto codice errato, iniziavo a pensare di non capirci più nulla.

Il fatto è che, a differenza di quanto ero solito fare con altri RDBMS, non è sufficiente chiudere una connessione con

connection.close()

al termine dell’esecuzione del programma, il driver effettua caching delle tabelle per ottimizzare gli accessi al file system e non tutte le modifiche vengono scritte sul file.

È necessario fornire al driver l’istruzione di “SHUTDOWN” di modo che tutti i dati eventualmente ancora in cache vengano correttamente resi persistenti:

Statement shutdown = connection.createStatement("SHUTDOWN");
shutdown.execute();
shutdown.close();
connection.close();

Esiste inoltre, una funzione di shutdown che effettua anche una riorganizzazione del database ed una compressione dei dati. Essendo un’operazione lenta è bene non usarla troppo di frequente, il criterio d’uso è lo stesso di “SHUTDOWN”, “SHUTDOWN COMPACT”.

Alla prossima :)

Oracle 10g – Upsert

Recentemente mi è capitato di dover eseguire una insert or update, anche detta upsert, su Oracle 10g. Su MySQL ero solito usare la forma “INSERT … ON DUPLICATE KEY UPDATE …” che funziona piuttosto bene, ma che non era supportata dal database che stavo utilizzando.

La versione 10g di Oracle, però, supporta l’operazione “MERGE”, entrata a far parte dello standard SQL nel 2003, e si è rivelata molto comoda utilizzandola tramite JDBC.

Supponiamo di dover memorizzare in una tabella l’ultima pagina visitata da un utente in un dato giorno, possiamo utilizzare il seguente prepared statement java:

PreparedStatement pstmt = conn.prepareStatement(
"MERGE INTO last_page_table dst "+
   "USING ( "+
"SELECT \'"+username+"\' USER_ID, "+
   "TO_DATE(\'"+
      new java.sql.Date(System.currentTimeMillis())+
   "\'YYYY-MM-DD')  DATE_FIELD, "+
   "\'"+lastPage+"\' LAST_PAGE "+
"FROM DUAL) src "+
   "ON (dst.USER_ID = src.USER_ID AND "+
      "dst.DATE_FIELD = src.DATE_FIELD) "+
"WHEN MATCHED THEN "+
   "UPDATE SET dst.LAST_PAGE = src.LAST_PAGE "+
"WHEN NOT MATCHED THEN "+
   "INSERT (dst.USER_ID, "+
      "dst.DATE_FIELD, "+
      "dst.LAST_PAGE) "+
  "VALUES (src.USER_ID, "+
      "src.DATE_FIELD, "+
      "src.LAST_PAGE)");

In sostanza, l’operazione “MERGE” funziona tra tabelle, prende i valori di una e li inserisce o aggiorna nell’altra in base al matching della condizione nella clausola “ON”.
Tuttavia, la necessità non era di utilizzare valori preesistenti di una tabella ed inserirli o aggiornarli nell’altra, ma l’effettivo popolamento della tabella. Esigenza risolta appoggiandosi alla tabella “DUAL”.
Ovviamente, invece di inserire direttamente nella stringa i valori necessari, si poteva utilizzare anche il ‘?’ e chiamare poi le funzioni della classe PreparedStatement.

PreparedStatement pstmt = conn.prepareStatement(
"MERGE INTO last_page_table dst "+
   "USING ( "+
"SELECT ? USER_ID, ?  DATE_FIELD, ? LAST_PAGE "+
   "FROM DUAL) src "+
   "ON (dst.USER_ID = src.USER_ID AND "+
      "dst.DATE_FIELD = src.DATE_FIELD) "+
   "WHEN MATCHED THEN "+
      "UPDATE SET dst.LAST_PAGE = src.LAST_PAGE "+
   "WHEN NOT MATCHED THEN "+
      "INSERT (dst.USER_ID, "+
         "dst.DATE_FIELD, "+
         "dst.LAST_PAGE) "+
      "VALUES (src.USER_ID, "+
         "src.DATE_FIELD, "+
         "src.LAST_PAGE)");

Se non vi sono particolari necessità in termini di performance, sicurezza, flessibilità, in genere preferisco inserire tutto direttamente nella stringa in modo da poterla scrivere facilmente su un file di log prima di inviarla al driver:

String sqlMerge = "MERGE INTO ...";
PreparedStatement pstmt = conn.prepareStatement(sqlMerge);
logger.debug("Executing "+sqlMerge);
pstmt.executeUpdate();