繁体   English   中英

ParameterizedType并创建一个通用的dao

[英]ParameterizedType and creating a generic dao

我尝试这个通用代码,因为我不想为我的数据库中的每个实体创建一个dao类,因为我有80个专门用于那些我将只执行CRUD查询的人。 因为在大多数情况下我只需坚持或通过id进行查找。

public interface GenericDao<T, PK extends Serializable> {

    T create(T t);
    T read(PK id);
    T update(T t);
    void delete(T t);

}

接口的impl

@Component
public class GenericDaoJpaImpl<T, PK extends Serializable> 
                                            implements GenericDao<T, PK> {

    protected Class<T> entityClass;

    @PersistenceContext
    protected EntityManager entityManager;

    public GenericDaoJpaImpl() {
        ParameterizedType genericSuperclass = (ParameterizedType) getClass()
             .getGenericSuperclass();
        this.entityClass = (Class<T>) genericSuperclass
             .getActualTypeArguments()[0];
    }

    @Override
    public T create(T t) {
        this.entityManager.persist(t);
        return t;
    }

    @Override
    public T read(PK id) {
        return this.entityManager.find(entityClass, id);
    }

    @Override
    public T update(T t) {
        return this.entityManager.merge(t);
    }

    @Override
    public void delete(T t) {
        t = this.entityManager.merge(t);
        this.entityManager.remove(t);
    }

    @Override
    public void delete(Set<T> ts) {
        for( T t : ts){
            t = this.entityManager.merge(t);
            this.entityManager.remove(t);
        }
    }
}

例外

Caused by: 
  org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [dao.GenericDaoJpaImpl]: 
  Constructor threw exception; nested exception is 
  java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType

如何解决这个问题以及这个ParameterizedType是什么意思以及为什么我们必须在构造函数中使用它?

当我对构造函数进行注释时,除了public T read(PK id)之外,它还null pointer exception

    public GenericDaoJpaImpl() {
//      ParameterizedType genericSuperclass = (ParameterizedType) getClass()
//           .getGenericSuperclass();
//      this.entityClass = (Class<T>) genericSuperclass
//           .getActualTypeArguments()[0];
    }

我这样使用它:

@Autowired
private GenericDaoJpaImpl<AlerteAcheteur, Integer> acheteurAlerteDao;

我不想创建一个abstract类并像这样扩展它:

public class AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer> ... {

}

@Autowired
private AlerteAcheteurGenericDaoJpaImpl<AlerteAcheteur, Integer> acheteurAlerteDao;

不幸的是,没有办法让它完全按照你的意愿工作。

第1部分。为什么它不起作用

注意GenericDaoJpaImpl的确切声明 -
GenericDaoJpaImpl<T, PK extends Serializable> implements GenericDao<T, PK>

ClassCastException是因为getClass().getGenericSuperclass()返回Class<java.lang.Object>的实例,它是Typejava.lang.Class实现java.lang.reflect.Type ),但不是ParameterizedType 实际上, getClass().getGenericSuperclass()为其直接超类为java.lang.Object每个类返回Class<java.lang.Object>的实例。 因此,构造函数就像

public GenericDaoJpaImpl() {
    ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
    this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
}

适用于声明为AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>的类, AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer> 但这正是您不想申报DAO的方式。

Snippet 1,如果从GenericDaoJpaImpl ,将打印TPK (它们都将是sun.reflect.generics.reflectiveObjects.TypeVariableImpl的实例)。

片段1

Type[] genericInterfaces = getClass().getGenericInterfaces();
ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0];
System.out.println(genericInterface.getActualTypeArguments()[0]);
System.out.println(genericInterface.getActualTypeArguments()[1]);

片段2

@Bean(name = "alerteVendeurDao")
public GenericDao<AlerteVendeur, Long> alerteVendeurDao() {
    return new GenericDaoJpaImpl<AlerteVendeur, Long>();
}

即使在@Configuration -annotated类中有类似Snippet 2的东西,在运行时也不可能知道GenericDaoJpaImpl由于类型擦除而被参数化。 但是,如果Snippet 1是从AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long> class somepackage.entity.AlerteAcheteur东西AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>则会打印class somepackage.entity.AlerteAcheteurclass java.lang.Long (因为这些参数是明确的并且在编译时是已知的)。

最后,组件扫描在逻辑上甚至不适用于GenericDaoJpaImpl @Component -annotated类的Bean是“Singleton”-scoped。 除了只创建一个实例的事实之外,我们怎么会知道这个单例DAO应该在哪个实体上运行呢? 尽管如此,容器仍然可以实例化GenericDaoJpaImpl ,因为在运行时类型信息已经被擦除( 类型擦除! )。

此外,在相关情况下,建议使用更具体的@Repository而不是@Component来注释DAO。

第2部分。最好的选择是什么?

在您的特定情况下,最好的办法是将实体类声明为构造函数参数。 通过这种方式,可以通过将适当的构造函数参数传递给每个实例,在Spring配置中创建许多特定于实体的GenericDaoJpaImpl实例。

GenericDaoJpaImpl.java

public class GenericDaoJpaImpl<T, PK extends Serializable> 
                                            implements GenericDao<T, PK> {
    private final Class<T> entityClass;

    @PersistenceContext
    protected EntityManager entityManager;

    public GenericDaoJpaImpl(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    @Override
    public T create(T t) {
        this.entityManager.persist(t);
        return t;
    }

    @Override
    public T read(PK id) {
        return this.entityManager.find(entityClass, id);
    }

    @Override
    public T update(T t) {
        return this.entityManager.merge(t);
    }

    @Override
    public void delete(T t) {
        t = this.entityManager.merge(t);
        this.entityManager.remove(t);
    }

    @Override
    public void delete(Set<T> ts) {
        for( T t : ts){
            t = this.entityManager.merge(t);
            this.entityManager.remove(t);
        }
    }
}

AnnotationContextConfiguration.java

请注意,也可以通过基于构造函数的依赖注入在XML中执行相同的操作。

@Configuration
@ComponentScan("somepackage.service")// scan for services, but not for DAOs!
public class Config {

    @Bean(autowire = Autowire.BY_NAME)
    public GenericDaoJpaImpl<AlerteAcheteur, Long> alerteAcheteurDao() {
        return new GenericDaoJpaImpl<AlerteAcheteur, Long>(AlerteAcheteur.class);
    }

    @Bean(autowire = Autowire.BY_NAME)
    public GenericDao<AlerteVendeur, Long> alerteVendeurDao() {
          return new GenericDaoJpaImpl<AlerteVendeur, Long>(AlerteVendeur.class);
    }

   // other DAOs

   ...
}

AlerteServiceImpl.java (看起来怎么样)

请注意,字段名称很重要,因为DAO按名称自动装配。 如果您不想为alerteAcheteurDao命名字段,可以将@Qualifier@Autowired一起使用。

@Service
public class AlerteServiceImpl implements AlerteService {

    @Autowired
    private GenericDao<AlerteAcheteur, Long> alerteAcheteurDao;

    @Autowired
    private GenericDao<AlerteVendeur, Long> alerteVendeurDao;

    ...
}

这是一个非常优雅的解决方案。 您不需要像AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>这样的垃圾邮件类AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer> 添加新实体后,您只需要将新的GenericDaoJpaImpl实例添加到Spring配置中。

我希望这会有所帮助。

你的GenericDaoJpaImpl应该是抽象的。 只有具体的后代类型才能解析泛型类型T,PK并定义为Spring bean。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM