Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Switching Datasource with CDI Alternatives and Stereotypes

DZone's Guide to

Switching Datasource with CDI Alternatives and Stereotypes

· Java Zone ·
Free Resource

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Here we are, using H2 in our test environment, Derby in development and Postgres in production. It’s 2014, and Java EE still doesn’t have a decent configuration specificationSo how do we change datasources in Java EE depending on our environment ? There are several possibilities (from external property files, Maven resource filtering, Maven profiles with different configuration or JNDI tricks) but this post will only concentrate on one: switching datasource with CDI alternatives and stereotypes.

Use Case

Let’s say we have a Java EE web application (a CRUD application for a Speaker entity using JSF, EJB, JPA and CDI), running on JBoss, and we need to easily switch datasources: a H2 in-memory datasource in our test environment, a Derby datasource for development and a Postgres datasource in production. Each datasource has a unique JNDI name and points to a database (either in-memory or server). Then, with just a bit of XML configuration (yes, Java EE still uses XML, not JSon ;o) we want to switch datasources. This is how it looks like in a nice diagram:

Switch Datasource with CDI Alternatives DS

Notice the @DevDatabase and @ProdDatabase in the diagram. You will see later that these annotations are, in fact,CDI stereotypes and they will be very helpful in solving our use case.

Datasource Configuration

JBoss Console with DataSource

JBoss Console with the three DataSources

Something a bit painful in Java EE is how you create and configure new datasources. Thanks to @DataSourceDefinition annotation brought in Java EE 6, we can now create new datasources using an annotation in our code.  I won’t be doing this here, as I prefer to use JBoss admin CLI. The following commands will create a datasource for H2, Derby and Postgres (check the jboss-setup.cliscript because you need to create the Derby and Postgres JDBC drivers) and the result can be seen in the JBoss Console:

/subsystem=datasources/data-source=H2DS:add(driver-name=h2, user-name=sa, password=sa, connection-url="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", jndi-name=java:/global/datasources/H2DS, enabled=true)
/subsystem=datasources/data-source=DerbyDS:add(driver-name=derby, user-name=app, password=app, connection-url="jdbc:derby://localhost:1527/sample;create=true", jndi-name=java:/global/datasources/DerbyDS, enabled=true)
/subsystem=datasources/data-source=PostgresDS:add(driver-name=postgres, user-name=postgres, password=postgres, connection-url="jdbc:postgresql://localhost:5432/postgres", jndi-name=java:/global/datasources/PostgresDS, enabled=true)

Now that our JBoss knows these three datasources, let’s configure them in our persistence.xml file.

JPA Persistence Units

In a persistence.xml file we can have several persistence units. So the idea is to create three persistence units, each one pointing to a different datasource. So we will have:

  • alternative-test-pu the test datasource pointing to H2 in-memory at java:/global/datasources/H2DS
  • alternative-dev-pu the development datasource pointing to Derby at java:/global/datasources/DerbyDS
  • alternative-prod-pu the production datasource pointing at java:/global/datasources/PostgresDS

This is the way it looks like in the persistence.xml (for clarity I’ve omitted a few properties, you can check here the entire file):

persistence.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.1"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="alternative-test-pu" transaction-type="JTA">
<jta-data-source>java:/global/datasources/H2DS</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="javax.persistence.sql-load-script-source" value="insert.sql"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.transaction.flush_before_completion" value="true"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
</properties>
</persistence-unit>
<persistence-unit name="alternative-dev-pu" transaction-type="JTA">
<jta-data-source>java:/global/datasources/DerbyDS</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="javax.persistence.sql-load-script-source" value="insert.sql"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.transaction.flush_before_completion" value="true"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect"/>
</properties>
</persistence-unit>
<persistence-unit name="alternative-prod-pu" transaction-type="JTA">
<jta-data-source>java:/global/datasources/PostgresDS</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<property name="javax.persistence.sql-load-script-source" value="insert.sql"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.transaction.flush_before_completion" value="true"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
</properties>
</persistence-unit>
</persistence>

This kind of configuration can be unusual in a real project. You might prefer to have severalpersistence.xml files, each one declaring one persistent unit, and let a Maven profile to switch between files. But this is not the purpose of this post.

Injecting the EntityManager

How do we inject an EntityManager in Java EE 7? Well, we take any managed bean, add a @Transactionalannotation to get a managed transactional demarcation, and the @PersistenceContext annotation will do the job: given a persistence unit (here alternative-test-pu) the container will inject an EntityManager. In our application, the SpeakerBean is the one responsible to do CRUD operations on the Speaker entity, so this is how we inject the EntityManager;

