I want to reuse my AbstractDAO in a new project, except this time I don't want to use EJB annotations - just CDI ones.
So far, I've been using it like this:
public abstract class AbstractDAO<T> {
#PersistenceContext(unitName = "myUnit")
private EntityManager entityManager;
private Class<T> entityClass;
public AbstractDAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected EntityManager getEntityManager() {
return entityManager;
}
public void save(T entity) {
entityManager.persist(entity);
}
public void update(T entity) {
entityManager.merge(entity);
}
public void remove(T entity) {
entityManager.remove(entityManager.merge(entity));
}
public T findById(Object id) {
return entityManager.find(entityClass, id);
}
public List<T> findBy(String attrName, Object attrValue) {
// Impl here
}
// [...] Many more search methods
}
And I've been creating a DAO for each Entity, like this for example:
#Stateless
public class UserDAO extends AbstractDAO<User> {
public UserDAO() {
super(User.class);
}
public User findByUsername(String username) {
if (username != null) {
return super.findOneBy("username", username.toLowerCase());
}
return null;
}
}
Now I would like to get rid of the #Stateless annotation. But simply replacing it with a #RequestScoped one won't work because of the non-private constructor with no parameters requirement of JSR-346
How can I refactor my DAO to a pure CDI one ?
Two issues here: CDI beans are not transaction aware by default -unlike EJBs, so you will have to use #Transactional qualifier if you want to do saves/updates...
Second, your no-arg constructor: you only need to pass the entity class to your abstract class even though you also specify it as a generic argument. You can infer the actual class like this:
public class AbstractDAO<T> {
private transient Class<T> entityClass;
#SuppressWarnings("unchecked")
public AbstractDAO() {
Type generSuperCls = getClass().getGenericSuperclass();
if (generSuperCls instanceof Class) {
generSuperCls = ((Class<?>) generSuperCls).getGenericSuperclass();
}
ParameterizedType parameterizedType = (ParameterizedType) generSuperCls;
Type type = parameterizedType.getActualTypeArguments()[0];
if (type instanceof Class) {
this.entityClass = (Class<T>) type;
} else if (type instanceof ParameterizedType) {
this.entityClass = (Class<T>) ((ParameterizedType) type).getRawType();
}
}
#PersistenceContext
private EntityManager em;
public T getById(Object id) throws ServiceException {
return getEm().find(entityClass, id);
}
// other methods follow
}
As a side note, why do you want to get rid of EJBs? Benchmarks show you get better performance using pooled slsb than cdi and they together very well(every EJB beans is also a CDI bean in jee container).
Related
I have a Java web application running on Tomcat 9.
It uses the tools: Primefaces 10, JSF 2, CDI, EclipseLink.
This application is modularized as follows:
model
dao
service
managedBean
web
I created another modules
microservice
restful
where the "restful module" is the jsersey restful webservice.
I incorporate this webservice with the "service", "dao" and "model" modules using dependency injection (CDI) binding through this class
public class MyApplicationBinder extends AbstractBinder {
// mapping dependency injection with jersey
#Override
protected void configure() {
//bind(MicroserviceDataImpl.class).to(MicroserviceDataImpl.class); // concrete class
bind(SequenceRuleImpl.class).to(SequenceRule.class); // class implementing interface
bind(CaracteristicaMS.class).to(new TypeLiteral<Microservice<CaracteristicaDTO>>(){}); // classe implementing interface with generics
bind(MSCaracteristicaDaoImpl.class).to(new TypeLiteral<Dao<Caracteristica>>(){}).qualifiedBy(new Qualifier.DaoMSCaracteristica()); // class implementing interface with generics and qualifier
}
}
My qualifier
public class Qualifier {
public static class DaoMSCaracteristica extends AnnotationLiteral<DaoType> implements DaoType {
#Override
public TipoClasse value() {
return TipoClasse.CARACTERISTICA;
}
}
}
My resource
#RequestScoped #Path("/caracteristica")
public class CaracteristicaRestful {
#Inject
private Microservice<CaracteristicaDTO> cms;
// other attributes and methods
}
My microservice class
#Dependent
public class CaracteristicaMS implements Microservice<CaracteristicaDTO>, Serializable {
#Inject #DaoType(TipoClasse.CARACTERISTICA)
private Dao<Caracteristica> cd;
// others attributes and methods
}
My dao class
#Dependent #Default #DaoType(value = TipoClasse.CARACTERISTICA)
public class CaracteristicaDaoImpl extends AbsDao<Caracteristica> implements Dao<Caracteristica>, Serializable {
#NotNull #Inject #PersistenceUnitNameType(CADMAT_PU)
EntityManagerFactory emf;
// others attributes and methods
}
My Entity Manager factory provider
#ApplicationScoped
public class EMFProvider {
public static final EntityManagerFactory EMF_CADMAT = Persistence.createEntityManagerFactory(CADMAT_PU);
public static final EntityManagerFactory EMF_SIAFNET = Persistence.createEntityManagerFactory(SIAFEM_PU);
#Produces #Dependent #PersistenceUnitNameType(SIAFEM_PU)
public EntityManagerFactory getEMF_SIAFNET() {
return EMF_SIAFNET;
}
#Dependent #PersistenceUnitNameType(SIAFEM_PU)
public void closeENF_SIAFNET(#Disposes #PersistenceUnitNameType(SIAFEM_PU) EntityManagerFactory emf) {
if (emf != null && emf.isOpen()) {
Logger.getAnonymousLogger().log(Level.INFO, "***** closing em {0}", emf);
emf.close();
}
}
#Produces #ApplicationScoped #PersistenceUnitNameType(CADMAT_PU)
public EntityManagerFactory getEMF_CADMAT() {
return EMF_CADMAT;
}
#Produces #Dependent #PersistenceUnitNameType(CADMAT_PU)
public EntityManager getEM() {
EntityManager em = EMF_CADMAT.createEntityManager();
Logger.getAnonymousLogger().log(Level.INFO, "***** creating em {0}", em);
return em;
}
#ApplicationScoped #PersistenceUnitNameType(CADMAT_PU)
public void close(#Disposes #PersistenceUnitNameType(CADMAT_PU) EntityManagerFactory emf) {
if (emf != null && emf.isOpen()) {
Logger.getAnonymousLogger().log(Level.INFO, "***** closing em {0}", emf);
emf.close();
}
}
#Dependent #PersistenceUnitNameType(CADMAT_PU)
public void close(#Disposes #PersistenceUnitNameType(CADMAT_PU) EntityManager em) {
if (em != null && em.isOpen()) {
Logger.getAnonymousLogger().log(Level.INFO, "***** closing em {0}", em);
em.close();
}
}
}
My transactional interceptor
#Interceptor #TransactionType
public class TransactionalInterceptor {
#AroundInvoke
public Object transactional(InvocationContext ctx) throws Exception {
EntityManager em = null;
Field field = null;
try {
field = ctx.getMethod().getDeclaringClass().getDeclaredField("em");
} catch (NoSuchFieldException e1) {
try {
field = ctx.getMethod().getDeclaringClass().getSuperclass().getDeclaredField("em");
} catch (NoSuchFieldException e2) {
throw new IllegalStateException("invalid interceptor configuration for " + ctx.getMethod().getDeclaringClass());
}
}
field.setAccessible(true);
em = (EntityManager)field.get(ctx.getTarget());
if (em == null) throw new IllegalStateException("*** no em for transaction");
Logger.getAnonymousLogger().info("*** transaction em " + em);
Dao.begin(em);
Object obj = ctx.proceed();
Dao.commit(em);
return obj;
}
}
My interface annotation classes
#Qualifier #Retention(RetentionPolicy.RUNTIME) #Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface DaoType {
public TipoClasse value();
}
#Qualifier #Retention(RetentionPolicy.RUNTIME) #Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface PersistenceUnitNameType {
public String value();
}
Is there a way to bind my EntityManager using this structure?
In mapstruct 1.3.0.Final we have dependency injection via constructor. Documentation says:
The generated mapper will inject all classes defined in the uses
attribute
(...)
For abstract classes or decorators setter injection should be
used.
I have following example:
#Mapper
public abstract class VehicleMapper {
#Autowired
private CarMapper carMapper;
#Autowired
private BikeMapper bikeMapper;
#Override
public VehicleDTO toDto(final Vehicle source) {
if (source instanceof Car) {
return carMapper.toDto((Car) source);
} else if (source instanceof Bike) {
return bikeMapper.toDto((Bike) source);
} else {
throw new IllegalArgumentException();
}
}
(...)
So in my case it should look like this (componentModel defined in maven):
#Mapper
public abstract class VehicleMapper {
private CarMapper carMapper;
private BikeMapper bikeMapper;
#Autowired
public void setCarMapper(final CarMapper carMapper) {
this.carMapper = carMapper;
}
#Autowired
public void setBikeMapper(final BikeMapper bikeMapper) {
this.bikeMapper = bikeMapper;
}
#Override
public VehicleDTO toDto(final Vehicle source) {
if (source instanceof Car) {
return carMapper.toDto((Car) source);
} else if (source instanceof Bike) {
return bikeMapper.toDto((Bike) source);
} else {
throw new IllegalArgumentException();
}
}
(...)
Question:
So it is not possible to inject carMapper and bikeMapper via constructor ? does injectionStrategy = CONSTRUCTOR works only for classes declared in #Mapper(uses = {}) ?
I think that injectionStrategy = CONSTRUCTOR works on the interface that has the #Mapper annotation. I don't think it works with abstract classes. I'm sure it will not work when you define your own fields (instance variables). How would MapStruct know what user defined fields to initialise in the constructor?
My congfiguration is like below:
The abstract class:
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
// other methods create(T), edit(T), ...
The Ejb that extends the abstract class (i have many others EJBs and about 12 different persistence units):
#Stateless
public class FilesDao extends AbstractFacade<Files> {
#PersistenceContext(unitName = "myfirstPU")
private EntityManager firstEm;
#PersistenceContext(unitName = "mysecondPU")
private EntityManager secondEm;
// i have more than two persistenceUnit ...
#Override
protected EntityManager getEntityManager() {
return firstEm; // or secondEm based on condition
}
public FilesDao() {
super(Files.class);
}
public Files findByFileref(String inFileRef) {
try {
Query q = firstEm.createNamedQuery("Files.findByFileref"); // or secondEm based on condition
q.setParameter("fileref", inFileRef);
Files file = (Files) q.getSingleResult();
return file;
} catch (NoResultException e) {
return null;
}
}
I want to use FilesDao like this :
#Stateless
#LocalBean
public class FileBusiness {
#EJB
FilesDao fileDao;
public void myMethod(){
if(condition1){
//use the FileDao with the EnityManager **firstEm**
}
else if(condition2){
//use the FileDao with the EnityManager **secondtEm**
}
...
}
Is there a way to achieve that ?
I read about using CDI with produces method.
CDI-Producers are not that hard, see the following example.
The following classes are CDI-Qualifier annotations, which are used to distinguish implementations.
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR })
#Qualifier
public #interface MyfirstPUQualifier { }
#Retention(RetentionPolicy.RUNTIME)
#Target({ TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR })
#Qualifier
public #interface MysecondPUQualifier { }
The following is a CDI-Bean which injects the different EntityManagers and implements producer methods, making the two EntityManagers available to CDI, distinguished via CDI-Qualifiers
#ApplicationScoped
class MYProducer {
#PersistenceContext(unitName = "myfirstPU")
private EntityManager firstEm;
#PersistenceContext(unitName = "mysecondPU")
private EntityManager secondEm;
#Produces
#RequestScoped
#MyfirstPUQualifier
public EntityManager produceMyfirstPU(){
return firstEm;
}
#Produces
#RequestScoped
#MysecondPUQualifier
public EntityManager produceMysecondPU(){
return secondEm;
}
}
The following shows a CDI-Bean injecting both EntityManagers. This could be extracted to an abstract base class, because maybe needed by other DAOs as well.
#Stateless
public class FileDao {
#Inject
#MyfirstPUQualifier
private EntityManager emFirst;
#Inject
#MysecondPUQualifier
private EntityManager emSecond;
public void myDaoMEthod(){
final EntityManager em = getEntityManager();
...
}
private EntityManager getEntityManager(){
if(condition1){
return emFirst;
}
else if(condition2){
return emSecond;
}
}
}
No can use the FileDao bean without taking care what EntityManager to use, because shouldn't be decided in this bean anyway
#Stateless
#LocalBean
public class FileBusiness {
#EJB
FilesDao fileDao;
public void myMethod(){
// FileDao will decide what entity manager to use
fileDao.doStruff();
}
}
I have a library with some functionality that I want to reuse in other projects. My issue is that my service requires writing to the database. I would like for my library to use the datasource of the project that is inject my service.
Here is the minimal setup of my service
#Stateless
public class CustomService {
//to be added in producer
private EntityManager em;
private Principal principal;
//default constructor
public CustomService() {}
//custom constructor called in provider
public CustomService(Principal p, EntityManager e) {
principal = p;
em = e;
}
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Transactional
public CustomJPAObject createObject(...params...) {
//create JPA Object
em.persist(customObject);
em.flush();
return customObject;
}
}
I created a Custom Annotation for overriding the datasource
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE,ElementType.FIELD, ElementType.METHOD})
public #interface DynamicDS {
#Nonbinding String value() default "";
}
I also created a Singleton to be an EntityManager Producer
#Singleton
public class CustomEMProducer {
private Map<String, EntityManagerFactory> emfMap = new HashMap<>();
#Produces #Dependent #DynamicDS
public EntityManager produceEntityManager(InjectionPoint injectionPoint) {
String dataSourceName = null;
for(Annotation qualifier: injectionPoint.getQualifiers()) {
if(qualifier instanceof DynamicDS) {
DynamicDS dds = (DynamicDS) qualifier;
dataSourceName = dds.value();
break;
}
}
EntityManagerFactory emf = emfMap.get(dataSourceName);
if (emf == null) {
emf = Persistence.createEntityManagerFactory(dataSourceName);
emfMap.put(dataSourceName, emf);
}
return emf.createEntityManager();
}
#PostConstruct
public void cleanup() {
emfMap.entrySet().stream().forEach(entry -> entry.getValue().close());
}
}
Here is the code for my Service Producer
#Stateless
public class CustomServiceProvider {
#Inject private Principal principal;
#Produces #Dependent #DynamicDS
public BackgroundJobService getBackgroundJobService(InjectionPoint injectionPoint) throws EntityManagerNotCreatedException {
Annotation dsAnnotation = null;
for(Annotation qualifier: injectionPoint.getQualifiers()) {
if(qualifier instanceof BackgroundJobDS) {
dsAnnotation = qualifier;
break;
}
}
if (dsAnnotation != null) {
EntityManager em = CDI.current().select(EntityManager.class, dsAnnotation).get();
CustomService service = new CustomService(principal, em);
return service;
}
throw new EntityManagerNotCreatedException("Could not Produce CustomService");
}
}
The following is where I try to inject my new service
#Stateless
public class ProjectService {
#Inject #DynamicDS("project-ds") CustomerService service;
public CustomObject create(...params...) {
return service.createObject(...params...);
}
}
When I deploy my code and attempt to call the injected service I get the following error:
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.checkTransactionNeeded(AbstractEntityManagerImpl.java:1171)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1332)
...
It looks like all of the different levels of providers are preventing the #Transactional on the CustomService.createObject() method call from propagating the transaction. Does anyone have insight into why this is or an alternate way of accomplishing my goal of injecting a dynamic EntityManager?
After much experimenting, I was unable to get dynamically generate an EntityManager through the above code. After much research, I gave up on trying to pass in the name from outside the 3rd part library. I would up creating the following interface:
public interface CustomEntityManager {
EntityManager getEntityManager();
}
This meant that inside the project that uses the 3rd party service I can do the create the following implementation to inject the EntityManager
public ProjectSpecificEntityManager implements CustomEntityManager {
#PersistenceContext(unitname = "project-ds")
private EntityManager em;
public EntityManager getEntityManager() {
return em;
}
}
I had to update my CustomService to the following
#Stateless
public class CustomService {
//Ignore warning about no bean eligible because it is intended
//that the project that uses this library will provide the
//implementation
#SuppressWarnings("cdi-ambiguous-dependency")
#Inject
CustomEntityManager cem;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
#Transactional
public CustomJPAObject createObject(...params...) {
//create JPA Object
cem.getEntityManager().persist(customObject);
return customObject;
}
}
I want to create a remote EJB for an entity class. Is there a way of implementing a method with one parameter beign an object of a class that specifically has the #Entity annotation? The purpose of this, is to create just one bean for all my entities.
Example:
public void save(Entity ent){
em.persist(ent);
}
If you just have 1 ejb for all entities you will soon run into problems when you have to handle some of them in a special way.
But you can do like this with an abstract super-ejb, and still have 1 ejb per entity, but its very easy to create. And you can still overwrite the default, inherited methods.
public abstract class AbstractEjb<T> {
private Class<T> entityClass;
public AbstractEjb(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
public List<T> findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[1] - range[0] + 1);
q.setFirstResult(range[0]);
return q.getResultList();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
cq.select(getEntityManager().getCriteriaBuilder().count(rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}
To implement for an entity just do
#Stateless
public class TestEjb extends AbstractEjb<TestEntity> {
#PersistenceContext(unitName = "...")
private EntityManager em;
public TestEjb() {
super(TestEntity.class);
}
public EntityManager getEntityManager() {
return em;
}
}
Source: Generate JSF pages from entity classes in Netbeans.