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

  • Stuck with Transactions

    I have in the past successfully built web applications that use Spring and Hibernate with transaction support provided by HibernateTransactionManager. My grasp on how all that worked isn't very strong, but there were plenty of good examples to follow.

    Now, I'm building a standalone application that (1) won't have access to a container-provided transaction manager and (2) won't be using any kind of BeanFactory.

    I'd very much like to use Spring's JDBC abstractions, but am clueless how to get transaction support. Here's how things are going:

    I'm using commons-dbcp for connection pooling; I set up a dataSource like this:

    Code:
    private void init() {
      GenericObjectPool connectionPool = new GenericObjectPool();
      connectionFactory = new DriverManagerConnectionFactory(getDatabaseURI(),
            getUsername(), getPassword());
      PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
            connectionFactory, connectionPool, null, getValidationQuery(), false, false);
      new PoolingDriver().registerPool(this.getClass().getName(), connectionPool);
      dataSource = new PoolingDataSource(connectionPool);
    }
    
    public JdbcTemplate getJdbcTemplate() {
      return new JdbcTemplate(dataSource);
    }
    This code is contained in a Database object, which defines the connectionURI, the validation query, username, password, etc.

    I have other classes which use the Database object to get a JdbcTemplate:

    Code:
    public void doSomething() throws SQLException {
      JdbcTemplate jdbcTemplate = database.getJdbcTemplate();
      jdbcTemplate.update&#40;"<some update query">&#41;;
      jdbcTemplate.update&#40;"<some other update query">&#41;;
    &#125;
    This all works when the connection factory is instructed to create connections with autoCommit = true, which is undesirable. Otherwise, work done by the JdbcTemplate is never commited.

    Work done in subclasses of SqlUpdate isn't committed either:

    Code:
    public class DeleteSuspension extends SqlUpdate &#123;
      public DeleteSuspension&#40;Database database&#41; throws SQLException &#123;
        setDataSource&#40;database&#41;;
        setSql&#40;database.getStatement&#40;"DELETE_SUSPENSION"&#41;.getSql&#40;&#41;&#41;;
        declareParameter&#40;new SqlParameter&#40;Types.VARCHAR&#41;&#41;;
        declareParameter&#40;new SqlParameter&#40;Types.VARCHAR&#41;&#41;;
        declareParameter&#40;new SqlParameter&#40;Types.VARCHAR&#41;&#41;;
        compile&#40;&#41;;
      &#125;
    
      public int run&#40;AgentSuspension suspension&#41; &#123;
        Object&#91;&#93; params = new Object&#91;&#93; &#123; suspension.getServerId&#40;&#41;,
            suspension.getDatabaseId&#40;&#41;,
            suspension.isScheduledDowntime&#40;&#41; ? "Y" &#58; "N" &#125;;
        
        return update&#40;params&#41;;
      &#125;
    &#125;
    
    ...
    DeleteSuspension s = new DeleteSuspension&#40;dataSource&#41;;
    s.run&#40;suspension&#41;;
    ...
    Concurrently running threads will get JdbcTemplates from the Database object and perform work. The sum of work done by each thread should be considered a transaction. It seems to me that I should be using a PlatformTransactionManager (probably DataSourceTransactionManager). But I'm stumped on how to hook it in. I'd greatly appreciate if someone could point me in the right direction.

  • #2
    Sigh. Sometimes walking away from the computer for a while helps clarify things. I think I've figured it out, although I'm not 100% certain it's the correct approach.

    1. Each Database now has its own DataSourceTransactionManager. The DataSource is wrapped with a TransactionAwareDataSourceProxy:

    Code:
    connectionFactory = new DriverManagerConnectionFactory&#40;getDatabaseURI&#40;&#41;,
            getUsername&#40;&#41;, getPassword&#40;&#41;&#41;;
    PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory&#40;
            connectionFactory, connectionPool, null, getValidationQuery&#40;&#41;, false, false&#41;;
    new PoolingDriver&#40;&#41;.registerPool&#40;this.getClass&#40;&#41;.getName&#40;&#41;, connectionPool&#41;;
    dataSource = new TransactionAwareDataSourceProxy&#40;new PoolingDataSource&#40;connectionPool&#41;&#41;;
    
    txManager = new DataSourceTransactionManager&#40;dataSource&#41;;
    txDef = new DefaultTransactionDefinition&#40;&#41;;
    txDef.setPropagationBehavior&#40;TransactionDefinition.PROPAGATION_REQUIRED&#41;;
    2. When a thread wants to execute a transaction in the Database, it calls this method on the Database object:

    Code:
    public TransactionStatus startTransaction&#40;&#41; &#123;
      return txManager.getTransaction&#40;txDef&#41;;
    &#125;
    3. And when a thread wants to commit a transaction, it calls:

    Code:
    public void commit&#40;TransactionStatus txStatus&#41; &#123;
      txManager.commit&#40;txStatus&#41;;
    &#125;

    Comment


    • #3
      Using a JDBC DataSourceTransactionManager programmatically through the PlatformTransactionManager API seems the right way to go for your needs. JDBC data access code will automatically participate in such transactions.

      Wrapping your DataSource in a TransactionAwareDataSourceProxy is actually not strictly necessary if you're using Spring's JDBC support: JdbcTemplate and Spring's JDBC operation objects will automatically detect active transactions, even if working on the plain target DataSource. TransactionAwareDataSourceProxy is mainly intended for non-Spring JDBC data access code which is not aware of Spring transactions.

      Juergen

      Comment


      • #4
        Hi, I am with problems to understand and to implement the manager of transactions of the Spring. I have that to insert a register in two tables. The implementation of my DAO is:
        Code:
        public class FornecedoresImpl extends LinkPecasAbstract&#123;
            
            private JdbcTemplate jdbcTemplate;
            private TransactionTemplate transactionTemplate;
            private static Logger logger = Logger.getLogger&#40;"br.com.rodobens"&#41;;
            
            private int insertFornecedor&#40;FornecedoresBean bean&#41;&#123;
                StringBuffer sql = new StringBuffer&#40;&#41;;
                sql.append&#40;" insert into DMMLP009 &#40;IDSTATFOR, IDTIPFOR, NMFOR, NRCPJFOR, NMFANFOR, IDMRCFOR, "&#41;;
                sql.append&#40;" DSENDFOR, NRENDFOR, NMBAIFOR, CDCEPFOR, NMCIDFOR, SIUNFFOR, NRDDDTELFOR, NRTELFOR, DSEMAILFOR&#41; "&#41;;
                sql.append&#40;" values &#40;" + DominioStatusFornecedor.ATIVO + ", ?, ?, ? ,? ,? ,? ,? ,? ,? ,? ,?, ?, ?, ? &#41; "&#41;;
               
                Object&#91;&#93; parametros = new Object&#91;&#93;&#123; new Integer&#40;bean.getTipoFornecedor&#40;&#41;&#41;, bean.getNomeFornecedor&#40;&#41;,
                                                    new Long&#40;bean.getCnpj&#40;&#41;&#41;, bean.getNomeFantasia&#40;&#41;, new Integer&#40;bean.getMarca&#40;&#41;&#41;,
                                                    bean.getRua&#40;&#41;, new Integer&#40;bean.getNumero&#40;&#41;&#41;, bean.getBairro&#40;&#41;, new Integer&#40;bean.getCep&#40;&#41;&#41;,
                                                    bean.getCidade&#40;&#41;, bean.getUf&#40;&#41;, new Integer&#40;bean.getDdd&#40;&#41;&#41;, new Integer&#40;bean.getTelefone&#40;&#41;&#41;,
                                                    bean.getEmailFornecedor&#40;&#41; &#125;;  
                                
               return jdbcTemplate.update&#40;sql.toString&#40;&#41;, parametros&#41;;
            &#125;
            
            private int insertVendedorDeFornecedor&#40;final String nomeVendedor, final String emailVendedor&#41;&#123;
                StringBuffer sql = new StringBuffer&#40;&#41;;
                sql.append&#40;" insert into DMMLP010 &#40;CDVENFOR, CDFOR, NMVENFOR, DSEMAILVENFOR&#41; "&#41;;
                sql.append&#40;" values &#40;&#40;select coalesce&#40;max&#40;CDVENFOR&#41;+1, 1&#41; "&#41;;
                sql.append&#40;" from DMMLP010 where CDFOR = &#40;select max&#40;CDFOR&#41; from DMMLP009&#41;&#41;,"&#41;;
                sql.append&#40;" &#40;select max&#40;CDFOR&#41; from DMMLP009&#41;, ?, ?"&#41;;
        
                Object&#91;&#93; parametros = new Object&#91;&#93;&#123; nomeVendedor, emailVendedor&#125;;
                
                return jdbcTemplate.update&#40;sql.toString&#40;&#41;, parametros&#41;;        
            &#125;
        
            public void insereFornecedor&#40;final FornecedoresBean bean&#41;&#123;
                transactionTemplate.execute&#40;new TransactionCallbackWithoutResult&#40;&#41;&#123;
                    protected void doInTransactionWithoutResult&#40;TransactionStatus status&#41;&#123;
                        try&#123;
                            // insere o fornecedor
                            insertFornecedor&#40;bean&#41;;
                            
                            // insere vendedor
                            insertVendedorDeFornecedor&#40;bean.getNomeVendedor1&#40;&#41;, bean.getEmailVendedor1&#40;&#41;&#41;;
            
                            // se existe outro vendedor, insere
                            if&#40;bean.getNomeVendedor2&#40;&#41; != null&#41;&#123;
                                insertVendedorDeFornecedor&#40;bean.getNomeVendedor2&#40;&#41;, bean.getEmailVendedor2&#40;&#41;&#41;;
                            &#125;
                        &#125;
                        catch&#40;Exception ex&#41;&#123;
                            logger.error&#40;ex.getMessage&#40;&#41;&#41;;
                            logger.error&#40;"Fornecedor não foi inserido."&#41;;
                        &#125;
                    &#125;
                &#125;&#41;;
            &#125;
            /**
             * @param jdbcTemplate  seta o valor de jdbcTemplate na variável jdbcTemplate.
             */
            public void setJdbcTemplate&#40;JdbcTemplate jdbcTemplate&#41; &#123;
                this.jdbcTemplate = jdbcTemplate;
            &#125;
        
            /**
             * @param transactionTemplate  seta o valor de transactionTemplate na variável transactionTemplate.
             */
            public void setTransactionTemplate&#40;TransactionTemplate transactionTemplate&#41; &#123;
                this.transactionTemplate = transactionTemplate;
            &#125;
            
        &#125;
        My context is thus:
        Code:
        	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        		<property name="dataSource" ref="dataSource" />
        	</bean>
        	
        	<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        		<property name="transactionManager" ref="transactionManager" />
        	</bean>
        
        	<!-- associa um template a um data source -->
        	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        		<property name="dataSource" ref="dataSource" />
        	</bean>
        	<bean id="fornecedoresDAO" class="br.com.rodobens.dao.fornecedores.FornecedoresImpl">
        		<property name="jdbcTemplate" ref="jdbcTemplate"/>
        		<property name="transactionTemplate" ref="transactionTemplate"/>
        	</bean>
        First insert functions, but as it throws an exception. But first insert commit!!!
        Because?
        Thank's
        Carlos

        Comment

        Working...
        X