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 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 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.
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 ()
public static LinkedBlockingQueue 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.
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.
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:
- How to listen for table changes in Postgres
- Oracle change notifications
- How to implement listen notify functionality in the MySQL Database
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)
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.