Transaction does not roll back when using Spring 3 @Transactional annotation

3.6k views Asked by At

I am using the Spring (version 3.0) '@Transactional' annotation in order to demonstrate transaction support in Spring but can't get this working (despite having seen similar issues people have encountered in this and other technical forums).

Here is my Spring config entry in spring-application-context.xml:

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

  <tx:annotation-driven /> 

  <bean id="formatDao" class="com.gj.dao.FormatDao">
    <property name="dataSource" ref="dataSource"/>
  </bean>

Here is my transactional method in my test class:

  @Transactional(readOnly = true)
  public class FormatDaoTest
  {

      private static ApplicationContext context = new FileSystemXmlApplicationContext(
        "c:\\catalogue\\src\\com\\gj\\conf\\spring-application-context.xml");

      public static void main(String[] args)
      {
    FormatDaoTest test = new FormatDaoTest();
    test.doTransaction();
      }


      @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
      public void doTransaction()
      {

    IDao dao = (IDao) context.getBean("formatDao");

    Format newFormat = new Format(1, "Test Format 1");

    // Creating a single format
    dao.create(newFormat);

    List newFormatList = new ArrayList();
    newFormatList.add(new Format(2, "Test Format 2"));
    newFormatList.add(new Format(3, "Test Format 3"));
    newFormatList.add(new Format(4, "Test Format 4"));
    newFormatList.add(new Format(5, "Test Format 5"));

    // Creating a list of formats 
    dao.create(newFormatList);

    List updatedFormatList = new ArrayList();
    updatedFormatList.add(new Format(1, "My Test Format 1"));
    updatedFormatList.add(new Format(2, "My Test Format 2"));
    updatedFormatList.add(new Format(3, "My Test Format 3"));

    // Update a list of formats
    dao.update(updatedFormatList);

    Format updatedFormat = new Format(4, "My Test Format 4 with a long format description so allowed table column size is exceeded");

    // Update a single format resulting in a java.sql.SQLException: ORA-12899: value too large for column
    // "RSSPPF1_OWNER"."FORMAT"."FORMAT_DESC" (actual: 88, maximum: 30)
    dao.update(updatedFormat);


      }

  }

The following SQL exception is thrown (as I would expect):

Exception in thread "main" org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [UPDATE FORMAT SET format_desc = ? WHERE format_id = ?]; SQL state [72000]; error code [12899]; ORA-12899: value too large for column "RSSPPF1_OWNER"."FORMAT"."FORMAT_DESC" (actual: 88, maximum: 30)
  ; nested exception is java.sql.SQLException: ORA-12899: value too large for column "RSSPPF1_OWNER"."FORMAT"."FORMAT_DESC" (actual: 88, maximum: 30)

    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:83)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:602)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:811)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:833)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:260)
    at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(NamedParameterJdbcTemplate.java:264)
    at com.gj.dao.FormatDao.update(FormatDao.java:68)
    at com.gj.test.FormatDaoTest.doTransaction(FormatDaoTest.java:62)
    at com.gj.test.FormatDaoTest.main(FormatDaoTest.java:26)
  Caused by: java.sql.SQLException: ORA-12899: value too large for column "RSSPPF1_OWNER"."FORMAT"."FORMAT_DESC" (actual: 88, maximum: 30)

    at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70)
    at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:131)
    at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:204)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:455)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:413)
    at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:1034)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:194)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:953)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1222)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3387)
    at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3468)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1062)
    at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:817)
    at org.springframework.jdbc.core.JdbcTemplate$2.doInPreparedStatement(JdbcTemplate.java:1)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:586)
    ... 7 more

However, after the exception is thrown the transaction does not rollback and I can see the previous database inserts and updates prior to the exception being thrown have been committed to the FORMAT table:

  FORMAT_ID FORMAT_DESC
  1     My Test Format 1
  2     My Test Format 2
  3     My Test Format 3
  4     Test Format 4
  5     Test Format 5

I would have expected to see nothing committed to the database following this exception.

Does anybody know where I am going wrong and am I missing some key concept here?

Any help would be greatly appreciated!

1

There are 1 answers

4
axtavt On

@Transactional is applied to the Spring-managed beans only (unless you have configured advanced features), whereas in your case FormatDaoTest isn't managed by Spring.

I guess you can do the following as a quick fix:

public static void main(String[] args) {
    FormatDaoTest test = context.getAutowireCapableBeanFactory()
        .createBean(FormatDaoTest.class);
    test.doTransaction();
}  

As a more elegant solution you can use Spring TestContext framework, which supports transaction-aware tests out of the box, see 9.3.5.4 Transaction management.