Class derived from generic class don't get the

2020-05-06 08:26发布

问题:

In my spring project, I have this template for my Dao classes:

public class Dao<E> {

    private final E entity;

    @Autowired
    SessionFactory sessionFactory;

    protected Session getCurrentSession(){
        return sessionFactory.getCurrentSession();
    }

    public Dao(E entity) {  
        this.entity = entity;
    }

    public Dao(Class<?> classe) {
        this.entity = (E) classe;
    }

    public E getEntity() {
        return this.entity;
    }

    @Transactional
    public boolean persist(E transientInstance) {
        sessionFactory.getCurrentSession().persist(transientInstance);
        return true;
    }

    @Transactional
    public boolean remove(E transientInstance) {
        sessionFactory.getCurrentSession().delete(transientInstance);
        return true;
    }

    @Transactional
    public boolean merge(E detachedInstance) {
        sessionFactory.getCurrentSession().merge(detachedInstance);
        return true;
    }

    @Transactional
    public E findById(int id) {
        E instance = (E) sessionFactory.getCurrentSession().get(entity.getClass(), id);
        return instance;
    }

    @Transactional
    public E findByField(String field, String value) {
        String expressao = entity.toString();
        String nome_classe = new String();
        StringTokenizer st = new StringTokenizer(expressao);
        while (st.hasMoreTokens()) {
            nome_classe = st.nextToken();
        }
        String query = "from "+nome_classe+" where "+field+" = :data";

        Query q = sessionFactory.getCurrentSession().createQuery(query);
        q.setParameter("data", value);
        E instance = (E) q.uniqueResult();
        return instance;
    }

    @Transactional
    public List<E> findAll() {
        List<E> instance = (List<E>) sessionFactory.getCurrentSession().createSQLQuery("select * from usuario").list();
        return instance;
    }

}

Each one of my Dao classes have this structure:

@Repository
public class UsuarioHome extends Dao<Usuario> {

    public UsuarioHome() {
        super(Usuario.class);
    }

}

Which means that when I call the methods findById, findByField, findAll, I should receive a object from types Usuario, Usuario and List.

The two fist classesa re returning the right value, but the Last one don't. When I run this method (from my service class):

@Transactional
public List<Usuario> listagem_usuarios() {
    List<Usuario> lista = usuario.findAll();
    System.out.println("listagem_usuario find "+lista.size()+" users");
    System.out.println(lista.getClass().getName());
    for(int i=0; i<lista.size(); i++) {
        System.out.println("i = "+i+" {");
        if(lista.get(i) instanceof Usuario)
            System.out.println("usuario");
        else if(lista.get(i) instanceof Object)
            System.out.println("object");
        else
            System.out.println("outro");
        System.out.println("}");
    }
    return lista;
}

I am receiving "object" as response, when I should see "usuario". Anyone can tell what I doing wrong here?

回答1:

The problem is in your generic DAO findAll method; you are using query string select * from usuario

@Transactional
public List<E> findAll() {
        List<E> instance = (List<E>) sessionFactory.getCurrentSession().createSQLQuery("select * from usuario").list();
        return instance;
}

How try with like this;

@Transactional
public List<E> findAll() {
return (List<E>) sessionFactory.getCurrentSession().createCriteria(entity)
        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
}

Here are also suggestions for your template DAO class;

  1. I prefer to this variable declaration

    private Class<E> entity;
    
  2. And in constructor with class argument

    public Dao(Class<E> clazz) {
        this.entity = clazz;
    }
    


回答2:

I strongly suspect that this is the problem:

public Dao(Class<?> classe) {
    this.entity = (E) classe;
}

You're effectively casting Usuario.class to Usuario. That's not right. The class and an instance of the class are different things. The class is not an entity - it's the type of the entity.

It's not clear what you're trying to do with the entity field (you're calling toString() on it - what do you expect the result to be?) but I suspect you should actually have an entityClass field instead, which is a Class<E> or a Class<? extends E>. Fundamentally, you need to distinguish between the two concepts though.



回答3:

Your findAll() method is wrong. It uses a SQL query which selects all the columns of the table named usuario, which returns a List<Object[]>, instead of using an HQL query returning all the instances of the generic entity. The code should be

String hql = "select u from " + entity.getName() + " u";
return (List<E>) sessionFactory.getCurrentSession().createQuery(hql).list();

This is correct provided the entity field contains the entity class (i.e. Usuario.class in this case). See Jon Skeet's answer for an explanation on this.