JDBC 編程式交易管理


Spring提供兩種方式實現編程式的交易管理,一是直接使用PlatformTransactionManager實現,二是使用org.springframework.transaction.support.TransactionTemplate。

先來看看如何使用PlatformTransactionManager,在這邊使用它的實現類別DataSourceTransactionManager,可以改寫一下 使 用 JdbcTemplate,讓它具有交易管理功能,修改一下UserDAO類別的insert()方法來作示範:
  • UserDAO.java
package onlyfun.caterpillar;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.
datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.
support.DefaultTransactionDefinition;

public class UserDAO implements IUserDAO {
private DataSourceTransactionManager transactionManager;
private DefaultTransactionDefinition def;
private JdbcTemplate jdbcTemplate;

public void setDataSource(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
transactionManager =
new DataSourceTransactionManager(dataSource);
// 建立交易的定義
def = new DefaultTransactionDefinition();
def.setPropagationBehavior(
TransactionDefinition.PROPAGATION_REQUIRED);
}

public void insert(User user) {
String name = user.getName();
int age = user.getAge().intValue();

TransactionStatus status =
transactionManager.getTransaction(def);
try {
jdbcTemplate.update("INSERT INTO user (name,age) "
+ "VALUES('" + name + "'," + age + ")");
// 下面的SQL有錯誤,用以測試交易
jdbcTemplate.update("INSER INTO user (name,age) "
+ "VALUES('" + name + "'," + age + ")");
}
catch(DataAccessException e) {
transactionManager.rollback(status);
throw e;
}
transactionManager.commit(status);
}

public User find(Integer id) {
List rows = jdbcTemplate.queryForList(
"SELECT * FROM user WHERE id=" + id.intValue());

Iterator it = rows.iterator();
if(it.hasNext()) {
Map userMap = (Map) it.next();
Integer i = new Integer(
userMap.get("id").toString());
String name = userMap.get("name").toString();
Integer age = new Integer(
userMap.get("age").toString());

User user = new User();

user.setId(i);
user.setName(name);
user.setAge(age);

return user;
}

return null;
}
}

在insert()方法中使用了DataSourceTransactionManager來進行交易管理,如果發生了例外,則catch區塊中會進行交 易的回滾(Rollback),在insert()方法中固定撰寫錯有錯誤的SQL(注意INSERT方法少寫了一個T),因此實際上資料並不會被儲存至 資料庫中。

要使用MySQL資料庫的交易處理,必須建立交易類型的表格,例如InnoDB類型的表格,我這邊用來建立表格的SQL如下所示:
CREATE TABLE user (
    id INT(11) NOT NULL auto_increment PRIMARY KEY,
    name VARCHAR(100) NOT NULL default '',
    age INT
) TYPE = InnoDB;

另一個實現編程式交易管理的方法是使用TransactionTemplate,它需要一個TransactionManager實例,一個例子如下所示:
...
TransactionTemplate transactionTemplate =
        new TransactionTemplate(transactionManager);
...
transactionTemplate.execute(new TransactionCallback() {
    public Object doInTransaction(TransactionStatus status) {
        try {
            jdbcTemplate.update("INSERT INTO user (name,age) "
               + "VALUES('" + name + "'," + age + ")");
            ...
        }
        catch(DataAccessException e) {
            status.setRollbackOnly();
        }
         return result; // commit
    }
});

如果發生了例外,則會進行回滾,否則提交交易,如果沒有返回值,則也可以使用TransactionCallbackWithoutResult:
...
transactionTemplate.execute(
        new TransactionCallbackWithoutResult() {
                public void doInTransactionWithoutResult(
                                TransactionStatus status) {
            .        ...
                }
            });