Skip to content

H2 db

January 13, 2010

H2 è un veloce Java SQL database Open Source, che può funzionare in modalità client-server o embedded.
Spring 3 fornisce supporto per utilizzarlo in maniera embedded tramite configurazione xml

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:jdbc="http://www.springframework.org/schema/jdbc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd">

<jdbc:embedded-database id="dataSource" type="H2">
    <jdbc:script location="classpath:schema.sql"/>
    <jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

oppure attraverso la creazione e l’avvio da codice:

EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2).addScript("my-schema.sql").addScript("my-test-data.sql").build();
/*(EmbeddedDatabase estende javax.sql.DataSource)*/
// faccio le operazioni di ho necessità
db.shutdown(); // fermo il db

Questa seconda soluzione può essere utilizzata nei test nella seguente maniera dentro una classe di test

private EmbeddedDatabase db;

@Before
public void setUp() {
 db = new EmbeddedDatabaseBuilder().addDefaultScripts().build();
}

@After
public void tearDown() {
 db.shutdown();
}

Features

Vediamo una tabella comparativa

Feature H2 Derby HSQLDB MySQL PostgreSQL
Pure Java Yes Yes Yes No No
Embedded Mode (Java) Yes Yes Yes No No
Performance (Embedded) Fast Slow Fast N/A N/A
In-Memory Mode Yes Yes Yes No No
Transaction Isolation Yes Yes No Yes Yes
Cost Based Optimizer Yes Yes No Yes Yes
Explain Plan Yes No Yes Yes Yes
Clustering Yes No No Yes Yes
Encrypted Database Yes Yes No No No
Linked Tables Yes No Partially *1 Partially *2 No
ODBC Driver Yes No No Yes Yes
Fulltext Search Yes No No Yes Yes
User-Defined Datatypes Yes No No Yes Yes
Files per Database Few Many Few Many Many
Table Level Locking Yes Yes No Yes Yes
Row Level Locking Yes *9 Yes No Yes Yes
Multi Version Concurrency Yes No No No Yes
Role Based Security Yes Yes *3 Yes Yes Yes
Updatable Result Sets Yes Yes *7 No Yes Yes
Sequences Yes No Yes No Yes
Limit and Offset Yes No Yes Yes Yes
Temporary Tables Yes Yes *4 Yes Yes Yes
Information Schema Yes No *8 No *8 Yes Yes
Computed Columns Yes No No No Yes *6
Case Insensitive Columns Yes No Yes Yes Yes *6
Custom Aggregate Functions Yes No No Yes Yes
Footprint (jar/dll size) ~1 MB *5 ~2 MB ~700 KB ~4 MB ~6 MB

*1 HSQLDB supports text tables.
*2 MySQL supports linked MySQL tables under the name ‘federated tables’.
*3 Derby support for roles based security and password checking as an option.
*4 Derby only supports global temporary tables.
*5 The default H2 jar file contains debug information, jar files for other databases do not.
*6 PostgreSQL supports functional indexes.
*7 Derby only supports updatable result sets if the query is not sorted.
*8 Derby and HSQLDB don’t support standard compliant information schema tables.
*9 H2 supports row level locks when using multi version concurrency.

Web Console

Una caratteristica utilissima, è la console web con comandi assisititi (autocompletamento) per le istruzioni sql, o la visualizzazione dei database e dei dati in essi contenuti.

Una volta scaricato H2 dalla cartella bin si lancia il server attraverso il comando:

java -cp h2*.jar org.h2.tools.Server

oltre a partire h2 in modalità server verrà automaticamente aperto il browser all’ indirizzo

http://192.168.1.103:8082

questo perchè il comando lancerà iseguenti servizi

Web server running on http://192.168.1.103:8082 (only local connections)
TCP server running on tcp://192.168.1.103:9092 (only local connections)
PG server running on pg://192.168.1.103:5435 (only local connections)

La web console è personalizzabile in quanto a localizzazione, i file con i dati vengono salvati con
<nomedb>.h2.db nella cartella dell’ utente.
Una volta creato il db e i dati eventuali, si può utilizzare il file così generato nel caso si voglia utilizzarlo come
db embedded da deployare dentro una webapp, altrimenti si salvano le istruzioni sql utilizzate nella console
per creare i fiel sql da passare ad h2 al suo avvio.

