当前位置: 首页 > 面试题库 >

ParameterizedType并创建通用dao

怀齐智
2023-03-14
问题内容

我尝试使用此通用代码,因为我不想为数据库中的每个实体创建一个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);

}

接口的印象

@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。实际上,对于每个直接超类为的类,Class<java.lang.Object>将由的实例返回。因此,像getClass().getGenericSuperclass()``java.lang.Object

public GenericDaoJpaImpl() {
    ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
    this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
}

适用于声明为的类AlerteAcheteurGenericDaoJpaImpl extends GenericDaoJpaImpl<AlerteAcheteur, Integer>。但这正是您不希望声明DAO的方式。

摘录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>();
}

即使存在Snippet
2中带@Configuration注释的类之类的内容,在运行时也无法知道GenericDaoJpaImpl由于类型Erase导致参数化了。但是,如果代码片段1是从类似的代码执行的AlerteAcheuteurDao implements GenericDao<AlerteAcheuteur, Long>class somepackage.entity.AlerteAcheteur并且class java.lang.Long将被打印(因为这些参数是明确的,并且在编译时是已知的)。

最后,组件扫描甚至在逻辑上都不适用于GenericDaoJpaImpl。带@Component注释类的Bean 是“
Singleton”作用域的。除了将只创建一个实例这一事实之外,我们如何知道该单例DAO应该在哪个实体上运行?尽管如此,容器仍然能够实例化GenericDaoJpaImpl,因为在运行时类型信息已经被擦除(类型擦除!)。

此外,在相关情况下,建议使用更具体的注释DAO
@Repository,而不是@Component

第2部分。什么是最好的选择?

在您的特定情况下,最好的选择是将实体类声明为构造函数参数。这样,可以GenericDaoJpaImpl通过将适当的构造函数参数传递给每个实例来在Spring配置中创建大量特定于实体的实例。

通用DaoJpaImpl.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>。添加新实体后,您只需GenericDaoJpaImpl向Spring配置添加新实例。

我希望这会有所帮助。



 类似资料:
  • 问题内容: (对我来说)有点危险。基本上,我想有两个从同一个类继承的不同单例。在任何一种情况下,我都想使用某个本身派生的类。所以我既有又有。和被用作使用在其胃和分别。我在所有领域都失败了。上一次尝试是工厂模式,该模式只是将Swift 1.2引入了Segfault: 如前所述,我还尝试使以下模式通用: (这不是您所看到的通用方法。但是我的所有尝试都失败了,因此我展示了我的出发点。) 无论如何,我似乎

  • 问题内容: 我正在编写一个应用程序,以便在对等网络中进行一些分布式计算。在定义网络时,我有两个类P2PNetwork和P2PClient。我希望这些是通用的,因此具有以下定义: 与P2PClient一起定义setNetwork(T network)的方法。我希望用这段代码描述的是: P2PNetwork由某种类型的客户端组成 P2PClient只能属于其客户端与该客户端具有相同类型的网络(循环引用

  • 问题内容: 该代码似乎不起作用,它将抛出异常: java.lang.ClassCastException:[Ljava.lang.Object; 无法转换为… 有人可以告诉我如何创建具有通用类型的数组吗?谢谢。 问题答案: 您不能:必须将类作为参数传递:

  • 问题内容: 嗨,我正在尝试创建一组六边形集(六边形是我创建的类)。 但是当我尝试编译时出现此错误 我该如何解决? 问题答案: 您不能使用泛型创建数组。使用或代替。 这是正式的解释。

  • 这就是我想做的: > 使用pdfbox制作2个不同的pdf文件 使用pdfmerger将这两个文件合并在一起 如果要将#1保存到服务器端本地硬盘并加载#2的文件,我知道如何执行此操作。但我想做的是使用“直接从内存”。我已经搜索了这个pdfboxes中的所有方法,但仍然找不到它。 这是我从本地文件获取的代码 谢谢你。

  • 我使用RxJava(聚合服务)编写了一个Spring Boot微服务来实现以下简化用例。大局是当讲师上传课程内容文档时,应该生成并保存一组问题。 用户将文档上载到系统 当用户上载文档时,会从中创建许多问题(可能有数百个左右)。这里的问题是,我正在按顺序发布一个问题,以便CRUD服务保存它们。由于IO密集型网络调用,这会大大降低操作速度,因此完成整个过程大约需要20秒。以下是当前代码,假设所有问题都