[英]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;
不幸的是,沒有辦法讓它完全按照你的意願工作。
注意GenericDaoJpaImpl
的確切聲明 -
GenericDaoJpaImpl<T, PK extends Serializable> implements GenericDao<T, PK>
。
ClassCastException
是因為getClass().getGenericSuperclass()
返回Class<java.lang.Object>
的實例,它是Type
( java.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
,將打印T
和PK
(它們都將是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.AlerteAcheteur
和class java.lang.Long
(因為這些參數是明確的並且在編譯時是已知的)。
最后,組件掃描在邏輯上甚至不適用於GenericDaoJpaImpl
。 @Component
-annotated類的Bean是“Singleton”-scoped。 除了只創建一個實例的事實之外,我們怎么會知道這個單例DAO應該在哪個實體上運行呢? 盡管如此,容器仍然可以實例化GenericDaoJpaImpl
,因為在運行時類型信息已經被擦除( 類型擦除! )。
此外,在相關情況下,建議使用更具體的@Repository
而不是@Component
來注釋DAO。
在您的特定情況下,最好的辦法是將實體類聲明為構造函數參數。 通過這種方式,可以通過將適當的構造函數參數傳遞給每個實例,在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.