Announcement Announcement Module
Collapse
No announcement yet.
Exception handling with transaction AOP Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Exception handling with transaction AOP

    Hello,

    I have the follownig configuration in my springConfig.xml file :
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http&#58;//www.springframework.org/dtd/spring-beans.dtd">
    
    <beans>	
    	<!-- ========================= Start of PERSISTENCE DEFINITIONS ========================= -->
    	<!-- DataSource Definition -->
    	<bean id="dataSource"
          		class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    		<property name="driverClassName">
    			<value>oracle.jdbc.driver.OracleDriver</value>
    		</property>
    		<property name="url">
    			<value>URL</value>
    		</property>
    		<property name="username">
    			<value>USERNAME</value>
    		</property>
    		<property name="password">
    			<value>PASSWORD</value>
    		</property>
    	</bean>
    	
    	<!-- Hibernate SessionFactory Definition -->
    	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    		<property name="mappingResources">
    			<list>
    				<value>MAPPING_RESOURCES</value>
    			</list>
    		</property>		
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
    				<prop key="hibernate.show_sql">true</prop>
    				<prop key="hibernate.cglib.use_reflection_optimizer">true</prop>
    				<prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
    			</props>
    		</property>	
    		<property name="dataSource">
    			<ref bean="dataSource"/>
    		</property>
    	</bean>
    	
    	<!-- Spring Data Access Exception Translator Defintion -->
    	<bean id="jdbcExceptionTranslator" class="org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator"> 
    		<property name="dataSource"><ref bean="dataSource"/></property> 
    	</bean>
    
    	<!-- Hibernate Template Defintion -->
    	<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
    		<property name="sessionFactory"><ref bean="sessionFactory"/></property> 
    		<property name="jdbcExceptionTranslator"><ref bean="jdbcExceptionTranslator"/></property> 
    	</bean>
    	
    	<!-- ErrMsg DAO object&#58; Hibernate implementation -->
    	<bean id="errMsgDao" class="ErrMsgDAOHibernateImpl">
    		<property name="hibernateTemplate"><ref bean="hibernateTemplate"/></property>
    	</bean>
    	
    	<!-- ========================= Start of SERVICE DEFINITIONS ========================= -->
    	
    	<!-- Hibernate Transaction Manager Definition -->
    	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    		<property name="sessionFactory"><ref local="sessionFactory"/></property>
    	</bean>
    	
    	<!-- ErrrorMessage Service Defintion -->
    	<bean id="errorMessageServiceTarget"
    			class="ErrorMessageServiceHibernateImpl">
    		<property name="errMsgDao"><ref local="errMsgDao"/></property>
    	</bean>
    	
    	<!-- Transactional proxy for the ErrrorMessage Service -->
    	<bean id="errorMessageService"
    			class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    		<property name="transactionManager"><ref local="transactionManager"/></property>
    		<property name="target"><ref local="errorMessageServiceTarget"/></property>
    		<property name="transactionAttributes">
    			<props>
    				<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
    				<prop key="save*">PROPAGATION_REQUIRED</prop>
    				<prop key="update*">PROPAGATION_REQUIRED</prop>
    				<prop key="delete*">PROPAGATION_REQUIRED</prop>
    			</props>
    		</property>
    	</bean>
    </beans>
    And in my ErrorMessageServiceHibernateImpl, I have the following code :
    Code:
    public void update&#40;Cwd01 errMsg&#41; throws Exception &#123;
    	try &#123;
    		this.getErrMsgDao&#40;&#41;.updateErrMsg&#40;errMsg&#41;;
    	&#125; catch&#40;DataIntegrityViolationException dive&#41; &#123;
    		throw dive;
    	&#125; catch &#40;Exception e&#41; &#123;
    		throw e
    	&#125;
    &#125;
    But when I make an update with a field too large (I try to set a 300 characters String in a 250 database field), I don't go into the DataIntegerityViolationException of my business service, but well in my JSF-bean that has called this service.

    How is it possible ?

    I have the following error :
    Code:
    org.springframework.dao.DataIntegrityViolationException&#58; &#40;Hibernate operation&#58; Could not execute JDBC batch update&#41;&#58; data integrity violated by SQL 'update CWD01 set NUMVER=?, REFMAJ=?, DATCRT=?, DATMAJ=?, HEUMAJ=?, USRMAJ=?, PGMMAJ=?, STAANN=?, LIBMSG_1=?, LIBMSG_2=?, LIBMSG_3=?, LIBMSG_4=?, NUMREF=?, SWIAUD=? where NUMMSG=?'; nested exception is java.sql.BatchUpdateException&#58; ORA-01401&#58; inserted value too large for column
    
    java.sql.BatchUpdateException&#58; ORA-01401&#58; inserted value too large for column
    
    	at oracle.jdbc.dbaccess.DBError.throwBatchUpdateException&#40;DBError.java&#58;430&#41;
    	at oracle.jdbc.driver.OraclePreparedStatement.executeBatch&#40;OraclePreparedStatement.java&#58;2948&#41;
    	at org.apache.commons.dbcp.DelegatingPreparedStatement.executeBatch&#40;DelegatingPreparedStatement.java&#58;231&#41;
    	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch&#40;BatchingBatcher.java&#58;57&#41;
    	at org.hibernate.jdbc.AbstractBatcher.executeBatch&#40;AbstractBatcher.java&#58;174&#41;
    	at org.hibernate.engine.ActionQueue.executeActions&#40;ActionQueue.java&#58;226&#41;
    	at org.hibernate.engine.ActionQueue.executeActions&#40;ActionQueue.java&#58;137&#41;
    	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions&#40;AbstractFlushingEventListener.java&#58;274&#41;
    	at org.hibernate.event.def.DefaultFlushEventListener.onFlush&#40;DefaultFlushEventListener.java&#58;27&#41;
    	at org.hibernate.impl.SessionImpl.flush&#40;SessionImpl.java&#58;730&#41;
    	at org.hibernate.impl.SessionImpl.managedFlush&#40;SessionImpl.java&#58;324&#41;
    	at org.hibernate.transaction.JDBCTransaction.commit&#40;JDBCTransaction.java&#58;86&#41;
    	at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit&#40;HibernateTransactionManager.java&#58;488&#41;
    	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit&#40;AbstractPlatformTransactionManager.java&#58;435&#41;
    	at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning&#40;TransactionAspectSupport.java&#58;258&#41;
    	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke&#40;TransactionInterceptor.java&#58;67&#41;
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed&#40;ReflectiveMethodInvocation.java&#58;144&#41;
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke&#40;JdkDynamicAopProxy.java&#58;174&#41;
    	at $Proxy56.update&#40;Unknown Source&#41;
    	at com.cwsoft.emmngt.view.bean.ErrorMessageBean.updateConfirm&#40;ErrorMessageBean.java&#58;194&#41;
    	at sun.reflect.NativeMethodAccessorImpl.invoke0&#40;Native Method&#41;
    	at sun.reflect.NativeMethodAccessorImpl.invoke&#40;NativeMethodAccessorImpl.java&#58;39&#41;
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke&#40;DelegatingMethodAccessorImpl.java&#58;25&#41;
    	at java.lang.reflect.Method.invoke&#40;Method.java&#58;324&#41;
    	at com.sun.faces.el.MethodBindingImpl.invoke&#40;MethodBindingImpl.java&#58;126&#41;
    	at com.sun.faces.application.ActionListenerImpl.processAction&#40;ActionListenerImpl.java&#58;72&#41;
    	at javax.faces.component.UICommand.broadcast&#40;UICommand.java&#58;312&#41;
    	at javax.faces.component.UIViewRoot.broadcastEvents&#40;UIViewRoot.java&#58;266&#41;
    	at javax.faces.component.UIViewRoot.processApplication&#40;UIViewRoot.java&#58;380&#41;
    	at com.sun.faces.lifecycle.InvokeApplicationPhase.execute&#40;InvokeApplicationPhase.java&#58;75&#41;
    	at com.sun.faces.lifecycle.LifecycleImpl.phase&#40;LifecycleImpl.java&#58;200&#41;
    	at com.sun.faces.lifecycle.LifecycleImpl.execute&#40;LifecycleImpl.java&#58;90&#41;
    	at javax.faces.webapp.FacesServlet.service&#40;FacesServlet.java&#58;197&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter&#40;ApplicationFilterChain.java&#58;237&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter&#40;ApplicationFilterChain.java&#58;157&#41;
    	at org.extremecomponents.table.filter.ExportFilter.doFilter&#40;ExportFilter.java&#58;87&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter&#40;ApplicationFilterChain.java&#58;186&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter&#40;ApplicationFilterChain.java&#58;157&#41;
    	at com.cwsoft.commons.webapp.filter.SecurityFilter.doFilter&#40;SecurityFilter.java&#58;66&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter&#40;ApplicationFilterChain.java&#58;186&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter&#40;ApplicationFilterChain.java&#58;157&#41;
    	at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter&#40;ReplyHeaderFilter.java&#58;75&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter&#40;ApplicationFilterChain.java&#58;186&#41;
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter&#40;ApplicationFilterChain.java&#58;157&#41;
    	at org.apache.catalina.core.StandardWrapperValve.invoke&#40;StandardWrapperValve.java&#58;214&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;104&#41;
    	at org.apache.catalina.core.StandardPipeline.invoke&#40;StandardPipeline.java&#58;520&#41;
    	at org.apache.catalina.core.StandardContextValve.invokeInternal&#40;StandardContextValve.java&#58;198&#41;
    	at org.apache.catalina.core.StandardContextValve.invoke&#40;StandardContextValve.java&#58;152&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;104&#41;
    	at org.jboss.web.tomcat.security.CustomPrincipalValve.invoke&#40;CustomPrincipalValve.java&#58;66&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;102&#41;
    	at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke&#40;SecurityAssociationValve.java&#58;150&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;102&#41;
    	at org.jboss.web.tomcat.security.JaccContextValve.invoke&#40;JaccContextValve.java&#58;54&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;102&#41;
    	at org.apache.catalina.core.StandardPipeline.invoke&#40;StandardPipeline.java&#58;520&#41;
    	at org.apache.catalina.core.StandardHostValve.invoke&#40;StandardHostValve.java&#58;137&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;104&#41;
    	at org.apache.catalina.valves.ErrorReportValve.invoke&#40;ErrorReportValve.java&#58;118&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;102&#41;
    	at org.apache.catalina.core.StandardPipeline.invoke&#40;StandardPipeline.java&#58;520&#41;
    	at org.apache.catalina.core.StandardEngineValve.invoke&#40;StandardEngineValve.java&#58;109&#41;
    	at org.apache.catalina.core.StandardValveContext.invokeNext&#40;StandardValveContext.java&#58;104&#41;
    	at org.apache.catalina.core.StandardPipeline.invoke&#40;StandardPipeline.java&#58;520&#41;
    	at org.apache.catalina.core.ContainerBase.invoke&#40;ContainerBase.java&#58;929&#41;
    	at org.apache.coyote.tomcat5.CoyoteAdapter.service&#40;CoyoteAdapter.java&#58;160&#41;
    	at org.apache.coyote.http11.Http11Processor.process&#40;Http11Processor.java&#58;799&#41;
    	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection&#40;Http11Protocol.java&#58;705&#41;
    	at org.apache.tomcat.util.net.TcpWorkerThread.runIt&#40;PoolTcpEndpoint.java&#58;577&#41;
    	at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run&#40;ThreadPool.java&#58;683&#41;
    	at java.lang.Thread.run&#40;Thread.java&#58;534&#41;
    Thanks in advance for your help.
    Blaise

  • #2
    The business service is not performing any logic with the DataIntegrityViolationException - it is simply rethrowing it so it will be handled by the JSF layer - that is the correct behaviour for the code you show here.

    Rob

    Comment


    • #3
      First of all, thanks for your reply.

      But in fact, I don't rethrow the exception, I log it in a log file. I clean my code too much ;-)

      Code:
      public void update&#40;Cwd01 errMsg&#41; throws Exception &#123; 
         try &#123; 
            this.getErrMsgDao&#40;&#41;.updateErrMsg&#40;errMsg&#41;; 
         &#125; catch&#40;DataIntegrityViolationException dive&#41; &#123; 
            this.logger.debug&#40;"DataIntegrityViolationException", dive&#41;;
            throw dive; 
         &#125; catch &#40;Exception e&#41; &#123;
            this.logger.debug&#40;"Exception", e&#41;;
            throw e 
         &#125; 
      &#125;
      And I never get this log message. So ??

      Apparently, the exception is "catched" during the commit of the transaction (see error stack of previous message). Could you explain me how the spring transaction interceptor work ? When is the commit done ?

      And how is it possible that I don't catch this error in my business service ?

      Thanks in advance...

      Comment


      • #4
        Ah. The problem here is that you are using Hibernate and the call to save() or saveOrUpdate() in your DAO does not actually send the SQL to the database. Rather, Hibernate will 'flush' all the commands to the database before the transaction commits - this is why you get the error when it comes to commit time. The commit fires after your business method has returned, but before control is passed back to the client.

        Rob

        Comment


        • #5
          Hi!
          We love to use spring and hibernate, but we are facing the similar problem.

          In our general architecture, we have presentation layer, business logic layer and dao layer (by hibernate).
          Of course, the transactions are handled by the business layer.
          And we want to catch all data exception just in the business layer, but not directly shown in presneation layer.

          Now, as this problem, some Dao exceptions are caught by presentation layer....

          Could you suggest a better solution?
          Use hibernate and spring, but keep the dao exception under business layer.

          Thanks!

          Comment


          • #6
            Actually, we haven't yet found any solution. We are searching on this forum hoping to find THE solution. If you give me your e-mail adress, I could let you know if I find something...

            Kind regards,

            Comment


            • #7
              There are two possibilities (that I am aware of):

              1. Perform a session.flush() in your DAO-method to force hibernate to execute the SQL statements

              2. Write an interceptor that uses Springs exception translation mechanism and declare it to be used outside the transaction interceptor.


              Regards,
              Andreas

              Comment


              • #8
                Just found an interceptor implementation I used earlier. Maybe it could be useful for someone.

                Code:
                public class ExceptionTranslatingInterceptor extends HibernateAccessor implements MethodInterceptor &#123;
                
                    /**
                     * Creates a new <code>ExceptionTranslatingInterceptor</code> instance.
                     */
                    public ExceptionTranslatingInterceptor&#40;&#41; &#123;&#125;
                
                    /**
                     * Creates a new <code>ExceptionTranslatingInterceptor</code> instance.
                     * 
                     * @param sessionFactory the <code>SessionFactory</code>
                     */
                    public ExceptionTranslatingInterceptor&#40;SessionFactory sessionFactory&#41; &#123;
                        super.setSessionFactory&#40;sessionFactory&#41;;
                    &#125;
                
                    
                    /**
                     * @see org.aopalliance.intercept.MethodInterceptor#invoke&#40;org.aopalliance.intercept.MethodInvocation&#41;
                     */
                    public Object invoke&#40;MethodInvocation invocation&#41; throws Throwable &#123;
                        try &#123;
                            return invocation.proceed&#40;&#41;;
                        &#125; catch &#40;DataAccessException e&#41; &#123;
                            // already a spring exception
                            throw e;
                        &#125; catch &#40;HibernateException e&#41; &#123;
                            throw convertHibernateAccessException&#40;e&#41;;
                        &#125; catch &#40;SQLException e&#41; &#123;
                            throw convertJdbcAccessException&#40;e&#41;;
                        &#125;
                    &#125;
                &#125;
                Regards,
                Andreas

                Comment


                • #9
                  Just to add two cents:

                  I've found the use of ThrowsAdvice to be most appropriate in this case.

                  The throws advice simply catches certain specific exceptions I care about that could only happen when the transaction is committed and otherwise catches generic DataAccessException. The advice simply translates the Spring exception to an exception for my own hierarchy. This way, my client (a Struts based view tier) is completely unaware of Spring's exception hierarchy and only the ones from my own hierarchy that my business tier says it can throw.

                  I do this because I don't think you should explicitly call session.flush() unless you actually need that behavior. Otherwise, let Hibernate's transactional write-behind do its thing to optimize and reduce the overal queries performed.

                  Beyond that, there might be cases where you still wouldn't identify all problems until the transaction is committed which means you still need to catch and worry about DataAccessExceptions originating from both tiers.

                  --- More at: http://forum.springframework.org/showthread.php?t=16973
                  ---
                  Last edited by robyn; May 14th, 2006, 10:25 AM.

                  Comment

                  Working...
                  X