Uncategorized

Speedment, Embedded database

The tutorial application will be as self-contained as possible. To this end, the database will be embedded within the application itself. There are multiple options to run an embedded database from within Java, H2, HSQLBD, Derby and more. The database layer will be Speedment, this means only databases supported by Speedment can be used. In this case, the MariaDB database will be used. With some work, the MariaDB database can be run embedded in an application as well. Running MariaDB this way is useful to make the application fully self-contained. It is also useful when it comes to unit testing the service layer in later tutorials.

Maven dependencies

To make the MariaDB database available to be embedded in the application, the correct dependencies need to be added to the Maven POM. To get the latest versions for these dependencies, check http://mvnrepository.com.

<dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>${mariadb-client.version}</version>
</dependency>
<dependency>
    <groupId>ch.vorburger.mariaDB4j</groupId>
    <artifactId>mariaDB4j</artifactId>
    <version>${mariadb4j.version}</version>
</dependency>

This adds two dependencies. The first one adds the database client used by Java to connect to the database. The second dependency contains the embedded MariaDB for Java implementation.

Spring Configuration

After the required dependencies have been added to the Maven POM, the database can be embedded in the application. Since the database needs to be started each time the application is started, this is best done via a Spring Configuration class. After embedding the database into the application via a Spring Configuration, Speedment needs to be connected to the database. This is done in another Spring Configuration.

The Spring Configuration needs to be placed in a location scanned by Spring Boot. As can be seen in an earlier example (speedment tutorial architecture), Spring Boot is scanning everything below com.ractoc. This means the Spring Configuration needs to be placed in a package below com.ractoc.tutorials.speedment.rest. Since more Spring Configurations will be added, they deserve their own package, com.ractoc.tutorials.speedment.rest.configuration.

Embedded Database Configuration

The Embedded Database Configuration consists of three main parts. Database configuration parameters, Startup of the database and Shutdown of the database. To be able to link the Embedded Database Configuration to the startup and shutdown of the Spring Boot application, the Embedded Database Configuration needs to implement the ServletContextListener.

@Configuration
public class EmbeddedDatabaseConfiguration implements ServletContextListener

Configuration parameters

The configuration parameters used to setup and run the embedded database come from the general Spring Boot application.properties. There are two types of parameters used byt the Embedded Database Configuration, general database parameters and embedded database parameters. The general database parameters will later be reused by the Speedment Configuration. The embedded database parameters are specific for the Embedded Database Configuration.

ParameterExample valueDescription
port9876Port used by the embedded database to listen to incoming connections
schemamy_collectionSchema in the database which holds all the tables
dataDirc:/temp/db/my-collectionLocation on the file system where the mariaDB datafiles will be stored
schemaScriptsql/create_schema.sql Script used to generate the database schema, located on the classpath
dataScriptsql/basedata.sqlScript containing the base setup data for the database, located on the classpath
embeddedtrueIs an embeded database being used or not

These parameters are injected by Spring Boot when the Embedded Database Configuration is loaded for the first time. This is indicated by the @Value annotation, as illustrated by the following code snippet.

// General database configuration parameters
@Value("${dbms.port}")
private int port;
@Value("${dbms.schema}")
private String schema;

// embeded database configuration parameters
@Value("${dbms.embedded.data.dir}")
private String dataDir;
@Value("${dbms.embedded.script.schema}")
private String schemaScript;
@Value("${dbms.embedded.script.data}")
private String dataScript;
@Value("${dbms.embedded}")
private boolean embedded;

Here the ${..} withing the @Value annotation refers to the name of the parameter in the application.properties file.

Startup

Because the Embedded Database Configuration implements ServletContextListener it is possible to react to ServletContext changes. In case of the database startup, this is done by implementing the contextInitialized method.

