Saturday, August 16, 2014

How to browse the content of an H2 in-memory database

In this post we'll see how to browse the content of an in-memory H2 database instance. 
As this H2 mode is usually used in unit tests and integration tests, it may come in handy the day a test does not yield the expected results and we need to get a grasp of the data that are handled. 

Method A - H2 TCP server

Starting an H2 TCP server allows to remotely connect to an in-memory database using SQL clients such as Squirrel SQL or Eclipse integrated SQL client.

A TCP server is created through calling createTcpServer() on org.h2.tools.Server. This method
takes an optional String vararg argument that allows to specify options such as the port the server will listen to.

Example

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.h2.tools.Server;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class H2MemoryDbBrowserTest {

 private static final String DATABASE_URL = "jdbc:h2:mem:demo;USER=sa;INIT=RUNSCRIPT FROM 'classpath:/scripts/init-db.sql'";
 private static Connection conn;

 @BeforeClass
 public static void initDatabase() throws SQLException {
  conn = DriverManager.getConnection(DATABASE_URL); // creating SQL connection will trigger the DB creation
 }
 
 @AfterClass
 public static void closeDatabase() throws SQLException {
  conn.close();
 }

 @Test
 public void testMethodA() throws SQLException {
  Server server = Server.createTcpServer("-tcpPort", "9999");
  server.start();
  server.stop();
 }
}
 


In this example, the arguments we passed to the createTcpServer() method means the TCP server will be reachable on port 9999.

If we execute this unit test with a breakpoint set after the TCP server has started, we are able to connect to the in-memory DB. To illustrate this, let's connect to it using Squirrel SQL.

Our database has been created with the URL jdbc:h2:mem:demo and a user account whose username is "sa" and has no password. With the TCP server running, our database is remotely accessible with the following URL: jdbc:h2:tcp//localhost:9999/mem/demo.

Connection configuration in Squirrel SQL


Squirrel SQL connected to our in-memory DB

Method B - H2 web server

Another solution consists in starting an H2 web server by calling the static org.h2.tools.Server.startWebServer() method. This method takes an SQL connection as argument. This SQL connection is created by calling java.sql.DriverManager.getConnection() and passing our database's URL as argument.

When the H2 web server starts up, the code execution is suspended and it triggers the opening of a web interface in our default browser:


H2 web server interface

Important note: the suspended code execution will only resume after we explicitly disconnect the web interface by clicking on the button in the upper left corner. Closing either the tab of the browser won't be enough.

Note that the instruction to start the H2 web server can also be executed in the IDE expression evaluation tool. That way, it is even possible to browse the in-memory DB at any point of an executing test without having to first somewhat change the code.

Runnable example

A Maven based project with runnable examples is accessible here

Sunday, April 13, 2014

Conditional bean validation using Hibernate Validator

In this post, we’ll see how to achieve conditional bean validation in a few steps using Hibernate Validator.

Thorough explanation on how to implement bean validation is out of this article's scope, so the reader is assumed to already have practical experience with bean validation (aka JSR-303).


Example use case

Suppose we have a bean of type ContactInfo.java that stores user contact information. For simplicity sake, we consider it only holds a country, a zip code and a phone number. Depending on the user’s country we may want the zip code to be mandatory or optional.

Here’s our bean:

package domain;

import javax.validation.constraints.NotNull;

import validationgroup.USValidation;


public class ContactData {
 private Country country; // ENUM indicating the user's country

 private String zipCode;

 private String phoneNumber;

 public ContactData(Country country, String zipCode, String phoneNumber) {
  this.country = country;
  this.phoneNumber = phoneNumber;
  this.zipCode = zipCode;
 }

 public Country getCountry(){
  return this.country;
 }
 
 public String getZipCode() {
  return zipCode;
 }
 
 public String getPhoneNumber() {
  return phoneNumber;
 }
 
}

Step 1 - Define a validation group

Create a marker interface. This will be used as an identifier to a group of validation rules:


public interface USValidation {

}

Step 2 - Add validation rules to our validation group

Add the @Null annotation on the zipCode getter as follows:


@NotNull(message="Zip code is mandatory", groups={USValidation.class})
 public String getZipCode() {
  return zipCode;
 }

The groups attribute specifies the group(s) to which the validation rule belongs to.

Step 3 - Configure the validator

Tell the validator object to apply the validation rules from our group, in addition to the default ones (which are the validation rules with no groups attribute specified).

There are actually two ways to configure the validator:

Solution A 

The simplest way is when we got a reference to the validator object. In that case, we just need to pass it the bean instance to validate, plus the interface that corresponds to the group we defined at step 1.


ContactData cd = new ContactData(Country.US, null, null);
Set<ConstraintViolation<ContactData>> validationResult = validator.validate(cd);
Assert.assertEquals(validationResult.size(), 0);

Solution B 

In case we don’t get a reference to the validator object  (for instance, when we rely on the @Valid Spring annotation to trigger bean validation from within a Controller), we can define a custom Sequence Provider to specify which are the validation groups that must be applied.

To define a custom Sequence Provider, we just need to create a class that implements the DefaultSequenceProvider interface. This interface exposes a single method that returns a list of classes that correspond to the validation groups we want to apply. 

NOTE: the class type of the bean we want to validate must be added to the returned list. Otherwise an exception like the following is thrown:

domain.ContactDataBis must be part of the redefined default group sequence.

This behavior ensures that the underlying validator object will get the default validation rules at the very least.

Here’s our custom Sequence Provider:

public class ContactDataSequenceProvider implements DefaultGroupSequenceProvider<ContactData>{
 
 
 public List<Class<?>> getValidationGroups(ContactData contactData) {
  List<Class<?>> sequence = new ArrayList<Class<?>>();
  
  /*
   * ContactDataBis must be added to the returned list so that the validator gets to know
   * the default validation rules, at the very least.
   */
  sequence.add(ContactDataBis.class);
  
  /*
   *  Here, we can implement a certain logic to determine what are the additional group of rules
   *  that must be applied. 
   */
  if(contactData != null && contactData.getCountry() == Country.US){
   sequence.add(USValidation.class);
  }
  
  return sequence;
 }

}


Once our custom sequence provider is defined, we just need to annotate the bean class with @GroupSequenceProvider like this:

@GroupSequenceProvider(value = ContactDataSequenceProvider.class)
public class ContactDataBis extends ContactData{

 public ContactDataBis(Country country, String zipCode, String phoneNumber) {
  super(country, zipCode, phoneNumber);
 }
 
}

Based on this, the validator will look for the validation groups it should apply by executing the getValidationGroups method from our Sequence Provider.

Source code

Source code and running examples (unit tests) are available here.