簡體   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