Announcement Announcement Module
Collapse
No announcement yet.
Transaction problems Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Transaction problems

    So far as I can tell, I have my transactions set up like the petclinic example, but they are not working. In the following example, I have 2 updates in a row, and if the second fails I want the first to roll back (which it doesn't - it throws an exception.) I read something about enabling debug which I did, but there don't seem to be any transactions there. Here's my code:


    ------------in cwa-servlet.xml------------

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value ="${db.driver}"/>
    <property name="url" value ="${db.url}"/>
    <property name="username" value ="${db.user}"/>
    <property name="password" value ="${db.pw}"/>
    </bean>

    <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSou rceTransactionManager">
    <property name="dataSource"><ref local="dataSource"/></property>
    </bean>

    <bean id="docProxy"
    class="org.springframework.transaction.interceptor .TransactionProxyFactoryBean">
    <property name="transactionManager"><ref bean="transactionManager"/></property>
    <property name="target"><ref bean="docDao"/></property>
    <property name="transactionAttributes">
    <props>
    <prop key="*">PROPAGATION_REQUIRED,-DataAccessException,-SQLException</prop>
    </props>
    </property>
    </bean>

    <bean id="docDao" class="com.csc.cwa.db.DocumentManagerDao">
    <property name="dataSource">
    <ref local = "dataSource" />
    </property>
    <property name="documentQueryDao">
    <ref bean="docQueryDao"/>
    </property>
    <property name="docsLocation" value="${cwadocs.url}" />
    <property name="templatesLocation" value="${cwa.templates}" />
    <!--<property name="incrementer"><ref local="incrementer"/></property>-->
    </bean>

    ----------and the DAO----------
    public class DocumentDao extends JdbcDaoSupport{

    ...
    public void updateSection(SectionText sect, int userId) {


    SqlUpdate su =
    new SqlUpdate(getDataSource(), "insert into Hist (" +
    " id, version, hTx, title, content, userId, isApplic) " +
    " values (?,?,?,?,?,?,?,?,?)");

    su.declareParameter(new SqlParameter("id", Types.INTEGER));
    su.declareParameter(new SqlParameter("version", Types.VARCHAR));
    su.declareParameter(new SqlParameter("hTx", Types.VARCHAR));
    su.declareParameter(new SqlParameter("title", Types.VARCHAR));
    su.declareParameter(new SqlParameter("content", Types.LONGVARCHAR));
    su.declareParameter(new SqlParameter("userId", Types.INTEGER));
    su.declareParameter(new SqlParameter("isApplic", Types.TINYINT));
    su.compile();

    Section secData = documentQueryDao.loadSection(sect.getDocId(), sect.getId());
    Object[] oa = new Object[7];
    oa[0] = new Integer(sect.getId());
    oa[1] = secData.getVersion();
    if (sect.getComment() == null) {
    oa[2] = "No comment";
    } else {
    oa[2] = sect.getComment();
    }
    oa[3] = secData.getTitle();
    oa[4] = secData.getContent();
    oa[5] = new Integer(secData.getUser().getId());
    oa[6] = new Integer(secData.getApplic());

    int count = su.update(oa);

    //Now update the current record
    su = new SqlUpdate(getDataSource(), "update section set version = ?, " +
    "content = ?, " +
    "isApplic = ?, " +
    "user_id = ? " +
    "where id = ?");

    su.declareParameter(new SqlParameter("version", Types.VARCHAR));
    su.declareParameter(new SqlParameter("content", Types.LONGVARCHAR));
    su.declareParameter(new SqlParameter("isApplic", Types.TINYINT));
    su.declareParameter(new SqlParameter("user_id", Types.INTEGER));
    su.declareParameter(new SqlParameter("id", Types.INTEGER));
    su.compile();

    oa = new Object[5];
    oa[0] = sect.getVersion();
    oa[1] = new String(sect.getContent());
    oa[2] = new Integer(sect.getApplic());
    oa[3] = new Integer(userId);
    oa[4] = new Integer(sect.getId());
    count = su.update(oa);

    }

    How can I get this to work?

    Thanks in advance...

  • #2
    Exactly what is happening? Is your data not being written? Is it being written, but not rolling back when an exception is thrown?

    Have you turned on DEBUG level logging? At least for the org.springframework.transaction package.

    BTW your XML doesn't seem to match your sources. Your XML references a DocumentManagerDao class and a docQueryDao configured object, but doesn't include either of those. Could you include both of those as well?

    Does the DocumentManagerDao implement an interface? Proxying interfaces is considered a best practice than proxying concrete classes. For one you don't have to include CGLIB in your path.

    Comment


    • #3
      When I go in (through the Eclipse debugger) and enter a value where the second sql statement will fail, the first one is not rolled back. When I set the logging level to 'debug' I didn't see any statements saying a transaction was starting, etc. or anything about transactions.

      The DocumentDao below is really the DocumentManagerDao - I try to shorten things a little for posting & guess I messed that one up: sorry! The query dao doesn't need transactions - Eclipse started getting confused as my dao got very large so I separated out the queries.

      I actually have the TransactionProxyFactoryBean pointing to the class that calls this function because sometimes I'm calling 2-3 dao functions that I want to process together.

      Thanks for your help!!

      Comment


      • #4
        Does your proxied class implement an interface?

        Since you are running this in the debugger, you should step into where the code calls DataSourceUtils.getConnection. This will show whether the ThreadLocal which holds the current transaction information is properly set. Or perhaps into the interceptor which is suppose to start the transaction.

        Comment


        • #5
          The proxied class only implements Serializable.

          I stepped through the debugger and found that org.springframework.jdbc.core.JdbcTemplate's public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action) is called many times, but I haven't figured out how to check for a transaction in it. (I stepped into DataSourceUtils into applyTransactionTimeout once or twice & the ConnectionHolder holder = null) How do I find ThreadLocal?

          Thanks!

          Comment


          • #6
            It looks like DataSourceUtils.doGetConnection is where it estabishes whether or not a transaction is present and whether or not it needs to establish one. I guess you'd have to step into TransactionSynchronizationManager and beyond to really understand this stuff. I'm no expert on the code itself. But hopefully it can shed some light on the problem for you.

            Comment


            • #7
              Thanks for trying! I never get to that statement - I think my code is oblivious to my transaction declaration.

              Comment


              • #8
                Then you might consider trying to set a breakpoint inside of the transactional interceptor.

                Does the class which calls your DAO implement an interface? If not, do you call setTargetClass in the TransactionProxyFactoryBean to enable using object proxies using CGLIB?

                Comment


                • #9
                  The class which calls my DAO only implements the Serializable interface, and I didn't do whatever you said about CGLIB. I actually just changed the code to make the DAO (which extends JdbcDaoSupport) the target and I set lazy-init="true" on the DAO in the appName-servlet.xml, and I got the same results (no mention of a transaction.) The datasource has been set before I get in there (I have everything wired together in appName-servlet.xml so I guess that's in there at application startup(?) Just trying to trace a single function through the steps doesn't seem to be getting me anywhere.

                  Comment


                  • #10
                    OK, I MIGHT be understanding a little, but before I change my code around I'd like to make sure I understand. After looking at petclinic some more it looks like: the TransactionProxyFactoryBean which references the 'dao' is called 'clinic'. 'clinic' is then assigned to each of the forms that need data access in petclinic-servlet.xml. The java classes referenced by these form definitions have clinic as type 'Clinic' - 'Clinic' is an interface. Then the actual 'dao' implements 'Clinic.' Therefore in order to get this to work for me, I need to create an interface to define all of the public methods in my dao and implement it in my dao, then pass the proxy to the dao instead of the actual dao. The biggest thing that threw me is that you can assign something of type TransactionProxyFactoryBean ('clinic') to a 'Clinic' variable, but I assume this is because the target in 'clinic' implements the 'Clinic' interface?

                    Comment


                    • #11
                      The key is whatever is being Proxied needs to implement some interface. You don't need to proxy the DAO, or even have the DAO use an interface (although its advisable for testing purposes) if you are using another class to call several operations from the DAO. For example:

                      Code:
                      public interface DocManager {
                        public void doStuff();
                      }
                      
                      public class DocManagerImpl {
                        private MyDao dao;
                        public void setDocDao(DocDao dao) {
                          this.dao = dao;
                        }
                        public void doStuff() {
                          dao.call1();
                          dao.call2();
                          dao.call3();
                        }
                      }
                      To wire it up:

                      Code:
                      <bean id="docManager" class="DocManagerImpl>
                        <property name="docDao" ref="docDao"/>
                      </bean>
                      
                      <bean id="docDao" class="">
                        <property name="dataSource"><ref local="dataSource"/></property>
                      </bean>
                      
                      <bean id="docProxy" 
                      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
                        <property name="transactionManager"><ref bean="transactionManager"/></property> 
                        <property name="target"><ref bean="docManager"/></property> 
                        <property name="transactionAttributes"> 
                          <props> 
                            <prop key="*">PROPAGATION_REQUIRED,-DataAccessException,-SQLException</prop> 
                          </props> 
                        </property> 
                      </bean>

                      Comment


                      • #12
                        Thanks for all your help!! It took me the entire day yesterday but the trx's are working now.

                        Comment

                        Working...
                        X