有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java是否可以在运行时动态实例化DAO类?

所以我四处看了看,但仍然找不到任何能帮助我的东西。这是我的代码:

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());
        }
    }

如您所见,我将返回使用“tablename”找到的相应DAO的实例。我需要这个方法,因为我可以通过一些参数知道在运行时查询哪个表。唯一的问题是,当我调用'findById'方法时,它只会给我一个空指针异常,因为该dao的EntityManager是空的

现在。。。EntityManager工作正常。下面是调用该方法的类

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)));
    }

}

因此,在这里,与注入的DAO相关的第一个“findById”工作正常,而另一个DAO无论如何都将为任何调用的方法返回空指针,因为实体管理器为空。我想这是因为它不是注入bean,有没有办法解决这个问题并修复实体管理器为null的问题

编辑:我忘了提到我在做这件事时没有使用Spring,只使用普通的CDI。无论如何,分享评论中提到的DAO类结构可能会很有用:

这是ExtendedDAO,它扩展了包含一些默认查询方法的BaseDAO:

@Slf4j 公共抽象类ExtendedBaseDAO<;T扩展BaseEntity,ID扩展Serializable>;扩展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;
}

}

任何DAO类都会扩展这个类,因此可以访问EntityManager。事实上,对于服务方法中的注入DAO来说,这是非常好的


共 (1) 个答案

  1. # 1 楼答案

    因为您控制着DAO类,所以我认为将字符串动态转换为bean的最简单解决方案是使用带有绑定成员的CDI限定符(参见CDI 2.0规范第5.2.6节)

    所以你已经有了:

    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<...> {
        ...
    }
    

    使用绑定成员定义限定符批注:

    @Qualifier
    @Retention(RUNTIME)
    @Target({METHOD, FIELD, PARAMETER, TYPE})
    public @interface MyDao {
        String tableName();
    }
    

    将其添加到你的bean中:

    @MyDao(tableName="other")
    @ApplicationScoped
    public class WizardFieldConfigDAO extends ExtendedBaseDAO<...> {...}
    
    @MyDao(tableName="other")
    @ApplicationScoped
    public class OtherDAO extends ExtendedBaseDAO<...> {...}
    

    创建实用工具注释文字:

    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;
        }
    }
    

    像这样理解:

    @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是一篇描述这一点的好文章

    注意动态创建@Dependent作用域bean,请参见this!!!TL;DR:最好为DAO定义一个明确的正常范围(例如^{