Manual rollback Spring MVC + Hibernate

1.7k Views Asked by At

I am using Spring MVC + Hibernate

//Class for Generic Methods for **save and update** 

@Service("PersistenceTemplate")
@Transactional
public class PersistenceTemplate {

@Resource(name = "sessionFactory")
private SessionFactory sessionFactory;

// SAVE 
public <T> long save(T entity) throws DataAccessException {
    Session session = sessionFactory.getCurrentSession();
    long getGenVal=(Long) session.save(entity);
    return getGenVal;
}
//UPDATE
public <T> void update(T entity) throws DataAccessException {
    sessionFactory.getCurrentSession().update(entity);
}

}

AT Controller

@Resource(name = "PersistenceTemplate")
private PersistenceTemplate pt;
long result=pt.save(receiveTrxObj1);
pt.Update(receiveTrxObj2);

Problem statement

How to roll back save statement if Update fails to update the entity in database ?

3

There are 3 best solutions below

0
On

first your @Service("PersistenceTemplate") should be marked as @Repository because its doing the work of DAO layer.

from the controller you should call a Service which should be annotated with @service and @Transactional and inside this service you create a method which will call a DAO layer.

if save or Update fails to update the entity in database the method from which it is called (ie. the method in service layer) will not complete and the transaction is cancelled automatically because persistence objects are synchronized with database near the end of the completion of method of service layer once the control comes back to it.

See the below example.

@Service("authorLoadService")
@Transactional
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS,value="request")
public class AuthorEntityLoadService implements EntitiesLoadService{

    private AuthorDAO authorDao;//this is my DAO





    @Autowired
    @Qualifier("authorDAO")
    public void setAuthorDao(AuthorDAO authorDao) {
        this.authorDao = authorDao;
    }

    @Override
    public void deleteEntities(Object o) {
        // TODO Auto-generated method stub

    }

    @Override
    public void loadEntities(Object o) {
        Set<author_pojo> author=(Set<author_pojo>)o;
        Iterator<author_pojo> itr=author.iterator();

        while (itr.hasNext()) {
            author_pojo authorPojo = (author_pojo) itr.next();
            authorDao.save(authorPojo);

        }


    }

    @Override
    @Transactional(readOnly=true)
    public List getEntities() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    @Transactional(readOnly=true)
    public Object getEntity(Object o) {
        String author=(String)o;
    author_pojo fetAuthor=authorDao.findOneByName(author);

        return fetAuthor;
    }

}

My Abstract Generic DAO

public abstract class AbstractHibernateDAO<T extends Serializable> {

    public Class<T> clazz;//class object reference

    protected SessionFactory mysessionFactory;


    @Autowired
    public void setMysessionFactory(SessionFactory mysessionFactory) {
        this.mysessionFactory = mysessionFactory;
    }

    public T findOneByName(final String name){

        return (T) getCurrentSession().createQuery("from "+clazz.getName()).uniqueResult();
    }


    public void setClazz(final Class<T> clazzToSet) {
        this.clazz = clazzToSet;
    }

    public T findOne(final Long id) {
        return (T) getCurrentSession().get(clazz, id);
    }

    @SuppressWarnings("unchecked")
    public List<T> findAll() {
        return getCurrentSession().createQuery("from " + clazz.getName()).list();
    }

    public void save(final T entity) {
        getCurrentSession().merge(entity);
    }

    public void update(final T entity) {
        getCurrentSession().update(entity);
    }

    public void delete(final T entity) {
        getCurrentSession().delete(entity);
    }

    public void deleteById(final Long entityId) {
        final T entity = findOne(entityId);
        delete(entity);
    }

    protected Session getCurrentSession() {

        return mysessionFactory.getCurrentSession();
    }
}

my concerete DAO

@Repository("authorDAO")
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS,value="request")
public class AuthorDAO extends AbstractHibernateDAO<author_pojo> {

    public AuthorDAO() {

        setClazz(author_pojo.class);
    }

    public author_pojo findOneByName(final String name){
        System.out.println(clazz);
        return (author_pojo) getCurrentSession().createQuery("from "+clazz.getName() +" where authorName=:name").setParameter("name", name).uniqueResult();
    }



}
0
On

You could use application level exception to rollback your entity operations. When this custom exception thrown the related operations rollback. Please see following documents to see how to define custom rollback in Spring.

7
On

For you to be able to rollback the save if the update fails, the save and update have to occur within the same transaction. Services are a natural place to put DAO calls that need to execute within the same transaction.

Putting a @Transactional annotation on the controller method would create complications due to proxying the controller, see the Spring MVC documentation, 17.3.2:

A common pitfall when working with annotated controller classes happens when applying functionality that requires creating a proxy for the controller object (e.g. @Transactional methods). Usually you will introduce an interface for the controller in order to use JDK dynamic proxies. To make this work you must move the @RequestMapping annotations, as well as any other type and method-level annotations (e.g. @ModelAttribute, @InitBinder) to the interface as well as the mapping mechanism can only "see" the interface exposed by the proxy. Alternatively, you could activate proxy-target-class="true" in the configuration for the functionality applied to the controller (in our transaction scenario in ). Doing so indicates that CGLIB-based subclass proxies should be used instead of interface-based JDK proxies. For more information on various proxying mechanisms see Section 9.6, “Proxying mechanisms”.

See this question for what goes in a service as opposed to in a controller.