Embedded in a WebApp

Se il nostro scopo è consegnare una webapplication con i file del db contenuti al suo interno dobbiamo fare delle considerazioni.

Di default, l’url di configurazione di h2 ricerca il file <nome> .h2.db nella cartella dell’ utente corrente per avviare il server.

Nel nostro caso vogliamo che l’applicazione faccia avviare il server a cui l’applicazione successivamente si connetterà.

Per raggiungere questo scopo, possiamo scrivere un ServletContextListener che recupera il file <nome>.h2.db dal classloader

e ne passa il percorso all’ url di configurazione per l’avvio.

public class H2StarterListener implements ServletContextListener {

 private Connection conn;
 private Server server;
 private Logger log = LoggerFactory.getLogger(this.getClass());

 public void contextInitialized(ServletContextEvent event) {
 try {
     org.h2.Driver.load();
     ServletContext servletContext = event.getServletContext();
     String name = getParam(servletContext, "db.name", "mydb");
     String realPath = getClass().getClassLoader()
     .getResource("mydb.h2.db").toString();
     String path =  realPath.substring(0,realPath.lastIndexOf("/"));
     StringBuilder sb = new StringBuilder("jdbc:h2:")
     .append(path).append("/").append(name)
     .append(";DB_CLOSE_ON_EXIT=FALSE");
     String url = getParam(servletContext, "db.url", sb.toString());
     String user = getParam(servletContext, "db.user", "sa");
     String password = getParam(servletContext, "db.password", "sa");
     conn = DriverManager.getConnection(url, user, password);
     servletContext.setAttribute("connection", conn);

     String serverParams = getParam(servletContext, "db.tcpServer", null);
     if (serverParams != null) {
         String[] params = StringUtils.arraySplit(serverParams, ' ', true);
         server = Server.createTcpServer(params);
         server.start();
     }
  } catch (Exception e) {
    log.error(e.getMessage());
  }

 }

 private String getParam(ServletContext servletContext,
     String key, String defaultValue) {

     String value = servletContext.getInitParameter(key);
     return value == null ? defaultValue : value;
 }

 public Connection getConnection() {
    return conn;
 }

 public void contextDestroyed(ServletContextEvent event) {
    try {
        conn.createStatement().execute("SHUTDOWN");
    } catch (Exception e) {
        log.error(e.getMessage());
    }
    try {
       conn.close();
    } catch (Exception e) {
       log.error(e.getMessage());
    }
    if (server != null) {
       server.stop();
       server = null;
    }
 }
}

Oltre al listener, ci scriviamo anche un Datasource che effettua lo stesso lavoro di recupero del path attraverso il classloader

e lo utilizza per stabilire la connessione.

H2 Spring bean

Nel caso non ci interessi avviare il database dalla applicazione, ma ci vada bene che il server parta con un file che è dentro la home directory dell’ utente corrente possiamo utilizzare il server come Spring bean:

<!-- Server -->
<bean id = "org.h2.tools.Server"
 factory-method="createTcpServer"
 init-method="start"
 destroy-method="stop">
 <constructor-arg value="-tcp,-tcpAllowOthers,true,-tcpPort,8043" />
</bean>

<!-- console -->
<bean id="org.h2.tools.Server-WebServer"
 scope="singleton"
  factory-method="createWebServer"
  init-method="start">
 <constructor-arg value="-web,-webAllowOthers,true,-webPort,8082"/>
 </bean>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
  destroy-method="close" depends-on="org.h2.tools.Server">
 <property name="driverClass" value="org.h2.Driver"/>
 <property name="jdbcUrl"
 value="jdbc:h2:<code>~/test</code>;DB_CLOSE_ON_EXIT=FALSE"/>
 <property name="user" value="sa"/>
 <property name="password" value="sa"/><property name="maxPoolSize" value="20"/>
 <property name="minPoolSize" value="5"/>

</bean>

Author: Massimiliano Dessì

La console nel web.xml la mappiamo con:

<pre><servlet>
    <servlet-name>H2Console</servlet-name>
    <servlet-class>org.h2.server.web.WebServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>H2Console</servlet-name>
    <url-pattern>/console/*</url-pattern>
</servlet-mapping>

Author: Massimiliano Dessì

Advertisements
No comments yet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: