Generic DAO with CDI error = can not be cast to java.lang.reflect.ParameterizedType

1

I'm trying to implement a generic DAO with CDI. Apparently I'm doing it right, however I'm getting the following error:

  

can not be cast to java.lang.reflect.ParameterizedType

This error is pointing me to this line of my Repository class:

Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

IRepository interface code

package br.com.amarildo.repository;

import java.util.List;

public interface IRepository<T> {

    public T porId(Long id);

    public T guardar(T entity);

    public void alterar(T entity);

    public void remover(T entity);

    public List<T> todos();

}

Repository Class code

package br.com.amarildo.repository;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

public class Repository<T> implements IRepository<T> {

    protected final EntityManager manager;
    private final Class<T> clazz;

    @SuppressWarnings("unchecked")
    @Inject
    public Repository(EntityManager manager) {
        this.manager = manager;
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public T porId(Long id) {
        return manager.find(clazz, id);
    }

    public T guardar(T entity) {
        return this.manager.merge(entity);
    }

    public void alterar(T entity) {
        this.manager.merge(entity);
    }

    public void remover(T entity) {
        this.manager.remove(entity);
    }

    public List<T> todos() {
        TypedQuery<T> query = manager.createQuery("from " + clazz.getName(), clazz);
        List<T> resultList = query.getResultList();
        return resultList;
    }

}

testRepository class

package br.com.amarildo.repository;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import br.com.amarildo.model.FuncionarioModel;


public class TesteRepository extends Repository<FuncionarioModel> implements Serializable {
    private static final long serialVersionUID = 1L;

    public TesteRepository(EntityManager manager) {
        super(manager);
    }

    public List<String> cargosQueContem(String cargo) {
        TypedQuery<String> query = super.manager.createQuery("select distinct cargo from FuncionarioModel " + "where upper(cargo) like upper(:cargo)", String.class);
        query.setParameter("cargo", "%" + cargo + "%");
        return query.getResultList();
    }

}

Code class OfficialController where I run TesteRepository

package br.com.amarildo.controller;

import java.io.Serializable;

import javax.inject.Inject;

import br.com.amarildo.model.FuncionarioModel;
import br.com.amarildo.repository.TesteRepository;
import br.com.amarildo.util.NegocioException;
import br.com.amarildo.util.Transactional;

public class FuncionarioController implements Serializable {
    private static final long serialVersionUID = 1L;

    @Inject 
    private TesteRepository funcionarioRepository;

    @Transactional
    public FuncionarioModel salvar(FuncionarioModel funcionarioModel) throws NegocioException {
        if (funcionarioModel.getCargo().isEmpty() ) {
            throw new NegocioException("Não é Possivel Salvar Funcionario sem Cargo");
        }
        return this.funcionarioRepository.guardar(funcionarioModel);
    }

    @Transactional
    public void atualizar(FuncionarioModel funcionarioModel) throws NegocioException {
        if (funcionarioModel.getCargo().isEmpty()) {
            throw new NegocioException("Não é possível fazer a Alteração campo cargo está vazio !");
        }
        this.funcionarioRepository.alterar(funcionarioModel);
    }

    @Transactional
    public void excluir(FuncionarioModel funcionarioModel) throws NegocioException {
        funcionarioModel = this.funcionarioRepository.porId(funcionarioModel.getCodigo());
        if (funcionarioModel.getCargo()== null) {
            throw new NegocioException("Não é possível excluir um Funcionario Demitido!");
        }
        this.funcionarioRepository.remover(funcionarioModel);
    }

}

How to solve this?

Here is the error I'm getting:

Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type TesteRepository with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject private br.com.amarildo.controller.FuncionarioController.funcionarioRepository
  at br.com.amarildo.controller.FuncionarioController.funcionarioRepository(FuncionarioController.java:0)

    at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:359)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:281)
    at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:134)
    at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:155)
    at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:518)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:68)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:66)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:63)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:56)
    ... 4 more

jun 29, 2017 10:26:45 AM org.apache.catalina.core.ContainerBase startInternal
GRAVE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at java.util.concurrent.FutureTask.report(Unknown Source)
    at java.util.concurrent.FutureTask.get(Unknown Source)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:441)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:787)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:629)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:351)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:485)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:153)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:924)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:871)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)

