So I've looked a little bit around but still couldn't find anything that could help me. So this is my code:
public ExtendedBaseDAO getCorrespondingDAO(String tableName) throws DAOException {
log.info("BaseService.getCorrespondingDAO(...): Getting DAO correspondant to table with name " + tableName
+ "...");
try {
Optional<EntityType<?>> entityFound = entityManager.getMetamodel().getEntities().stream()
.filter(entity -> ((Table) entity.getJavaType().getAnnotation(Table.class)).name().equalsIgnoreCase(tableName)).findFirst();
log.info("Found entity with name " + entityFound.get().getJavaType().getSimpleName() + " mapped to " + tableName);
Reflections reflections = new Reflections("eu.unicredit.fit.fit_core.dao");
Optional<Class<? extends ExtendedBaseDAO>> daoClassFound = reflections.getSubTypesOf(ExtendedBaseDAO.class)
.stream().filter(daoClass -> daoClass.getSimpleName().replaceAll("DAO", "")
.equals(entityFound.get().getJavaType().getSimpleName()))
.findFirst();
log.info("The correspondant DAO found is " + daoClassFound.get().getSimpleName() + ". Instantiating it...");
return daoClassFound.get().getConstructor().newInstance();
} catch (Exception e) {
throw new DAOException("It was not possible to find the DAO associated with the table " + tableName
+ "! Error: " + e.getLocalizedMessage());
}
}
As you can see I'm returning an instance of the corresponding DAO found using the 'tablename'. I need this method due to the fact that I'll know which table to interrogate at runtime through some parameters. The only problem is that when I'm calling the 'findById' method it's just going to give me a null pointer exception because the EntityManager for that dao is null.
Now... the EntityManager works fine. Here's the class calling that method
public class WizardFieldConfigService extends BaseService {
@Inject
private WizardFieldConfigDAO wizardFieldConfigDAO;
/**
* Retrieves the field data from the specific table requested for. To specify
* the table use the fieldDataRequest.
*
* @param fieldDataRequest The DTO to be used as input
* @return a {@link FieldDataResponseDTO} object with a map containing the
* requested values
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public FieldDataResponseDTO getFieldData(FieldDataRequestDTO fieldDataRequest) {
log.info("WizardFieldConfigService.getFieldData(...): Retrieving field data for field with id "
+ fieldDataRequest.getFieldID() + "...");
WizardFieldConfig fieldBaseData = wizardFieldConfigDAO.findByID((long)fieldDataRequest.getFieldID());
log.info("Found field data: " + fieldBaseData.toString());
List<BaseEntity> response = getCorrespondingDAO(fieldBaseData.getDomainName())
.findWithConditions(fieldDataRequest.getConditions());
return new FieldDataResponseDTO().setPlaceHolder(fieldBaseData.getPlaceholder())
.setLabel(fieldBaseData.getFieldName()).setRegex(fieldBaseData.getRegex())
.setValueList((Map<? extends Serializable, String>) response.stream()
.collect(Collectors.toMap(BaseEntity::getId, BaseEntity::getDescription)));
}
}
So here the first one 'findById' related to the Injected DAO is working fine, while the other DAO no matter what will return a null pointer for any method called because of the entity manager being null. I suppose it is because of it not being a injected bean, is there a way I can get through this problem and fix the entity manager being null?
EDIT: I forgot to mention that I'm doing it without Spring and just with plain CDI. Anyways it could be useful to share the DAO classes structure as mentioned in the comments:
This is ExtendedDAO which extends BaseDAO containing some default query methods:
@Slf4j public abstract class ExtendedBaseDAO<T extends BaseEntity, ID extends Serializable> extends BaseDao<T, ID>{
@PersistenceContext(unitName = "fit-core-em")
private EntityManager em;
protected ExtendedBaseDAO(Class<T> type) {
super(type);
}
public List<T> findWithConditions(List<ConditionDTO> conditions) {
//...
}
@Override
protected EntityManager getEntityManager() {
return this.em;
}
}
Any DAO class would extend this one and therefore have access to the EntityManager. This in fact works perfectly fine for the Injected DAO within the service method
Since you are in control of the DAO classes, I think the simplest solution to dynamically translate a string to a bean is using CDI qualifiers with binding members (see CDI 2.0 spec. section 5.2.6).
So you already have:
public abstract class ExtendedBaseDAO<T extends BaseEntity, ID extends Serializable> extends BaseDao<T, ID>{ {
...
}
@ApplicationScoped
public class WizardFieldConfigDAO extends ExtendedBaseDAO<...> {
...
}
@ApplicationScoped
public class OtherDAO extends ExtendedBaseDAO<...> {
...
}
Define a qualifier annotation with a binding member:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface MyDao {
String tableName();
}
Add it to your beans:
@MyDao(tableName="other")
@ApplicationScoped
public class WizardFieldConfigDAO extends ExtendedBaseDAO<...> {...}
@MyDao(tableName="other")
@ApplicationScoped
public class OtherDAO extends ExtendedBaseDAO<...> {...}
Create a utility annotation literal:
import javax.enterprise.util.AnnotationLiteral;
public class MyDaoQualifier extends AnnotationLiteral<MyDao> implements MyDao {
private String tableName;
public MyDaoQualifier(String tableName) {
this.tableName = tableName;
}
@Override
public String tableName() {
return tableName;
}
}
Get it like:
@Inject @Any
private Instance<ExtendedBaseDAO<?,?>> instance;
public ExtendedBaseDAO getCorrespondingDAO(String tableName) throws DAOException {
try {
return instance.select(new MyDaoQualifier(tableName)).get();
} catch (ResolutionException re) {
throw new DAOException(re);
}
}
Here is a nice article describing this.
BEWARE of dynamically creating @Dependent
-scoped beans, see this!!! TL;DR: Better define an explicit normal scope to your DAOs (e.g. @ApplicationScoped
).