Generic DAO with Hibernate and Spring, is their a better way than this?

17,622

Solution 1

I think your solution is fine but you do not need the class parameter T. It just limits you and does not allow to re-use the same DAO for Integers and Strings (for example).

Save method does not need this type at all.

Methods like get() or find() should receive generic type themselves:

public <T> T findById(Class<T> clazz, Serializable id);

public <T> List<T> listAll( Class<T> clazz );

Solution 2

A good place to start is this Generic DAO article its from 2006 but has some good information in it. To update the generic DAO for Spring, hibernate and annotations this is what I have done. Also this newer article is quite useful as well.

All identifier is is a generic intferface to make sure the class has a I getId() and setId(I id)

Create a Generic DAO interface

public interface GenericDao<T extends Identifier<I>, I extends Serializable> {
    public T find(I id);
    public void delete(T obj);
    public void saveOrUpdate(T obj);
}

Create your GenericDAO Implementation

public abstract class GenericDaoImpl<T extends Identifier<I>, I extends Serializable> implements GenericDao<T, I>{

    private Class<T> type;

    @Autowired
    private SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    protected SessionFactory getSessionFactory() {
        if (sessionFactory == null)
            throw new IllegalStateException("SessionFactory has not been set on DAO before usage");
        return sessionFactory;
    }

    public Class<T> getType() {
        return type;
    }

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

    @Transactional(readOnly = true)
    @Override
    public T find(I id) {
        return (T) getSessionFactory().getCurrentSession().get(getType(), id);
    }

    @Transactional
    @Override
    public void delete(T obj) {
        getSessionFactory().getCurrentSession().delete(obj);
    }

    @Transactional
    @Override
    public void saveOrUpdate(T obj) {
        getSessionFactory().getCurrentSession().saveOrUpdate(obj);
    }
}

Object DAO interface:

public interface SomeObjectDao extends GenericDao<SomeObject, Long>{
}

Object DAO implementation

@Repository
public class SomeObjectDaoImpl extends GenericDaoImpl<SomeObject, Long> implements SomeObjectDao {

}

now in Any class that needs it, like a service class, you can get autowiring by just adding the object class dao that you need

@Autowired
private SomeObjectDao someObjectDao;

Solution 3

Better than writing it by yourself would be using

In both it will look very similar: only the interface, no Implmentation:

public interface UserDao extends GenericDao<User, Long> {
     User findByLogin(String login);         
}

If you are interested in, look at the documentation.

Solution 4

It looks like you are passing the type to the dao just so you can get the correct type for the generics in the dao. Rather than do that you could use Spring's java configuration which would allow you to have a method something like:

@Bean(name="myAsdfDao")
public GenericDao<MyAsdf, MyAsdfId> getMyAsdfDao() {
    return new GenericDao<MyAsdf, MyAsdfId>();
}

which would let you keep entity specific daos without having to pass the type to a constructor through xml config. This would be in an @Configuration annotated class that provides java based configuration for spring.

Share:
17,622
NimChimpsky
Author by

NimChimpsky

side hustle : metriculous.network Spring is too bloated, I created my own web app framework Infrequent tweets What if programming languages were methods to eat an orange?

Updated on June 04, 2022

Comments

  • NimChimpsky
    NimChimpsky almost 2 years
    public class GenericDao <T, PK extends Serializable> {
    
        private final Class<T> type;
    
        @Resource(name = "sessionFactory")
        private SessionFactory sessionFactory;
    
        public GenericDao(final Class<T> type) {
        this.type = type;
        }
    
        public PK save(final T o) {
        return (PK) sessionFactory.getCurrentSession().save(o);
        }
    // ... get,delete, etc
    

    App context bean:

    <bean id="fooDao" class="com.mycompany.dao.GenericDao">
            <constructor-arg>
                <value>com.mycompany.Foo</value>
            </constructor-arg>
        </bean>
    

    And in service layer invoke like so :

    @Autowired
    private GenericDao<Foo, Integer> fooDao;
    ...
    public doStuffIncludingSave(Foo foo)
    fooDao.save(foo);
    
  • NimChimpsky
    NimChimpsky about 12 years
    and then I could remove the constructor argument and use the same implemtation to find/delete any type ?
  • NimChimpsky
    NimChimpsky about 12 years
    and how would I invoke with dependency injection ?
  • NimChimpsky
    NimChimpsky about 12 years
    what would it look like in either of them ?
  • NimChimpsky
    NimChimpsky about 12 years
    aha, so I would have different methods on the class for each entity type (foo, bar etc).
  • Glenn Lawrence
    Glenn Lawrence over 10 years
    Which class is your Identifier? e.g. org.springframework.expression.spel.ast.Identifier or org.hibernate.metamodel.relational.Identifier. Either way I get a compile error saying: "Identifier does not take parameters"
  • Sebastien
    Sebastien about 10 years
    Neither is what I used, Made it myself: public interface Identifier<I extends Serializable> { I getId(); void setId(I identifier); }
  • Dev
    Dev about 9 years
    vary helpful answer @Sebastien
  • Ayman Al-Absi
    Ayman Al-Absi almost 9 years
    This implementation does not have Service interface and implementation. To let it work without Synchronization error, make sure to change @EnableTransactionManagement in Configuration class to @EnableTransactionManagement(proxyTargetClass=true). Then move @Transactional annotation to Service Implementation class instead of DaoImpl Class which already annotated with @Repository
  • Erlan
    Erlan over 8 years
    It would be nice if there was short explanation of what it it does?