简体   繁体   中英

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

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);

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;

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 );

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.

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.

The bad point of such design -- what would you do to perform some non-trivial actions? And what about actions on many object? When the number of objects used in request will reach some significant amount (say, 1000) you will face a great slowdown due to multiple db requests.

From my experience the good way it to create some class like presented GenericDao and then derive particular DAO's from it. It allows to put some useful common methods in GenericDao and implement specific methods in particular derivatives.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM