0

Before I start this seemingly LONG paragraph, I like to express my gratitude to any suggestions/advices I may receive. -- John Zhu

I have developed a simple test to help me understand how spring's declarative (@Transactional) transaction management framework works in conjunction with spring's RESTful web services.

For this purpose, I developed the following RESTful controller:

    @RequestMapping(value="register", method=RequestMethod.POST, produces="application/json", consumes="application/json")
public void accountFacade () {
    tester.accountTest();
}

where "tester" is bean of the following service class and is autowired into this controller. The sole method in the class is meant to be transactional:

@Transactional
public class TransactionTest extends ActivityStatus {
@Autowired
private ObjectManager objectManager;

@Autowired
private GenericDB gdb;

public static void main(String[] args) {
    ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/test- context.xml");   
    TransactionTest tester = (TransactionTest) ac.getBean("tester");
    tester.accountTest();
}

@Transactional(readOnly=false, propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED,
               rollbackFor=RuntimeException.class)
public void accountTest () {
    try {
           String sql = "insert into account set account_id=0, ....";
           gdb.jdbcTemplateInsert(sql);
        
           sql = "insert into login set account_id=....";
           gdb.jdbcTemplateInsert(sql, values);
    } catch (DataAccessException dae) {
        System.out.println("Catching DataAccessException (dae)");
        throw new RuntimeException (dae);
    }
}

In above, GenericDB is a class which provides customized jdbcTemplate.

Test runs are to invoke accountFacade from a browser. I intentionally set the data so that the 1st SQL would succeed, and the 2nd would fail, in order to trigger a rollback.

The PROBLEM: the end-result is that a record was inserted into Account, and remained, even the 2nd insert failed as intended.

A few notes:

  1. If I provide the correct data to both SQLs, test runs would be successful.

  2. If I just run TransactionTest as a standalone java application from Eclipse (see main(...) in the class defn) against a local database, rollback seems to happen.

  3. The execution did catch an AccessDataException (see my print statement in the "catch" block). I thus conclude that "new RuntimeException()" was thrown.

  4. The error message (call stack) due to the failed insertion did show, among many other lines, the following:

    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) [spring-tx-3.1.1.RELEASE.jar:3.1.1.RELEASE]

which indicates that method "accountTest" was correctly "wrapped" by Spring's transaction interceptor. This seems to imply that my configuration (see below) was effective.

Related lines in applicationContext.xml:

<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<bean id="tester" class="org.SandRiver.Controllers.TransactionTest">
</bean>

A few words about my execution environment: I ran the application in OpenShift's environment which consists of

  1. jboss 6.1
  2. mysql 5.x (I don't know "x").
  3. I am using Maven to stay up-to-date with Spring versions.
Community
  • 1
  • 1
user1693207
  • 119
  • 2
  • 11
  • Are you using InnoDB in your database? – Shinichi Kai Sep 12 '13 at 07:34
  • I am not familiar with the term "InnoDB". I am using basic functionality of MySQL. – user1693207 Sep 19 '13 at 23:54
  • InnoDB is the storage engine of MySQL. Please see [here](http://stackoverflow.com/questions/1082194/when-should-you-choose-to-use-innodb-in-mysql) and [here](http://stackoverflow.com/questions/3818759/what-is-innodb-and-myisam-in-mysql) for the details. If you are using MyISAM, which is default storage engine in MySQL 5.5.4 and prior, use InnoDB. MyISAM does not support transactions. – Shinichi Kai Sep 20 '13 at 00:48
  • Kai San, thanks for your followups. May you explain a bit further. It is a surprise to me to learn that the default storage engine of MySQL 5.5.4 or prior do not support transactions. However, I have been told all along that transaction management is one of the key and fundamental features in DBMS. I must have missed something. John Zhu – user1693207 Sep 20 '13 at 01:35
  • You can configure the storage engine on a table (or a database) basis. I have posted how to do this as an answer. – Shinichi Kai Sep 20 '13 at 04:42
  • Kai San, thanks very much for your help. I checked the database tables (just learned how to check) and found they are all using MyISAM engine -- you are correct! I'll make changes according to your solution. – user1693207 Sep 20 '13 at 14:03
  • Kai San, Thanks again for your help. I finally got it working. My problem was a combination of two issues. One is due to storage engine selection (I had MyISAM before you pointed out the issue), another is Spring framework related. I just fixed all things, and it is running as expected now. – user1693207 Oct 03 '13 at 20:00

1 Answers1

0

You need to use InnoDB instead of MyISAM to support transactions.

To check the storage engine of a table:

mysql> show create table your_tbl_name;

To create a table using InnoDB:

CREATE TABLE your_tbl_name (
  col1 VARCHAR(10),
  col2 VARCHAR(10)
)
ENGINE = InnoDB;

Hope this helps.

Shinichi Kai
  • 4,415
  • 2
  • 21
  • 25