6

I've found a few tutorials on how to build a Hibernate DAO with generics, but they all use EntityManager instead of a SessionFactory. My question is how to build a DAO with generics using SessionFactory. I have the below so far:

Interface:

public interface GenericDao<T> {

    public void save(T obj);
    public void update(T obj);
    public void delete(T obj);
    public T findById(long id);
}

Class:

@Repository
public class GenericDaoImpl<T> implements GenericDao<T> {

    @Autowired
    private SessionFactory sessionFactory;

    public void save(T obj) {
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            session.save(obj);
            tx.commit();
        } catch (HibernateException e) {
            if(tx != null)
                tx.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }

    }

    public void update(T obj) {
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            session.update(obj);
            tx.commit();
        } catch (HibernateException e) {
            if(tx != null)
                tx.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }

    }

    public void delete(T obj) {
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            session.delete(obj);
            tx.commit();
        } catch (HibernateException e) {
            if(tx != null)
                tx.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }

    }

    public T findById(long id) {
        // ??
        return null;
    }

I'm unsure how to go about findById using generics. I believe the other methods are right, but correct me if I'm wrong.

SIDE QUESTION: Is using EntityManager more beneficial than using SessionFactory? I saw a few posts on the subject, but would like a few more opinions.

acdcjunior
  • 132,397
  • 37
  • 331
  • 304
Jake Miller
  • 2,432
  • 2
  • 24
  • 39
  • In Java, generics are implemented by erasure and your formal type parameter `T` becomes `Object` at run time. In other words, at run time type `T` does not exist. Any generic method which returns a newly minted `T` instance will therefore require a run time type token which the method can use to reflectively determine the type of instance it will need to create. A more practical signature for `findById(...)` is therefore `public T findById(Class class, long id)`. – scottb Jun 27 '16 at 00:07
  • @scottb so the Class token will be used to determine which type of object the method needs to return? How would I go about this exactly? With the `EntityManager` examples, I saw `entityManager.find(type.class, id);` but I'm unsure of how to do this with `SessionFactory`. – Jake Miller Jun 27 '16 at 00:12
  • If the type you need to return has a no-argument constructor, then the easiest possible way to dynamically mint a new instance of an arbitrary type `T` would be to use the `newInstance()` method of `Class`, eg. `T myObj = class.newInstance();`. Otherwise, you might need to use reflection via the `Class` object to invoke an appropriate constructor with arguments. In such a method, `class` in `Class class` plays the role of a *run time type token*. In Java, these are sometimes necessary precisely because generic types do not exist at run time. – scottb Jun 27 '16 at 00:41

1 Answers1

5

You need to have access to the Class<T> from within that method. You have two options, you can pass the Class<T> into the method:

public T findById(long id, Class<T> clazz) {
    // method implementation
}

Or you can pass the Class<T> into the constructor of the class for use in the method:

@Repository
public class GenericDaoImpl<T> implements GenericDao<T> {

    private Class<T> clazz;

    protected GenericDaoImpl(Class<T> clazz) {
        this.clazz = clazz;
    }

    // other methods omitted

    public T findById(long id) {
        // method implementation
    }
}

And subclasses would pass their class into the superclass:

public class UserDao extends GenericDaoImpl<User> {
    public UserDao() {
        super(User.class);
    }
}

Then, using your clazz instance you can get the entity in your generic method using the Session#get method:

T entity = session.get(clazz, id);

See the following questions for more information:


As far as the side question, the EntityManager is part of JPA (the Java Persistence API). Developing your application using the Java API specification instead of the Hibernate API allows your application to not become dependent on Hibernate. This allows you to switch between popular JPA implementations like Hibernate, OpenJPA, or TopLink without making and changes to your code.

This question has more information on the difference.

Community
  • 1
  • 1
blacktide
  • 10,654
  • 8
  • 33
  • 53
  • Thank you! Another side question - Is there a reason to use `EntityManager` over `SessionFactory` and vice versa? Or do they both do primarily the same thing? – Jake Miller Jun 27 '16 at 00:18
  • No need to add that `super(User.class);` line. The abstract generic class can find out the type argument of its children classes, as seen here: https://github.com/acdcjunior/acdcjunior-github-io-example-projects/blob/master/spring-mvc-jpa-mockito-piloto/src/main/java/net/acdcjunior/piloto/infrastructure/jpa/JpaAbstractRepository.java#L43 – acdcjunior Jun 27 '16 at 00:20
  • 1
    @JakeMiller `EntityManager` is JPA, `SessionFactory` is Hibernate (a JPA implementation). They are not the same thing. `EntityManager` (JPA) is to `Session` (Hibernate) as `EntityManagerFactory` (JPA) is to `SessionFactory` (Hibernate). Usually, we prefer JPA. But if you don't plan to ever change implementation from Hibernate to other (such as TopLink), it shouldn't matter (I mean, in this case, use whatever you prefer). – acdcjunior Jun 27 '16 at 00:22
  • @JakeMiller, I've edited the answer with an explanation for that. – blacktide Jun 27 '16 at 00:22
  • @acdcjunior Very nice! I never thought of doing it that way. – blacktide Jun 27 '16 at 00:24
  • @Casey yeah, just thought I'd mention it, add it as another option to your belt. :) good answer! +1! – acdcjunior Jun 27 '16 at 00:28