SpeakerBean
@Named
@Transactional
@ConversationScoped
public class SpeakerBean implements Serializable {
@PersistenceContext(unitName = "alternative-test-pu")
private EntityManager entityManager;
...

But as you can see in this code, there is no way to switch between persistence units here, and therefore, no way to switch datasources. The idea is to inject the EntityManager using the CDI @Inject annotation:

SpeakerBean
@Named
@Transactional
@ConversationScoped
public class SpeakerBean implements Serializable {
@Inject
private EntityManager entityManager;
...

Now, we need to produce the EntityManager to be able to inject it.

Producing three EntityManagers

As you might know by now, the only way to inject an EntityManager in Java EE is, either by using the@PersistenceContext annotation, or by producing it with CDI (thanks to producers). Take any POJO, add any attribute or method, annotate it with @Produces, and the result will be injectable by CDI. So the class below will produce our three EntityManagers, each pointing to a different persistent unit:

DatabaseProducer
public class DatabaseProducer {
@Produces
@PersistenceContext(unitName = "alternative-test-pu")
private EntityManager entityManagerTest;
@Produces
@PersistenceContext(unitName = "alternative-dev-pu")
private EntityManager entityManagerDev;
@Produces
@PersistenceContext(unitName = "alternative-prod-pu")
private EntityManager entityManagerProd;
}

This code won’t deploy because the deployment is ambiguous. If you don’t qualify a producer, a bean, or an injection point, it automatically gets a @Default qualifier. So here, the three EntityManager attributes have the same @Default qualifier and CDI doesn’t know how to make the difference between one another. The way to separate them is either to create different qualifiers, or stereotypes. Remember our use case: being able to switch from one datasource to another. Stereotypes is what we want. So what we really want is the following code (notice the @Alternative annotation and both stereotypes @DevDatabase and @ProdDatabase):

DatabaseProducer
public class DatabaseProducer {
@Produces
@PersistenceContext(unitName = "alternative-test-pu")
private EntityManager entityManagerTest;
@Produces
@Alternative
@DevDatabase
@PersistenceContext(unitName = "alternative-dev-pu")
private EntityManager entityManagerDev;
@Produces
@Alternative
@ProdDatabase
@PersistenceContext(unitName = "alternative-prod-pu")
private EntityManager entityManagerProd;
}

The way to read this code is “By default, produce the test EntityManager. If the @DevDatabase alternative is activated, produce the development EntityManager. If the the @ProdDatabase alternative is activated, produce the production EntityManager“.

Using CDI Stereotypes and Alternatives

In CDI, a stereotype encapsulates various properties including scope, interceptor bindings, qualifiers, alternatives, etc, into a single reusable package. In terms of code, it’s just a Java annotation annotated with @Stereotype. Below is the @DevDatabase stereotype:

DevDatabase
@Stereotype
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface DevDatabase {
}

The code will be similar for the @ProdDatabase stereotype. We now have one stereotype for the development database, one for the production database, and none for the test database (as it is the default).

On the other hand, alternatives are beans whose implementation is specific to a particular client module or deployment scenario. That’s exactly what we want: depending on our deployment scenario (test, development, production) we want a different EntityManager. So let’s bundle stereotypes and alternatives to get what we want.

Producing three EntityManagers with Stereotypes and Alternatives

A stereotype can indicate that the bean (or producer) to which it is applied is an @Alternative. An alternative stereotype lets us classify beans by deployment scenario. So, on our @DevDatabase and@ProdDatabase stereotypes we jus need to add the @Alternative qualifier:

DevDatabase
@Stereotype
@Alternative
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface DevDatabase {
}

Thanks to this stereotype with an alternative on it, we can now do the datasource switch in the beans.xml files. If the file is empty, no alternative is activated, so the default will apply and the test EntityManager pointing to H2 will be used. Instead, if we want to switch to the production Postgres database, we need to activate the alternative as follow:

beans.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bean-discovery-mode="all" version="1.1"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
<alternatives>
<stereotype>org.agoncal.sample.cdi.alternatives.database.cdi.ProdDatabase</stereotype>
</alternatives>
</beans>

Conclusion

In this post I’ve shown you how to use CDI to switch datasources. CDI comes with a powerful implementation of the Bridge Design Pattern in the name of Alternatives. This allows us to swap one implementation by another one, in our case, from one EntityManager to another one. Being strongly typed, the only way to achieve this in CDI is by using Stereotypes (CDI hates injection with String names) and producing the appropriate EntityManager, thanks to Producers andbeans.xml.

By the way, thanks to JBoss Forge for kicking out a web application with few commands.

So, download the code (or fork it on GitHub), give it a try, and give me some feedback.

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Topics:

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}