ParameterizedType并创建一个通用的dao [英] ParameterizedType and creating a generic dao
问题描述
我尝试这个泛型代码,因为我不想为我的数据库中的每个实体创建一个dao类,因为我有80个专门针对那些我将仅执行CRUD查询的人。因为在大多数情况下,我只需要坚持或通过id进行查找。
public interface GenericDao< T,PK extends Serializable> {
T create(T t);
T读(PK id);
T update(T t);
void delete(T t);
}
界面的impl
@Component
public class GenericDaoJpaImpl< T,PK extends Serializable>
实现GenericDao< T,PK> {
protected Class< T> entityClass;
@PersistenceContext
受保护的EntityManager entityManager;
$ b $ 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);
}
}
}
异常
导致:
org.springframework.beans.BeanInstantiationException:无法实例化bean类[dao.GenericDaoJpaImpl]:
构造函数抛出异常;嵌套异常是
java.lang.ClassCastException:java.lang.Class不能转换为java.lang.reflect.ParameterizedType
如何解决这个问题,以及 ParameterizedType
的含义以及为什么我们必须在构造函数中使用它?
当我评论构造函数时,它的工作原理除 public T read(PK id)
我得到了空指针异常
$ pre $ 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>实现GenericDao< T,PK>
。
ClassCastException
因为 getClass()。getGenericSuperclass()
返回 Class< java.lang.Object>
的一个实例,它是 Type
( java.lang.Class
implements java.lang.reflect.Type
),但不是 ParameterizedType
。事实上, Class< java.lang.Object>
的实例是由 getClass()。getGenericSuperclass()
对于直接超类为 java.lang.Object
的每个类。因此,构造函数像
public GenericDaoJpaImpl(){
已被参数化为 type erasure 。但是,如果代码片段1是通过类似于
ParameterizedType genericSuperclass =(ParameterizedType)getClass()。getGenericSuperclass );
this.entityClass =(Class< T>)genericSuperclass.getActualTypeArguments()[0];
$ b $ p
$ b适用于声明类似于
的类。AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl< AlerteAcheteur,Integer>
。但是,这正是你不想声明你的DAO的原因。
片段1,如果从
GenericDaoJpaImpl
,将打印T
和PK
(它们都是sun.reflect .generics.reflectiveObjects.TypeVariableImpl
)。
片段1
类型[] genericInterfaces = getClass()。getGenericInterfaces();
ParameterizedType genericInterface =(ParameterizedType)genericInterfaces [0];
System.out.println(genericInterface.getActualTypeArguments()[0]);
System.out.println(genericInterface.getActualTypeArguments()[1]);
Snippet 2
@Bean(name =alerteVendeurDao)
public GenericDao< AlerteVendeur,Long> alerteVendeurDao(){
返回新的GenericDaoJpaImpl< AlerteVendeur,Long>();
$ / code>即使在
@中有类似Snippet 2的内容,配置
-annotated类,在运行时不可能知道 GenericDaoJpaImpl的方式执行的,AlerteAcheuteurDao实现了GenericDao< AlerteAcheuteur,Long>
,class somepackage.entity.AlerteAcheteur
和将会打印出java.lang.Long
(因为这些参数在编译时是明确的和已知的)。
最后,组件扫描甚至在逻辑上不适用于
GenericDaoJpaImpl
。@豆组件
-annated类是Singleton - 放大。除了仅创建一个实例之外,我们怎么知道这个单例DAO应该在哪个实体上运行呢?尽管如此,容器仍然能够实例化GenericDaoJpaImpl
,因为在运行时类型信息已经被擦除( type erasure!)。
此外,在相关案例中,建议使用更具体的 <$ c $注释DAO c> @Repository ,而不是
@Component
。
最好的选择是什么?
在您的特定情况下,最好的办法是将实体类声明为构造函数参数。通过这种方式,可以在Spring配置中通过传递适当的构造函数参数来创建大量的特定于实体的
GenericDaoJpaImpl
实例。
GenericDaoJpaImpl.java
public class GenericDaoJpaImpl< T,PK extends Serializable>
实现GenericDao< T,PK> {
private final Class< T> entityClass;
@PersistenceContext
受保护的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
请注意,也可以通过基于构造函数的依赖注入。
@Configuration
@ComponentScan(somepackage.service)//扫描服务,但不适用于DAO!
public class Config {
@Bean(autowire = Autowire.BY_NAME)
public GenericDaoJpaImpl< AlerteAcheteur,Long> alerteAcheteurDao(){
返回新的GenericDaoJpaImpl< AlerteAcheteur,Long>(AlerteAcheteur.class);
}
@Bean(autowire = Autowire.BY_NAME)
public GenericDao< AlerteVendeur,Long> alerteVendeurDao(){
返回新的GenericDaoJpaImpl< AlerteVendeur,Long>(AlerteVendeur.class);
}
//其他DAO
...
}
AlerteServiceImpl.java (看起来如何)
请注意,字段名称是很重要,因为DAO是按名称自动装配的。如果您不想命名
alerteAcheteurDao
等字段,可以使用@Qualifier
与@Autowired $
@Service
公共类AlerteServiceImpl实现AlerteService {
@Autowired
private GenericDao< AlerteAcheteur,Long> alerteAcheteurDao;
@Autowired
private GenericDao< AlerteVendeur,Long> alerteVendeurDao;
...
}
这相当优雅解。您不需要像
那样的垃圾邮件类。AlerteAcheteurGenericDaoJpaImpl扩展了GenericDaoJpaImpl< AlerteAcheteur,Integer>
。在添加新的实体时,您只需添加GenericDaoJpaImpl
的新实例到Spring配置中。
我希望这会有帮助。
I try this generic code because i don't want to create a dao class for each entity i have in my database because i have 80 ones specially for those who i will excecute just CRUD query. because in most case i need just to persist or make a find by id.
public interface GenericDao<T, PK extends Serializable> { T create(T t); T read(PK id); T update(T t); void delete(T t); }
the impl of the interface
@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); } } }
the exception
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
How to resolve this and what means this
ParameterizedType
and why we have to use it in the constructor ?and when i comment the constructor it works except for
public T read(PK id)
i gotnull pointer exception
public GenericDaoJpaImpl() { // ParameterizedType genericSuperclass = (ParameterizedType) getClass() // .getGenericSuperclass(); // this.entityClass = (Class<T>) genericSuperclass // .getActualTypeArguments()[0]; }
I use it like this :
@Autowired private GenericDaoJpaImpl<AlerteAcheteur, Integer> acheteurAlerteDao;
i don't want to create a
abstract
class and extend it like this :public class AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer> ... { } @Autowired private AlerteAcheteurGenericDaoJpaImpl<AlerteAcheteur, Integer> acheteurAlerteDao;
解决方案Unfortunately, there is no way to make it work exactly as you want.
Part 1. Why it does not work
Note the exact declaration of
GenericDaoJpaImpl
-
GenericDaoJpaImpl<T, PK extends Serializable> implements GenericDao<T, PK>
.
ClassCastException
is thrown becausegetClass().getGenericSuperclass()
returns an instance ofClass<java.lang.Object>
, which isType
(java.lang.Class
implementsjava.lang.reflect.Type
), but notParameterizedType
. In fact, an instance ofClass<java.lang.Object>
is returned bygetClass().getGenericSuperclass()
for every class whose direct superclass isjava.lang.Object
. Thus, a constructor likepublic GenericDaoJpaImpl() { ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0]; }
works for a class declared like
AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
. But this is exactly how you do not want to declare your DAOs.Snippet 1, if ran from your
GenericDaoJpaImpl
, will printT
andPK
(both of them will be the instances ofsun.reflect.generics.reflectiveObjects.TypeVariableImpl
).Snippet 1
Type[] genericInterfaces = getClass().getGenericInterfaces(); ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0]; System.out.println(genericInterface.getActualTypeArguments()[0]); System.out.println(genericInterface.getActualTypeArguments()[1]);
Snippet 2
@Bean(name = "alerteVendeurDao") public GenericDao<AlerteVendeur, Long> alerteVendeurDao() { return new GenericDaoJpaImpl<AlerteVendeur, Long>(); }
Even if there is something like Snippet 2 in
@Configuration
-annotated class, at runtime it is impossible to know whatGenericDaoJpaImpl
has been parameterized to because of type erasure. However, if Snippet 1 was executed from something likeAlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>
,class somepackage.entity.AlerteAcheteur
andclass java.lang.Long
would be printed (because these parameters are unambiguous and known at compile time).Finally, the component scanning is not even logically applicable to
GenericDaoJpaImpl
. Beans of@Component
-annotated classes are "Singleton"-scoped. Besides the fact that only one instance will be created, how would we even know what entity this singleton DAO is supposed to operate on? Nevertheless, the container is still able to instantiateGenericDaoJpaImpl
, because at runtime the type information is already erased (type erasure!).Moreover, in relevant cases it is recommended to annotate DAOs with more specific
@Repository
, instead of@Component
.Part 2. What could be the best bet?
In your particular case the best bet is to declare an entity class as a constructor parameter. In this way, it is possible to create numerous entity-specific
GenericDaoJpaImpl
instances in Spring configuration by passing appropriate constructor argument to each of them.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
Please note that it is also possible to do the same in XML via constructor-based dependency injection.
@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 (how could it look like)
Please note that field names are important, because DAOs are autowired by name. If you do not want to name fields like
alerteAcheteurDao
, you can use@Qualifier
with@Autowired
.@Service public class AlerteServiceImpl implements AlerteService { @Autowired private GenericDao<AlerteAcheteur, Long> alerteAcheteurDao; @Autowired private GenericDao<AlerteVendeur, Long> alerteVendeurDao; ... }
This is quite an elegant solution. You don't have to spam classes like
AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
. Upon adding new entities, you just have to add new instances ofGenericDaoJpaImpl
to Spring configuration.I hope this will be helpful.
这篇关于ParameterizedType并创建一个通用的dao的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!