Spring08_純粋な注釈実戦_取引バージョンのサポート
13428 ワード
このチュートリアルのソースにアクセスしてください:tutorial_demo
前回のチュートリアルでは、Apache Commons DbUtilsと組み合わせて、単一テーブルのCRUD操作を実現しましたが、このチュートリアルの操作はトランザクションをサポートしていません.このチュートリアルでは、既存の知識に基づいて、トランザクションをサポートするバージョンに変更し、後続の学習の準備をしています.
一、振り替え操作問題の分析
次に、問題がある問題を分析するために、振り替え操作を実現します.
1.1、業務層インタフェースIAccountServiceに相応の方法を追加する
1.2.ビジネス層実装クラスAccountServiceImplに新たに追加する方法
1.3、試験クラスに試験方法を追加する
テストメソッドを実行し、テストに成功しました.問題ないようです.
1.4、サービス層の振り込み方法の修正
テストメソッドを実行すると、プログラムに異常が発生しますが、srcはデータベースでお金が減少します.
振込プロセス分析:振り込みの流れが大きい原則は、ソース口座のお金が減少し、目的口座のお金が増加することである. ソース口座のお金が減少し、目的口座のお金が増加した操作は同時に成功するか、同時に失敗するか. は、同時に成功するために、同時に失敗した操作を同じトランザクションに包むべきである. 同じトランザクション・オペレーションでは、同じ接続(Connection)を使用する必要があります.
現在の問題:現在の振り替えプロセスではトランザクションが開かれておらず、すべての操作で独立した接続が使用されています.
二、コードアップグレード
前の分析によると、私たちは工事を修正して、事務をサポートさせ、満たすべき原則は以下の通りです.は、サービス・レベルでトランザクションを処理します(トランザクション、コミット、ロールバックをオンにします). Daoレイヤは、データベース操作のみを担当し、業務を担当しない.すなわち、Dao操作ごとに最も微細なCRUD操作のみを行う. Daoレイヤは例外を処理せず,出現した例外はサービスレイヤに投げ出される.
2.1、取引をサポートするツールクラスの作成
説明:この工事はThreadLocalを使用し、マルチスレッド環境で使用できることを保証した. dataSourceプロパティは@AutoWiredではなく、setメソッドで@AutoWiredを使用しています.なぜならdataSourceはstaticタイプであり、staticタイプ変数はSpringコンテナの作成よりも早く作成され、クラスローダが静的変数をロードした場合、Springコンテキストはまだロードされていないためです.したがって、クラスローダはbeanに静的クラスを正しく注入せず、失敗します.
2.2.Daoの実装クラスを修正する
2.3、サービス層実装クラスの修正
2.4.JdbcConfig構成クラスの変更
2.5、試験方法の実行
このテスト方法を実行すると、転送に成功します.
2.4の
2.6、現在コードに存在する問題
現在のコードはトランザクションのサポートを実現していますが、次のような問題があります.コードは煩雑で、サービス層の実装クラスは前のバージョンに比べて煩雑で、業務を担当する以外に事務の操作を担当しなければならない. JdbcUtilsクラスのトランザクション・オペレーションに関連するメソッドに変更がある場合、サービス・レイヤ・コードに対応するコール・ロケーションは変更されます.プロジェクトが膨大であれば、開発者にとっては悪夢です.
次の節ではSpring AOPを深く学び、上記の問題を解決します.
前回のチュートリアルでは、Apache Commons DbUtilsと組み合わせて、単一テーブルのCRUD操作を実現しましたが、このチュートリアルの操作はトランザクションをサポートしていません.このチュートリアルでは、既存の知識に基づいて、トランザクションをサポートするバージョンに変更し、後続の学習の準備をしています.
一、振り替え操作問題の分析
次に、問題がある問題を分析するために、振り替え操作を実現します.
1.1、業務層インタフェースIAccountServiceに相応の方法を追加する
//
void transfer(Integer srcId, Integer dstId, Float money);
1.2.ビジネス層実装クラスAccountServiceImplに新たに追加する方法
//
@Override
public void transfer(Integer srcId, Integer dstId, Float money) {
// Id
Account src = accountDao.findById(srcId);
Account dst = accountDao.findById(dstId);
if(src == null) {
throw new RuntimeException(" ");
}
if(dst == null) {
throw new RuntimeException(" ");
}
if(src.getMoney() < money) {
throw new RuntimeException(" ");
}
// ,
src.setMoney(src.getMoney() - money);
dst.setMoney(dst.getMoney() + money);
//
accountDao.update(src);
accountDao.update(dst);
}
1.3、試験クラスに試験方法を追加する
@Test
public void testTrans() {
accountService.transfer(1, 2, 10F);
}
テストメソッドを実行し、テストに成功しました.問題ないようです.
1.4、サービス層の振り込み方法の修正
//
@Override
public void transfer(Integer srcId, Integer dstId, Float money) {
// Id
Account src = accountDao.findById(srcId);
Account dst = accountDao.findById(dstId);
if(src == null) {
throw new RuntimeException(" ");
}
if(dst == null) {
throw new RuntimeException(" ");
}
if(src.getMoney() < money) {
throw new RuntimeException(" ");
}
// ,
src.setMoney(src.getMoney() - money);
dst.setMoney(dst.getMoney() + money);
//
accountDao.update(src);
// ,
int i = 100/0;
//
accountDao.update(dst);
}
テストメソッドを実行すると、プログラムに異常が発生しますが、srcはデータベースでお金が減少します.
振込プロセス分析:
現在の問題:現在の振り替えプロセスではトランザクションが開かれておらず、すべての操作で独立した接続が使用されています.
二、コードアップグレード
前の分析によると、私たちは工事を修正して、事務をサポートさせ、満たすべき原則は以下の通りです.
2.1、取引をサポートするツールクラスの作成
package org.codeaction.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@Component
public class JdbcUtils {
private static DataSource dataSource;
//
private static ThreadLocal tl = new ThreadLocal();
//
public static DataSource getDataSource() {
return dataSource;
}
//
@Autowired
public void setDataSource(DataSource dataSource) {
JdbcUtils.dataSource = dataSource;
}
//
public static Connection getConnection() throws SQLException {
System.out.println(dataSource);
Connection conn = tl.get();
if(conn == null) {
return dataSource.getConnection();
}
return conn;
}
//
public static void beginTransaction() throws SQLException {
Connection conn = tl.get();
if(conn != null) {
throw new SQLException(" , ");
}
conn = getConnection();
conn.setAutoCommit(false);
tl.set(conn);
}
//
public static void commitTransaction() throws SQLException {
Connection conn = tl.get();
if(conn == null) {
throw new SQLException(" , ");
}
conn.commit();
conn.close();
tl.remove();
}
//
public static void rollbackTransaction() throws SQLException {
Connection conn = tl.get();
if (conn == null) {
throw new SQLException(" , ");
}
conn.rollback();
conn.close();
tl.remove();
}
}
説明:
2.2.Daoの実装クラスを修正する
package org.codeaction.dao.impl;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.codeaction.dao.IAccountDao;
import org.codeaction.domain.Account;
import org.codeaction.util.JdbcUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {
@Autowired
private QueryRunner queryRunner;
public void setQueryRunner(QueryRunner queryRunner) {
this.queryRunner = queryRunner;
}
@Override
public List findAll() throws SQLException {
Connection conn = JdbcUtils.getConnection();
String sql = "select * from account";
List list = queryRunner.query(conn, sql, new BeanListHandler(Account.class));
return list;
}
@Override
public Account findById(Integer id) throws SQLException {
Connection conn = JdbcUtils.getConnection();
String sql = "select * from account where id = ?";
Account account = queryRunner.query(conn, sql, new BeanHandler(Account.class), id);
return account;
}
@Override
public void save(Account account) throws SQLException {
Object[] params = {account.getName(), account.getMoney()};
Connection conn = JdbcUtils.getConnection();
String sql = "insert into account(name, money) values(?, ?)";
queryRunner.update(conn, sql, params);
}
@Override
public void update(Account account) throws SQLException {
Object[] params = {account.getName(), account.getMoney(), account.getId()};
Connection conn = JdbcUtils.getConnection();
String sql = "update account set name=?, money=? where id=?";
queryRunner.update(conn, sql, params);
}
@Override
public void delete(Integer id) throws SQLException {
Object[] params = {id};
Connection conn = JdbcUtils.getConnection();
String sql = "delete from account where id=?";
queryRunner.update(conn, sql, id);
}
}
2.3、サービス層実装クラスの修正
package org.codeaction.service.impl;
import org.codeaction.dao.IAccountDao;
import org.codeaction.domain.Account;
import org.codeaction.service.IAccountService;
import org.codeaction.util.JdbcUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
import java.util.List;
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
@Autowired
private IAccountDao accountDao;
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public List findAll() {
List list = null;
try {
//1.
JdbcUtils.beginTransaction();
//2.
list = accountDao.findAll();
//3.
JdbcUtils.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
try {
//
JdbcUtils.rollbackTransaction();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
return list;
}
@Override
public Account findById(Integer id) {
Account account = null;
try {
//1.
JdbcUtils.beginTransaction();
//2.
account = accountDao.findById(id);
//3.
JdbcUtils.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
try {
//
JdbcUtils.rollbackTransaction();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
return account;
}
@Override
public void save(Account account) {
try {
//1.
JdbcUtils.beginTransaction();
//2.
accountDao.save(account);
//3.
JdbcUtils.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
try {
//
JdbcUtils.rollbackTransaction();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
@Override
public void update(Account account) {
try {
//1.
JdbcUtils.beginTransaction();
//2.
accountDao.update(account);
//3.
JdbcUtils.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
try {
//
JdbcUtils.rollbackTransaction();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
@Override
public void delete(Integer id) {
try {
//1.
JdbcUtils.beginTransaction();
//2.
accountDao.delete(id);
//3.
JdbcUtils.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
try {
//
JdbcUtils.rollbackTransaction();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
@Override
public void transfer(Integer srcId, Integer dstId, Float money) {
try {
//1.
JdbcUtils.beginTransaction();
//2.
Account src = accountDao.findById(srcId);
Account dst = accountDao.findById(dstId);
if(src == null) {
throw new RuntimeException(" ");
}
if(dst == null) {
throw new RuntimeException(" ");
}
if(src.getMoney() < money) {
throw new RuntimeException(" ");
}
src.setMoney(src.getMoney() - money);
dst.setMoney(dst.getMoney() + money);
accountDao.update(src);
//int x = 1/0;//
accountDao.update(dst);
//3.
JdbcUtils.commitTransaction();
} catch (Exception e) {
e.printStackTrace();
try {
//
JdbcUtils.rollbackTransaction();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
}
2.4.JdbcConfig構成クラスの変更
package org.codeaction.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.codeaction.util.JdbcUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class JdbcConfig {
@Value("${jdbc.driverClass}")
private String driverClass;
@Value("${jdbc.jdbcUrl}")
private String jdbcUrl;
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.password}")
private String password;
@Bean("queryRunner")
public QueryRunner queryRunner() {
return new QueryRunner();
}
@Bean("dataSource")
public DataSource dataSource() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass(driverClass);
dataSource.setJdbcUrl(jdbcUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
}
2.5、試験方法の実行
@Test
public void testTrans() {
accountService.transfer(1, 2, 10F);
}
このテスト方法を実行すると、転送に成功します.
2.4の
int i = 1/0;
の注釈を取り消し、試験方法を実行し、異常が発生し、正常にロールバックできる.2.6、現在コードに存在する問題
現在のコードはトランザクションのサポートを実現していますが、次のような問題があります.
次の節ではSpring AOPを深く学び、上記の問題を解決します.