Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using TransactionProxyFactoryBean with Spring and Hibernate

I have an old project that I'm trying to upgrade to spring 4 / hibernate 4.

We use this approach to configuring our transactions.

That is to say, in XML we define a transaction manager like:

<bean id="abstractTransactionProxy"
abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="hibernateTransactionManager"/>
<property name="transactionAttributes">
    <props>
        <prop key="create*">PROPAGATION_REQUIRED</prop>
        <prop key="update*">PROPAGATION_REQUIRED</prop>
        <prop key="delete*">PROPAGATION_REQUIRED</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
    </props>
</property>

and

<bean id="MySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource"     ref="myDataSource" />
    <property name="configLocation" value="WEB-INF/hibernate.cfg.xml" />
</bean>

and then each of our service beans are defined like:

<bean id="customerService" parent="abstractTransactionProxy">
    <property name="target" ref="customerTarget"/>
    <property name="personDAO" ref="personDAO" />
</bean>

and the DAOs are defined like:

    <alias name="personDaoImpl" alias="personDAO"/>
<bean id="personDaoImpl" 
    class="com.foo.bar.hibernate.PersonDaoImpl">
    <property name="sessionFactory" ref="MySessionFactory" />
</bean>

PersonDaoImpl (and all the other DAOs) have many methods that look like this (all of the DAOs extend HibernateDaoSupport):

    public List<Person> getPersonByCriteria(final String criteria) {
    List<Person> results = 
        (List<Person>) this.getHibernateTemplate().executeFind(new HibernateCallback<List<Person>>(){

        public List<Person> doInHibernate(Session session) throws HibernateException, SQLException {
            List<Person> results = (List<Person>) session.createQuery(MYSQLHERE).setString("criteria", criteria).list();
            return results;
        }
    });
    return results;
}

Now, the problem I Face at this point is getting rid of our dependency on extending our DAOs from HibernateDaoSupport, and by extension, on using it's HibernateTemplate helper object.

Here's the steps I've taken:

  • Simply remove them from the DAO methods - don't extend HibernateDaoSupport, remove the HibernateTemplate callback. This looks like:

        public List<Person> getPersonByCriteria(final String criteria) {
            List<Person> results = (List<Person>) getSessionFactory().getCurrentSession().createQuery(MYSQLHERE).setString("criteria", criteria).list();
    
            return results;
        }
    

Of course this gives me compile-time errors because 'getSessionFactory' was part of the base class 'HibernateDaoSupport' which I just removed.

  • So I implemented a 'MyHibernateDaoSupport' Class, and had my DAO extend that instead. It looks like:

    public class MyHibernateDaoSupport {
    
        private SessionFactory sessionFactory;
        protected Session session;
        protected static Logger logger = Logger.getLogger(MyHibernateDaoSupport.class);
    
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
            session = this.sessionFactory.getCurrentSession();
        }
    
        public SessionFactory getSessionFactory() {
        return this.sessionFactory;
        }
    
        public Session getHibernateTemplate() {
            session = this.sessionFactory.getCurrentSession();
            return session;
        }
    
        public MyHibernateDaoSupport() {
        }
    

    }

Now I can build, deploy and run and I hit this error message:

No session available in this thread

How can I make this work?

  • In simple terms, what was the 'hibernateCallback()', and it's anoymous inner class actually doing?

  • How can I make my project's transactions work as before, without using HibernateDaoSupport and HibernateTemplate classes?

I've tried tagging my 'CustomerService' (the business logic object that calls 'PersonDao') with @transactional but (A) I see the same error regardless and (B) I'd really prefer to be able to do this programmatically, to reduce the scale of changes to the way the code works now. I also tried putting the transactions at the DAO level instead, and met the same issue.

Apologies if that was way too much detail for the Stack overflow format.

like image 805
Paul Avatar asked Apr 28 '15 15:04

Paul


People also ask

What is the use of @transactional in Spring?

The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.

Which Transaction Manager implementation would be most appropriate?

In this model, Spring uses AOP over the transactional methods to provide data integrity. This is the preferred approach and works in most of the cases. Support for most of the transaction APIs such as JDBC, Hibernate, JPA, JDO, JTA etc. All we need to do is use proper transaction manager implementation class.

How would you enable transactions in Spring and what are their benefits?

The Spring Framework provides a consistent abstraction for transaction management that delivers the following benefits: Consistent programming model across different transaction APIs such as Java Transaction API (JTA), JDBC, Hibernate, Java Persistence API (JPA), and Java Data Objects (JDO).

Why is it best practice to mark transactions read only?

Transactions indeed put locks on the database — good database engines handle concurrent locks in a sensible way — and are useful with read-only use to ensure that no other transaction adds data that makes your view inconsistent.


1 Answers

  1. You need to inject the SessionFactory into your DAO classes:

    @Repository
    public class PersonDAOImpl implements PersonDAO {
    
        @Autowired
        private SessionFactory sessionFactory;
    
        public List<Person> getPersonByCriteria(final String criteria) {
            List<Person> results = (List<Person>) getSessionFactory().getCurrentSession().createQuery(MYSQLHERE).setString("criteria", criteria).list();
            return results;
        }
    }
    
  2. Add @Transactional to your service methods

  3. Add HibernateTransactionManager

  4. Instruct Spring to use declarative transactions:

    <tx:annotation-driven transaction-manager="transactionManager"/>
    
  5. Make sure you use LocalSessionFactoryBean for creating the SessionFactory

Here's a detailed step-by-step tutorial for configuring Spring and Hibernate 4

like image 177
Vlad Mihalcea Avatar answered Oct 12 '22 17:10

Vlad Mihalcea