Tutorial: Database Change Notifications And H2 Database Triggers

In this tutorial we’ll learn how to implement database change notifications in the H2 Relational Database using triggers.

We’ll start by providing a brief overview of the H2 Database and then review how to create triggers in H2.

H2 Database Trigger Example TOC

Full source code along with a review of the output when the script is executed has been provided as well.

An introduction to the H2 Database

The H2 Database Engine (H2 DB) is an open-source, lightweight, and very powerful relational database written entirely in the Java Programming Language and which supports a long list of features and ships as a single 2.2mb jar file.

H2 is frequently used when testing Java applications and works well as an embedded database and can be used with object-relational mapping tools such as the Java Persistence API (JPA).

H2 DB is available on mvnrepository and can be easily included in java applications that would benefit from utilizing an in memory database that supports SQL and that can help avoid data loss. 

The H2 relational database is used by the JBoss Wildfly application server (JBoss) and has been in use in JBoss as an embedded database for a long time.

H2 offers versatility to Java applications by supporting both embedded and server modes.

In embedded mode, H2 can be integrated directly into Java applications, providing a lightweight, in memory, fully SQL compliant database option.

In server mode, H2 functions as a robust client-server database, safeguarding data integrity while allowing concurrent access.

H2’s user-friendly features extend to its web-based H2 Console, making it easy to interact with the database and execute SQL statements, including insert, update, and delete operations.

H2’s ODBC driver support and ability to run in the client-server configuration ensures that Java applications can efficiently manage data and trigger actions while selecting between disk-based or in-memory storage, catering to a multitude of important database use cases.

How to create a trigger in the H2 Database in three steps

In this example we’ll create a trigger and a very basic implementation of the H2 TriggerAdapter interface.

The following guide is based on the comprehensive H2 trigger example below.

Preconditions

  • Java
  • H2 Database

Step One: Create a table

We apply the trigger to the table so as a first step we need to create the table — for this simple example, we execute the following SQL:

				
					create table test(id identity);
				
			

Below I’ve included the picture which has a pointer to where the trigger statement takes place in the example.

Step one: Groovy script with a pointer to the statement where a table is created.
Step One: Create a Table in the H2 Database

Step Two: Create the trigger for the H2 DB

We create the trigger for this example using the SQL below:

				
					create trigger if not exists notifier_trigger after insert on test for each row call 'com.thospfuller.Notifier';
				
			

We need to be careful to get the “after insert on” vocabulary correct, otherwise when the fire method on the TriggerAdapter is called, the oldRow and newRow will be null.

Step Two: Create the H2 database trigger on the h2 test database table ensuring that we use the correct vocabulary for when the instance of Notifier's fire method should be called.
Step Two: Create the H2 Database Trigger

Step Three: Implement the H2 Database Trigger Adapter

In step three we need to implement the TriggerAdapter interface.

The TriggerAdapter interface has one method called fire, which we can see below.

				
					public class Notifier extends TriggerAdapter {

  public void fire(Connection connection, ResultSet oldRow, ResultSet newRow) throws SQLException {
    // ...
  }
}
				
			

Below we can see the implementation of the H2 trigger adapter specification as it appears in the example.

Step Three: Implement the H2 Database trigger adapter specification (org.h2.tools.TriggerAdapter).
Implement the H2 DB trigger adapter specification.

Once we’ve completed all three steps, our application should be able to process data change events as they occur on any tables we apply this class to.

H2 Database Triggers and Database Change Notifications

I came across this thread about using triggers to notify when a data change takes place in the H2 database on StackOverflow.

This answer provides source code however I wanted to see this run so I spent some time putting together the example below, with my own modifications.

This H2 DB trigger example is written in Groovy and the output when the script is executed appears in the image that follows.

We rely on the java.util.concurrent.LinkedBlockingQueue which is a thread-safe queue used for processing events that are emitted when the trigger fires.

				
					package com.thospfuller

@GrabConfig(systemClassLoader=true)

@Grapes(
  @Grab(group='com.h2database', module='h2', version='2.2.224', scope='test')
)
import java.sql.Statement
import java.sql.Connection
import java.sql.ResultSet
import java.sql.SQLException
import java.sql.DriverManager

import java.util.concurrent.LinkedBlockingQueue

import java.util.concurrent.TimeUnit

import org.h2.tools.TriggerAdapter

@Singleton
public class QueueSingleton {

  private static final def queueSingleton = new LinkedBlockingQueue<String> ()

  public static LinkedBlockingQueue<String> getInstance () {
    return queueSingleton
  }
}

def pollingThread = new Thread ({

  def ctr = 0

  def queueSingleton = QueueSingleton.getInstance ()

  def result = null

  while ("end;" != result) {
  
    result = queueSingleton.take()
    
    println "pollingThread.iteration[${ctr++}] -> result: $result"
  }
})

pollingThread.name = "Polling Thread"

pollingThread.start ()

public class Notifier extends TriggerAdapter {

  final def queueSingleton = QueueSingleton.getInstance ()

  def ctr = 0

  public void fire(Connection connection, ResultSet oldRow, ResultSet newRow) throws SQLException {

    println "notifier.queue.size: ${queueSingleton.size ()}; ctr[${ctr++}]: oldRow: $oldRow, newRow: $newRow"

    String id = newRow?.getString ("id")

    if (id != null)
      queueSingleton.offer ("updated id: $id")
  }
}