Thank you all for the error It was solved as below: I had to do the injection in my TestRepository constructor of the TestRepository class was just that.

@Inject
public TesteRepository(EntityManager manager) {
    super(manager);
} 
    
asked by anonymous 28.06.2017 / 17:44

2 answers

1

Let's see what this line does:

Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

First, we get the superclass, including the generic type:

getClass().getGenericSuperclass()

And then we cast to ParameterizedType , that is, a type that contains generic parameters.

Then we get the generic type:

[...].getActualTypeArguments()[0];

And finally, we forced it with a cast for a class:

Class<T> clazz = (Class<T>) [...]

That is, if you have a BananaRepository extends Repository<Banana> :

  • The getClass() will give you the object that represents the class BananaRepository .

  • The .getGenericSuperclass() var will give Repository<Banana> - Note that this is a parameterized generic type, ie a ParameterizedType , then we can cast it.

  • The .getActualTypeArguments[0] will get the generic type of Repository<Banana> , ie Banana . This is a class, so the cast works.

So far so good, but things can go wrong in other scenarios:

  • If you have MyListRepository extends Repository<List<Banana>> , getActualTypeArguments[0] will bring List<Banana> , which is a parameterized type, not a class. The result of this will be ClasscastException .

  • If you have RawRepository extends Repository , without the generic type, getGenericSuperclass() will return an object of type Class , not ParameterizedType . Other ClassCastException here.

  • If you have BananaPrataRepository extends BananaRepository and BananaRepository extends Repository<Banana> , when instantiating BananaPrataRepository , getGenericSuperclass() will bring BananaRepository instead of Repository<Banana> , which will give you a ClassCastExcepotion .

  • If you instantiate Repository<T> directly, without being subclassed, getGenericSuperclass() will bring Object , and when trying cast ParameterizedType , will ClassCastException . / p>

  • If you have class StrangeRepository2 extends StrangeRepository1<String> and class StrangeRepository1<A> extends Repository<Banana> , the code will think that the generic type is the String class instead of the Banana class. This case in practice does not make sense, but it is important to know that in theory it can occur, and if it does, the wrong generic type will be found.

  • The conclusion is that the Repository class can only be used under the following conditions:

    • It can only be used through a subclass, never directly.

    • Subclasses should declare the generic type of the superclass ( Repository ) in extends .

    • Subclasses should not contain other subclasses themselves.

    Now let's look at the top of your stacktrace:

    java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
        at br.com.amarildo.repository.Repository.<init>(Repository.java:21)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:119)
    

    It shows that Weld (responsible for CDI in JBoss) invoked the constructor of class Repository directly through reflection. This will make you fall into case 4 of the above list.

    That is, where you are injecting a Repository<AlgumaCoisa> , you should do this:

    • Inject a AlgumaCoisaRepository .

    • Working with @Qualifier s.

    • Specify the correct bean to be injected into beans.xml .

    In addition, it is possible to allow subclasses of Repository themselves to have subclasses through the following:

    Class<?> directSubclass = getClass();
    while (directSubclass.getSuperclass() != Repository.class) {
        directSubclass = directSubclass.getSuperclass();
    }
    Type genericSuper = directSubclass.getGenericSuperclass();
    if (!(genericSuper instanceof ParameterizedType)) throw new UnsupportedOperationException("A superclasse definida deve conter os tipos genéricos adequados.");
    Type generic = ((ParameterizedType) genericSuper).getActualTypeArguments()[0];
    if (!(generic instanceof Class<?>)) throw new UnsupportedOperationException("O tipo genérico do repositório deve ser uma classe que não tenha ela memsma outro tipo genérico.");
    Class<T> clazz = (Class<T>) generic;
    

    In addition, the% class of% must be abstract.

        
    29.06.2017 / 06:43
    0

    Pass the generic class by parameter in the constructor

    public Repository(EntityManager manager, Class<T> entityClass) {
        this.manager = manager;
        this.clazz = entityClass;
    }
    
        
    28.06.2017 / 19:06