@Override
public void contextInitialized(ServletContextEvent sce) {
   // only start the database when running in embedded mode
   if (embedded) {
      // start database
      DBConfigurationBuilder configBuilder = DBConfigurationBuilder.newBuilder();
      configBuilder.setPort(port);
      configBuilder.setDataDir(dataDir);
      configBuilder.setSecurityDisabled(true);
      try {
         db = DB.newEmbeddedDB(configBuilder.build());
         db.start();
         db.createDB(schema);
         db.source(schemaScript);
         if (StringUtils.isNotEmpty(dataScript)) {
            db.source(dataScript);
         }
      } catch (ManagedProcessException e) {
         throw new UnableToInitializeDatabaseException(e);
      }
   }
}

In this method the database is started. First a database configuration is being created via a DBConfigurationBuilder. This configuration needs three settings:

  • The port on which the database will listen to incoming connections. This configuration item is injected by Spring Boot from the application.properties.
  • The dataDir where the database will store its data files. This configuration item is injected by Spring Boot from the application.properties.
  • The security of the database is disabled. Since it is an embedded database, it will never be called by anything except the application.

After the DBConfigurationBuilder has been provided with the correct configuration settings, it can be used to create the actual DB instance.
Once the DB instance has been created, it is started. This operation will take some time. Once the database is started, the schema is set. The name of the database schema is injected by Spring Boot from the application.properties.
After the schema is created, a script is run to created the tables, views etc. inside the schema. This script is always run, so it needs to take into account the fact that the schema may already exist. Another solution would be to implement a more intelligent way of handling database scripts. This however is beyond the scope of this tutorial. For convenience, the datascript in this tutorial always starts by dropping the entire schema and recreating it from scratch.
After the schema has been created, an optional dataScript is run to fill any tables with base data.

In case the embedded parameter states that no embedded database is used, the Embedded Database Configuration does nothing.

Shutdown

Besides stating up the database when the ServletContext is initialized, the database also needs to be shutdown when the ServletContext is destroyed. This way, the application correctly cleans up the running database so it can be restarted and the next run of the application. If the application would not correctly shutdown the database, the next run would give an error since the configured port would already be in use.

@Override
public void contextDestroyed(ServletContextEvent sce) {
   // shutdown database
   if (db != null) {
      try {
         db.stop();
      } catch (ManagedProcessException e) {
         throw new UnableToShutDownDatabaseException(e);
      }
   }
}

This shutdown of the database is connected to the contextDestroyed of the ServletContextListener. First a check is done to see if there is an actual database instance. During the initialization, an exception can occur before the database instance is actually created. Once it is checked that a database instance is present, the stop method is called. This will try and stop the database. If it is not possible to stop the database, this throws a ManagedProcessException. A possible cause would be when the database has been shutdown outside of the Java application, either explicitly or because of a problem. This ManagedProcessException is converted into a clearer UnableToShutDownDatabaseException, this is a RuntimeException.

Speedment Configuration

The connection between the application and the database is handled by Speedment. In this case, the Speedment Configuration does not need to be tied to the SevlectContextListener since there is nothing that needs to be started or stopped.

@Configuration
public class SpeedmentConfiguration

Configuration Parameters

The configuration parameters used to setup Speedment to connect to the embedded database come from the general Spring Boot application.properties.

ParameterExample valueDescription
hostlocalhostHostname where the database is located
port9876Port used by the embedded database to listen to incoming connections
schemamy_collectionSchema in the database which holds all the tables
usernamemycollectionUsername used to connect to the database
passwordMyCataloguelPassword used to connect to the database
collationutf8mb4_general_ciCharacter set used by the database
collationBinaryutf8mb4_bin Character set used by the database
debug true Running in debug activates additional logging to the Speedment framework

The parameters needed to setup the connection are once again injected by Spring Boot from the application.properties

@Value("${dbms.host}")
private String host;
@Value("${dbms.port}")
private int port;
@Value("${dbms.schema}")
private String schema;
@Value("${dbms.username}")
private String username;
@Value("${dbms.password}")
private String password;
@Value("${dbms.collation}")
private String collation;
@Value("${dbms.collation.binary}")
private String collationBinary;
@Value("${debug}")
private boolean debug;

Links

The main Speedment website can be found at https://www.speedment.com

The main documentation for Speedment can be found at https://speedment.github.io/speedment-doc/index.html

The source code for the tutorial series examples can be found at https://github.com/ractoc-tutorials/speedment