ParameterizedType并创建一个通用的dao [英] ParameterizedType and creating a generic dao

查看:154
本文介绍了ParameterizedType并创建一个通用的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(){
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 已被参数化为 type erasure 。但是,如果代码片段1是通过类似于的方式执行的,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 got 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 :

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 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. In fact, an instance of Class<java.lang.Object> is returned by getClass().getGenericSuperclass() for every class whose direct superclass is 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>. But this is exactly how you do not want to declare your DAOs.

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

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 what GenericDaoJpaImpl has been parameterized to because of type erasure. 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).

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 instantiate GenericDaoJpaImpl, 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 of GenericDaoJpaImpl to Spring configuration.

I hope this will be helpful.

这篇关于ParameterizedType并创建一个通用的dao的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