public class Main {

  def connection = DriverManager.getConnection("jdbc:h2:mem:test")

  void stop () {
    connection.close ()
  }

  public void run () {

    def statement = connection.createStatement()

    try {

      statement.execute("create table test(id identity);")

      statement.execute("create trigger if not exists notifier_trigger after insert on test for each row call 'com.thospfuller.Notifier';")

      for (int ctr in 0..25)
        statement.execute ("insert into test (id) values ($ctr); commit;")

    } finally {
      statement.close ()
    }
  }
}

def main = new Main ()

main.run ()

def queueSingleton = QueueSingleton.getInstance ()

queueSingleton.offer ("end;")

pollingThread?.join ()

main.stop ()

println "...done!"

return
				
			

H2 Trigger Example Output

Below I’ve included the output when this script is executed.

In this example output we can see the notifier print the queue size as well as the newRow, that appears here due to the insert operation, which resulted in the H2 database trigger firing.

The second pointer directs our eye to the output from the pollingThread, which loops indefinitely, taking the first item from the queue and then printing the result to the console; once “end;” is encountered the thread exits.

Output from the example Groovy script which uses triggers to implement database change notifications in the H2 database including pointers to the notifier, which processes insert events and places the id on the queue and the pollingThread, which takes items from the queue and then prints the value.
DB H2 Trigger Example Output

How to work with the org.h2.api.DatabaseEventListener interface

As mentioned, H2 delivers database change notifications via the org.h2.api.DatabaseEventListener specification.

The DatabaseEventListener interface offers limited functionality when compared with how to listen to table changes in Postgres and how to implement database change notifications in the Oracle database.

None of the methods provided by the DatabaseEventListener interface will notify when data-related changes occur.

The methods required to implement the interface include:

				
					closingDatabase () : void
exceptionThrown(SQLException sqlException, String sql) : void
init (String url): void
opened (): void
setProgress (String state, String name, int x, int max) : void
				
			

A working example implementation of the org.h2.api.DatabaseEventListener using the H2 database can be found on GitHub and is also included in the gist below followed by an image with pointers explaining how this works.

In the following example, H2 is running in embedded mode — that is, H2 is running entirely in memory in the same virtual machine that the Groovy script is running in.

Find the gist below on GitHub.

				
					@GrabConfig(systemClassLoader=true)

@Grapes(
  @Grab(group="com.h2database", module="h2", version="1.4.200")
)
import org.h2.api.DatabaseEventListener
import java.sql.SQLException
import java.sql.DriverManager

public class ExampleDatabaseEventListener implements DatabaseEventListener {

  public void closingDatabase () {
    println "closingDatabase: method invoked."
  }

  public void exceptionThrown (SQLException sqle, String sql) {
    println "exceptionThrown: method invoked; sqle: $sqle, sql: $sql"
  }

  public void init (String url) {
    println "init: method invoked; url: $url"
  }

  public void opened () {
    println "opened: method invoked."
  }

  public void setProgress (int state, String name, int x, int max) {
    println "setProgress: method invoked; state: $state, name: $name, x: $x, max: $max"
  }
}

//
// Note the event listener has been added as a parameter in the connection URL below.
//
def cxn = DriverManager.getConnection("jdbc:h2:mem:EventListenerInH2DatabaseExampleDB;DB_CLOSE_DELAY=-1;DATABASE_EVENT_LISTENER='ExampleDatabaseEventListener';")
def stm = cxn.createStatement()
def resultSet = stm.executeQuery("SELECT 1+1")

if (resultSet.next()) {
  println("next: ${resultSet.getInt(1)}")
}

cxn.close ()

println "...Done!"
				
			

In the following image, we can see an example of this script being executed, along with the output in the GroovyConsole.

H2 DatabaseEventListener implementation in Groovy with pointers to the interface, the connection URL, and the output.
An example of the H2 DatabaseEventListener running in the Groovy Console including output.

This concludes the section on how to implement the H2 DatabaseEventListener interface — the conclusion follows.

Tutorial Conclusion

It’s somewhat disappointing that the H2 DatabaseEventListener specification doesn’t offer similar functionality to that found in the PostgreSQL interface.

As a result, I submitted a new feature request on the H2 DB repository on GitHub and may try to implement this myself, time permitting.

This post is part of a series on database change notifications and other relational databases are covered as well, including articles on:

I have also recently created a guide that explains how to programmatically expose the H2 Database console so that it is available via the browser.

Finally, the COBOL Copybook Converter can help you read COBOL Copybooks into the R Project for Statistical Computing as properly structured data frames — this is an open-source data engineering project.

This article is a content experiment based on keyword research using SEMrush.

Frequently Asked Questions (FAQ)

What is the H2 Database License?

The H2 Database has been released under a dual open-source licensing model (MPL 2.0 or EPL 1.0) — find the H2 DB license on GitHub here.

author avatar
ThosPFuller
I am a software engineer based in Northern Virginia (USA) and this website focuses on content engineering, content experiments, web development, search engine optimization and digital marketing ideas.

ThosPFuller

I am a software engineer based in Northern Virginia (USA) and this website focuses on content engineering, content experiments, web development, search engine optimization and digital marketing ideas.

Leave a Reply