0. Introduction
Understanding how JSF works is a bit difficult for me. In StackOverflow, the possibility of making a singleton object for an entity manager is suggested.
The idea is making a complex application that uses several schemes of the database, but even this approach can be achieved using only one entity manage, as the scheme is chosen when defining the POJO (the class to persist that contains JPA annotations).
It can be interesting to have several persistence units in out persistence.xml file so that we can select our database scenario (we can use for development H2, Derby etc and for deployment Postgresql or MariaDB etc) making use of a property file for choosing our persistent unit.
But as reported in StackOverflow it can be interesting using different implementations of JPA (Hibernate, EclipseLink, TopLink, OpenJPA, DataNucleus, ObjectDB), but also some, not JPA approaches that can be taken into account (JDBC, ORM Hibernate, MyBatis, JDO..)
So, our soul mate CDI can help us in this way:
- We can design an interface that defines our desired CRUD (Create, read, update, write ...) functionality
- In a property/configuration file, we can choose the desired implementation of the interface.
- By means of CDI, we can use the chosen implementation as pointed out in a previous post.
In the sake of simplicity, we can design the interface with only some capabilities of a JPA entity manager.
1. CRUD Interface (main interface)
This interface defines only some basic CRUD functions for teaching purpose only. This file is IDAO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package org.ximodante.utils.jpa; public interface IDAO { // 1. begin transaction public void beginTrans(); // 2. commit transaction public void commitTrans(); // 3. create an entity in the DB public void persist(java.lang.Object entity); // 4. remove an entity from the DB public void remove(java.lang.Object entity); // 5. Retrieve an entity from the DB public <T> T find(java.lang.Class<T> entityClass, java.lang.Object primaryKey); // 6. Update an entity in the DB public void refresh(java.lang.Object entity); } |
2. The annotation interface (to assign a "key" to pick the desired implementation)
This annotation is used to label the classes that implement the main interface. This file is IDAOType.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package org.ximodante.utils.jpa; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; /** * For distinguishing different classes that implements the IDAO interface * @author Ximo Dante * */ @Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface IDAOType { String type() default "JPA-Hibernate"; } |
3. The class to be injected that implements IDAO (main interface)
In this case, we are using only one implementation of the IDAO interface. Why on earth wasting so much effort using injection if there is only one candidate for injection? This is maybe what you are thinking! But if we consider another implementation in a near future, all the scaffolding has been built.
Don't forget to make this class Serializable !!!!
This is the class: DAOJPAHibernate.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | package org.ximodante.utils.jpa; import java.io.Serializable; import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Default; import javax.inject.Inject; import javax.inject.Named; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.ximodante.utils.property.ApplicationProperties; @Named @ApplicationScoped // Singleton @Default @IDAOType(type="JPA-Hibernate") // for selecting the object at run time with CDI public class DAOJPAHibernate implements IDAO, Serializable{ private static final long serialVersionUID = 1L; private static final String JPAPersistenceUnitKey="JPAPersistenceUnit"; private EntityManagerFactory emf = null; private EntityManager em = null; @Inject ApplicationProperties appProps; @Override public void beginTrans() { em.getTransaction().begin(); } @Override public void commitTrans() { em.getTransaction().commit(); } @Override public void persist(Object entity) { em.persist(entity); } @Override public void remove(Object entity) { em.refresh(entity); } @Override public <T> T find(Class<T> entityClass, Object primaryKey) { return em.find(entityClass, primaryKey); } @Override public void refresh(Object entity) { em.refresh(entity); } /** * Creates the entity manager object needed to operate. */ @PostConstruct public void init() { String myPersistenceUnit=appProps.getProperty(JPAPersistenceUnitKey); // my_jap_test System.out.println("myPersistenceUnit="+myPersistenceUnit); this.emf = Persistence.createEntityManagerFactory(myPersistenceUnit); this.em = emf.createEntityManager(); } } |
It is important to note that the Persistence unit is obtained from the file application.properties as shown in a previous post for getting properties. Now to the application.properties file 2 new entries have been added:
1 2 | DAOProvider=JPA-Hibernate JPAPersistenceUnit=my_jpa_test |
The first one DAOJPAProvider will select our injection class
The second one JPAPersistenceUnit will select our persistence unit (defined in persistence.xml file) in case that the IDAO implementation is a JPA compliant. In this case, we are using "my_jpa_test"
4. The tool class
The purpose of this tool class is managing the parameter (type) of the annotation so that we can extract the desired dependency from all the possible candidates that implement the main interface.
This class is DAOTypeDescription
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import javax.enterprise.util.AnnotationLiteral; /** * Class that is a tool to manage the parameter "type" from the Annotation IDAOType so that * the desired candidate class for injection is selected * * @see https://stackoverflow.com/questions/33583032/dynamically-injecting-instances-via-cdi * @see https://stackoverflow.com/questions/24798529/how-to-programmatically-inject-a-java-cdi-managed-bean-into-a-local-variable-in * * @author Ximo Dante * */ public class DAOTypeDescriptor extends AnnotationLiteral<IDAOType> implements IDAOType{ private static final long serialVersionUID = 1L; private String type; public DAOTypeDescriptor(String type) { this.type = type; } @Override public String type() { return type; } } |
5. Injecting into a bean
In this case, the bean is DAOBean.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package org.ximodante.utils.jpa; import java.io.Serializable; import lombok.Getter; import javax.annotation.PostConstruct; import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Instance; import javax.inject.Inject; import javax.inject.Named; import org.ximodante.utils.property.ApplicationProperties; /** * * @author Ximo Dante * @date 29/08/2017 * */ @Named @SessionScoped public class DAOBean implements Serializable { private static final long serialVersionUID = 1L; private static final String DAOProviderKey="DAOProvider"; @Inject ApplicationProperties appProps; @Inject Instance<IDAO> unqualifiedDAO; @Getter private IDAO myDAO; @PostConstruct public void init() { String myDAOType=this.appProps.getProperty(DAOProviderKey); System.out.println("myDAOType=" + myDAOType); // get desired implementation of IDAO by injection myDAO = unqualifiedDAO.select(new DAOTypeDescriptor(myDAOType)).get(); } public String myName (Long id){ myDAO.beginTrans(); Person ps = myDAO.find(Person.class, id); myDAO.commitTrans(); return ps.getName(); } } |
Note:
Line 38: The initializing method with @PostConstruct annotation for selecting the class to inject.
Line 41: Gets from the property file who is the DAO provider
Line 45: Get the proper IDAO implementation class by its labelled annotation @IDAOType (type = "JPA-Hibernate")
that belongs to our implementing class
Line 48: A method for teaching purposes to be used in a xhtml file. This method retrieves the name of the person whose id is 14 (In this case is "Juanito")
6. The facelet file for testing
Let's use a modification of the file beanprop.xhtml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui"> <h:head> </h:head> <h:body> <h:form> <p:panel header="Keyboard Demo"> <p:keyboard value="#{testPropertyBean.comment}"/> <p:keyboard value="#{testPropertyBean.greet1}"/> <p:keyboard value="#{dAOBean.myName(14)}"/> </p:panel> <p:commandButton value="Submit"/> </h:form> </h:body> </html> |
Note:
Line 13 that executes the method myName of the DAOBean passing the id parameter=14.
And if we right-click on the file beanprop.xml and Run on Server, we can get this view
We can see "Juanito" in the last input box.
Great!
No hay comentarios:
Publicar un comentario