This tutorial shows how you can integrate Context Dependency Injection (CDI) to produce resources for JPA Applications. We will also learn how to handle multiple Database resources through CDI annotations.
Basically, CDI is an annotation-driven injection framework that minimizes the bulk of boilerplate code that developers have to write. This helps the productivity, testability, and the quality of business applications built on the Java platform.
Injecting JPA Resources in applications
Firstly, to inject an EntityManager in your CDI Beans you have to use the @PersistenceContext annotation as in the following example:
@RequestScoped public class RequestScopedBean { @PersistenceContext private EntityManager entityManager; public EntityManager getEntityManager() { return entityManager; } }
On the other hand, to separate concerns more cleanly (hiding the implementation of database access from the service class) and allow reusing of the PersistenceContext, it is better to use a Producer method for JPA Resources.
A sample Resources Class which produces an Entity Manager is the following one:
@ApplicationScoped public class Resources { @PersistenceUnit private EntityManagerFactory entityManagerFactory; @Produces @Default @RequestScoped public EntityManager create() { return this.entityManagerFactory.createEntityManager(); } public void dispose(@Disposes @Default EntityManager entityManager) { if (entityManager.isOpen()) { entityManager.close(); } } }
Please notice, the above class also includes both a create method to produce an EntityManager and a dispose method to safely release the resources.
By using a Producer method you can inject JPA resources in a standard way in all your CDI Beans:
@Inject private EntityManager em;
Using Multiple Databases
One common use cases for many applications is to handle multiple database resources. At minimum you should consider handling a production DB and a development DB.
In our initial example we have learnt how to inject an EntityManager in our application. Next, let’s see how we can improve this by adding your own custom EntityManager qualifier annotations.
The following Qualifier is the first step to create a MySQLDatabase Entity Manager:
package com.sample; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import javax.inject.Qualifier; @Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) public @interface MySQLDatabase { }
And here’s the Producer class, which will create Entity Managers for the “primary” Persistence Context
package com.sample; import javax.enterprise.inject.Produces; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; public class DatabaseProducer { @Produces @PersistenceContext(unitName = "primary") @MySQLDatabase private EntityManager em; }
Finally, here’s the sample persistence.xml file which contains two persistence unit: the first one which is bound to MySQL and the second one which uses the default ExampleDS DB.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="primary"> <jta-data-source>java:jboss/datasources/MySqlDS</jta-data-source> <properties> <property name="hibernate.hbm2ddl.auto" value="create-drop" /> </properties> </persistence-unit> <persistence-unit name="secondary"> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <properties> <!-- Properties for Hibernate --> <property name="hibernate.hbm2ddl.auto" value="create-drop" /> <property name="hibernate.show_sql" value="false" /> </properties> </persistence-unit> </persistence>
Now it’s just a matter of adding the @MySQLDatabase database annotation to mark an EntityManager injection for MySQL database:
@MySQLDatabase @Inject private EntityManager em;
You can create of course a corresponding qualifier for the DefaultDS and add it to the DatabaseProducer
@Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) public @interface DefaultDatabase { }
public class DatabaseProducer { @Produces @PersistenceContext(unitName = "primary") @MySQLDatabase private EntityManager em; @Produces @PersistenceContext(unitName = "secondary") @DefaultDatabase private EntityManager em; }
This way you can immediately give evidence of what you are doing with your Entity Managers.
JPA and CDI using @Alternative producers
When using this approach we suppose that it’s known at compile time the EntityManager that will be used in each method or class. If on the other hand you want to address this issue at packaging time, you can use the @Alternative annotation.
The @Alternative annotation lets you package multiple beans that match an injection point without ambiguity errors. In other words, you can apply the @Alternative annotation to two or more beans. Then, specify the bean you want to use in CDI’s beans.xml configuration file.
For example, here we will declare two database producers, one for MySQL and one for Default database:
package com.sample; import javax.enterprise.inject.Alternative; import javax.enterprise.inject.Produces; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Alternative public class MySQLDatabaseProducer { @Produces @PersistenceContext(unitName = "primary") private EntityManager em; }
package com.sample; import javax.enterprise.inject.Alternative; import javax.enterprise.inject.Produces; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Alternative public class DefaultDatabaseProducer { @Produces @PersistenceContext(unitName = "secondary") private EntityManager em; }
Next, inject an Entity Manager in your code:
@Inject private EntityManager em;
the choice of which EntityManager will be resolved by CDI’s beans.xml file (placed into the WEB-INF folder of your Web application).
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> <alternatives> <class>com.sample.MySQLDatabaseProducer</class> </alternatives> </beans>
Specifying the default Entity Manager properties
One more trick you can do with CDI, is defining a method and marking it as producer, This way, it will be called each time an EntityManeger object is needed. For example, here we are injecting some properties into our EntityManager once it is created. This can be useful for example if you have multiple databases with the same schemas, but using a different dialect (ex. MySQL-Oracle) and we want to choose dynamically which dialect will be used:
@PersistenceUnit(unitName = "primary") private EntityManagerFactory emf; @Produces @PersistenceContext(unitName = "primary") public void createEntityManager() { Map props = new HashMap(); props.put("hibernate.show_sql","true"); props.put("hibernate.dialect","org.hibernate.dialect.MySQLDialect"); props.put("hibernate.show_sql","true"); props.put("hibernate.format_sql","true"); EntityManager emy = emf.createEntityManager(props); }
Finally, each resource Producer might have a corresponding resource Disposer which can be used to perform some clean up actions before removing the instance. For example, here’s how you can dispose the EntityManager created by the createEntityManager method:
public void dispose(@Disposes @Default EntityManager entityManager) { if(entityManager.isOpen()) { entityManager.close(); } }