Announcement Announcement Module
Collapse
No announcement yet.
Problem with transactions using Spring+Hibernate Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Problem with transactions using Spring+Hibernate

    Hello,

    I'm currently having a problem having a transaction span over two Dao method calls. Allow me to post relevant parts of the application context XML and code to illustrate what I have going on right now.

    The Datasource:

    Code:
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
      <property name="jndiName"><value>java&#58;comp/env/jdbc/BrewerfanDB</value></property>
    </bean>
    The Session Factory:

    Code:
    	<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
    		<property name="dataSource"><ref local="dataSource"/></property>
    		<property name="mappingResources">
    		  <list>
    						<value>net/brewerfan/bizmodel/article/LinkedArticle.hbm.xml</value>
    			<value>net/brewerfan/bizmodel/common/Player.hbm.xml</value>
    				      </list>
    		</property>
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
    			</props>
    		</property>
    	</bean>
    The Transaction Manager:

    Code:
    	
    	<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
    		<property name="sessionFactory"><ref local="sessionFactory"/></property>
    	</bean>
    The transaction properties I wish to use:

    Code:
        <bean id="matchAllWithPropReq" 
            class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
          <property name="transactionAttribute"><value>PROPAGATION_REQUIRED</value></property>
        </bean>
    My Transaction Interceptor:

    Code:
    	<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
    		<property name="transactionManager"><ref local="transactionManager"/></property>
    		<property name="transactionAttributeSource">
    			<ref bean="matchAllWithPropReq"/>
    		</property>
    	</bean>
    My AutoProxyCreator, since I wish to have ALL method calls on all Service and Dao objects use the same transaction given a particular client call:

    Code:
        <bean id="autoProxyCreator" 
            class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
          <property name="interceptorNames">
            <list>
              <idref local="transactionInterceptor"/>
            </list>
          </property>
          <property name="beanNames">
            <list>
              <value>*Dao</value>
              <value>*Service</value>
            </list>
          </property>
        </bean>
    My Daos:

    Code:
    	<bean id="linkedArticleDao" class="net.brewerfan.dao.article.hibernate.LinkedArticleHibernateDaoImpl">
    		<property name="sessionFactory"><ref local="sessionFactory"/></property>
    	</bean>
    	<bean id="playerDao" class="net.brewerfan.dao.common.hibernate.PlayerHibernateDaoImpl">
    		<property name="sessionFactory"><ref local="sessionFactory"/></property>
    	</bean>
    My Service Object:

    Code:
    	<bean id="articleService" class="net.brewerfan.dao.article.ArticleService">
    		<property name="linkedArticleDao"><ref local="linkedArticleDao"/></property>
    		<property name="playerDao"><ref local="playerDao"/></property>
    	</bean>
    I have a base Dao implementation I use and extend with all my Daos:

    Code:
    public abstract class AbstractBrewerfanHibernateDao extends HibernateDaoSupport &#123;
    
    	protected abstract Class getPersistentClass&#40;&#41;;
    
    	public List findAll&#40;&#41; throws DataAccessException &#123;
    		return &#40;getLocalTemplate&#40;&#41;.find&#40;"from "
    				+ getPersistentClass&#40;&#41;.getName&#40;&#41;&#41;&#41;;
    	&#125;
    
    	public Entity find&#40;long id&#41; throws DataAccessException &#123;
    		List results = getLocalTemplate&#40;&#41;.find&#40;
    				"from " + getPersistentClass&#40;&#41;.getName&#40;&#41; + " where id=" + id&#41;;
    		if &#40;results.size&#40;&#41; > 0&#41; &#123;
    			return &#40;&#40;Entity&#41; results.get&#40;0&#41;&#41;;
    		&#125; else &#123;
    			throw new IllegalArgumentException&#40;"No "
    					+ getPersistentClass&#40;&#41;.getName&#40;&#41; + " found with id&#58; " + id&#41;;
    		&#125;
    	&#125;
    
    	public void store&#40;Entity entity&#41; throws DataAccessException &#123;
    		if &#40;entity.getId&#40;&#41; == 0&#41; &#123;
    			getLocalTemplate&#40;&#41;.save&#40;entity&#41;;
    		&#125; else &#123;
    			getLocalTemplate&#40;&#41;.update&#40;entity&#41;;
    		&#125;
    	&#125;
    
    	public void store&#40;Collection collection&#41; throws DataAccessException &#123;
    		for &#40;Iterator iter = collection.iterator&#40;&#41;; iter.hasNext&#40;&#41;;&#41; &#123;
    			Entity element = &#40;Entity&#41; iter.next&#40;&#41;;
    			store&#40;element&#41;;
    		&#125;
    	&#125;
    
    	public void remove&#40;long id&#41; &#123;
    	    HibernateTemplate ht = this.getLocalTemplate&#40;&#41;;
    		Object o = ht.load&#40;getPersistentClass&#40;&#41;, new Long&#40;id&#41;&#41;;
    		ht.delete&#40;o&#41;;
    	&#125;
    
    	public List findLimited&#40;int numberToFind, String orderProp,
    			String orderByType&#41; &#123;
    		Query query;
    		try &#123;
    			query = this.getLocalTemplate&#40;&#41;.createQuery&#40;
    					this.getLocalSession&#40;&#41;,
    					"from " + this.getPersistentClass&#40;&#41;.getName&#40;&#41;
    							+ " order by " + orderProp + " " + orderByType&#41;;
    			query.setMaxResults&#40;numberToFind&#41;;
    			return &#40;query.list&#40;&#41;&#41;;
    
    		&#125; catch &#40;DataAccessResourceFailureException e&#41; &#123;
    			e.printStackTrace&#40;&#41;;
    		&#125; catch &#40;HibernateException e&#41; &#123;
    			e.printStackTrace&#40;&#41;;
    		&#125; catch &#40;IllegalStateException e&#41; &#123;
    			e.printStackTrace&#40;&#41;;
    		&#125;
    		return &#40;null&#41;;
    	&#125;
    
    	protected Criteria createCriteria&#40;&#41; throws HibernateException &#123;
    
    		return &#40;this.getLocalTemplate&#40;&#41;.createCriteria&#40;this.getLocalSession&#40;&#41;,
    				this.getPersistentClass&#40;&#41;&#41;&#41;;
    
    	&#125;
    
    	private Session getLocalSession&#40;&#41; &#123;
    		return &#40;SessionFactoryUtils.getSession&#40;this.getSessionFactory&#40;&#41;, true&#41;&#41;;
    	&#125;
    
    	private HibernateTemplate getLocalTemplate&#40;&#41; &#123;
    		return &#40;new HibernateTemplate&#40;this.getSessionFactory&#40;&#41;&#41;&#41;;
    	&#125;
    Here's my client code (a struts action):

    Code:
    public class AddLinkedArticlePlayerAction extends AdminAction &#123;
    
        /*
         * &#40;non-Javadoc&#41;
         * 
         * @see net.brewerfan.gui.admin.action.AdminAction#adminPerform&#40;org.apache.struts.action.ActionMapping,
         *      org.apache.struts.action.ActionForm,
         *      javax.servlet.http.HttpServletRequest,
         *      javax.servlet.http.HttpServletResponse,
         *      net.brewerfan.bizmodel.common.User&#41;
         */
        public ActionForward adminPerform&#40;ActionMapping mapping, ActionForm form,
                HttpServletRequest request, HttpServletResponse response, User user&#41;
                throws ApplicationException &#123;
    
            System.out.println&#40;"Starting transaction!"&#41;;
            
            AddLinkedArticlePlayerForm playerForm = &#40;AddLinkedArticlePlayerForm&#41; form;
    
            long playerId = Long.parseLong&#40;playerForm.getPlayerId&#40;&#41;&#41;;
    
            ArticleService service = &#40;ArticleService&#41; BrewerfanConstants.APPLICATION_CONTEXT
                    .getBean&#40;"articleService"&#41;;
            Player player;
            try &#123;
                player = service.findPlayer&#40;playerId&#41;;
            &#125; catch &#40;IllegalArgumentException e&#41; &#123;
                ActionErrors ae = new ActionErrors&#40;&#41;;
                ae.add&#40;"playerId",
                        new ActionError&#40;"error.common.playerId.notfound"&#41;&#41;;
                this.saveErrors&#40;request, ae&#41;;
                return &#40;new ActionForward&#40;mapping.getInput&#40;&#41;&#41;&#41;;
            &#125;
    
            LinkedArticle article = service.findLinkedAritcle&#40;playerForm
                    .getLinkedArticleId&#40;&#41;&#41;;
            Set players = article.getPlayers&#40;&#41;;
            if &#40;players == null&#41; &#123;
                players = new HashSet&#40;&#41;;
            &#125;
            players.add&#40;player&#41;;
            article.setPlayers&#40;players&#41;;
            service.store&#40;article&#41;;
    
            request.getSession&#40;&#41;.setAttribute&#40;"linkedArticleId",
                    Long.toString&#40;playerForm.getLinkedArticleId&#40;&#41;&#41;&#41;;
            
            System.out.println&#40;"Ending transaction!"&#41;;
    
            return this.getRedirectSuccess&#40;mapping&#41;;
        &#125;
    I'm getting "Initiating transaction commit" written out three times in stdout, so i'm assuming there is one transaction being started and stopped
    for each call to the service (which delegeates to the dao). I've been trying to figure this out on my own for a month now... any help for this
    novice would be greatly appreciated!

  • #2
    Ofcourse you are getting new start/stop on each call of service, how should transaction know that you are not yet finished with it, that you have another DAO call ... ?

    You need to start transaction before Struts Action execution and commit/rollback after finish.

    Look at OpenSessionInViewInterceptor/Filter. Or write your own Action implementation that does similar things.

    Comment


    • #3
      Much obliged!

      Comment


      • #4
        The methods of the object that you proxy demarcate the transaction boundaries. When building an application with Spring I tend to map each transaction to a particular method in the service tier then call all the methods that i need to be part of that transaction within the control flow of that method.

        Rob

        Comment


        • #5
          Originally posted by robh
          The methods of the object that you proxy demarcate the transaction boundaries. When building an application with Spring I tend to map each transaction to a particular method in the service tier then call all the methods that i need to be part of that transaction within the control flow of that method.

          Rob
          So there's no possible way that I can span a transaction over multiple calls to a proxy, even if I start the transaction externally (within the struts action)?

          Comment

          Working...
          X