[英]ParameterizedType and creating a generic dao
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. 我尝试这个通用代码,因为我不想为我的数据库中的每个实体创建一个dao类,因为我有80个专门用于那些我将只执行CRUD查询的人。 because in most case i need just to persist or make a find by id.
因为在大多数情况下我只需坚持或通过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 接口的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);
}
}
}
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 ? 如何解决这个问题以及这个
ParameterizedType
是什么意思以及为什么我们必须在构造函数中使用它?
and when i comment the constructor it works except for public T read(PK id)
i got null pointer exception
当我对构造函数进行注释时,除了
public T read(PK id)
之外,它还null 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 : 我不想创建一个
abstract
类并像这样扩展它:
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. 不幸的是,没有办法让它完全按照你的意愿工作。
Note the exact declaration of GenericDaoJpaImpl
- 注意
GenericDaoJpaImpl
的确切声明 -
GenericDaoJpaImpl<T, PK extends Serializable> implements GenericDao<T, PK>
. GenericDaoJpaImpl<T, PK extends Serializable> implements GenericDao<T, PK>
。
ClassCastException
is thrown because getClass().getGenericSuperclass()
returns an instance of Class<java.lang.Object>
, which is Type
( java.lang.Class
implements java.lang.reflect.Type
), but not ParameterizedType
. ClassCastException
是因为getClass().getGenericSuperclass()
返回Class<java.lang.Object>
的实例,它是Type
( java.lang.Class
实现java.lang.reflect.Type
),但不是ParameterizedType
。 In fact, an instance of Class<java.lang.Object>
is returned by getClass().getGenericSuperclass()
for every class whose direct superclass is java.lang.Object
. 实际上,
getClass().getGenericSuperclass()
为其直接超类为java.lang.Object
每个类返回Class<java.lang.Object>
的实例。 Thus, a constructor like 因此,构造函数就像
public GenericDaoJpaImpl() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
}
works for a class declared like AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
. 适用于声明为
AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
的类, AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
。 But this is exactly how you do not want to declare your DAOs. 但这正是您不想申报DAO的方式。
Snippet 1, if ran from your GenericDaoJpaImpl
, will print T
and PK
(both of them will be the instances of sun.reflect.generics.reflectiveObjects.TypeVariableImpl
). Snippet 1,如果从
GenericDaoJpaImpl
,将打印T
和PK
(它们都将是sun.reflect.generics.reflectiveObjects.TypeVariableImpl
的实例)。
Snippet 1 片段1
Type[] genericInterfaces = getClass().getGenericInterfaces();
ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0];
System.out.println(genericInterface.getActualTypeArguments()[0]);
System.out.println(genericInterface.getActualTypeArguments()[1]);
Snippet 2 片段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 what GenericDaoJpaImpl
has been parameterized to because of type erasure . 即使在
@Configuration
-annotated类中有类似Snippet 2的东西,在运行时也不可能知道GenericDaoJpaImpl
由于类型擦除而被参数化。 However, if Snippet 1 was executed from something like AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>
, class somepackage.entity.AlerteAcheteur
and class java.lang.Long
would be printed (because these parameters are unambiguous and known at compile time). 但是,如果Snippet 1是从
AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>
class somepackage.entity.AlerteAcheteur
东西AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>
则会打印class somepackage.entity.AlerteAcheteur
和class java.lang.Long
(因为这些参数是明确的并且在编译时是已知的)。
Finally, the component scanning is not even logically applicable to GenericDaoJpaImpl
. 最后,组件扫描在逻辑上甚至不适用于
GenericDaoJpaImpl
。 Beans of @Component
-annotated classes are "Singleton"-scoped. @Component
-annotated类的Bean是“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? 除了只创建一个实例的事实之外,我们怎么会知道这个单例DAO应该在哪个实体上运行呢? Nevertheless, the container is still able to instantiate
GenericDaoJpaImpl
, because at runtime the type information is already erased ( type erasure! ). 尽管如此,容器仍然可以实例化
GenericDaoJpaImpl
,因为在运行时类型信息已经被擦除( 类型擦除! )。
Moreover, in relevant cases it is recommended to annotate DAOs with more specific @Repository
, instead of @Component
. 此外,在相关情况下,建议使用更具体的
@Repository
而不是@Component
来注释DAO。
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. 通过这种方式,可以通过将适当的构造函数参数传递给每个实例,在Spring配置中创建许多特定于实体的
GenericDaoJpaImpl
实例。
GenericDaoJpaImpl.java 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 AnnotationContextConfiguration.java
Please note that it is also possible to do the same in XML via constructor-based dependency injection . 请注意,也可以通过基于构造函数的依赖注入在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 (how could it look like) AlerteServiceImpl.java (看起来怎么样)
Please note that field names are important, because DAOs are autowired by name. 请注意,字段名称很重要,因为DAO按名称自动装配。 If you do not want to name fields like
alerteAcheteurDao
, you can use @Qualifier
with @Autowired
. 如果您不想为
alerteAcheteurDao
命名字段,可以将@Qualifier
与@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>
. 您不需要像
AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
这样的垃圾邮件类AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>
。 Upon adding new entities, you just have to add new instances of GenericDaoJpaImpl
to Spring configuration. 添加新实体后,您只需要将新的
GenericDaoJpaImpl
实例添加到Spring配置中。
I hope this will be helpful. 我希望这会有所帮助。
Your GenericDaoJpaImpl should be abstract. 你的GenericDaoJpaImpl应该是抽象的。 Only concrete descendant type should resolve the generic types T,PK and be defined as Spring beans.
只有具体的后代类型才能解析泛型类型T,PK并定义为Spring bean。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.