Spring?模擬轉(zhuǎn)賬開發(fā)實戰(zhàn)小結(jié)
一、轉(zhuǎn)賬業(yè)務(wù)場景分析
轉(zhuǎn)賬是金融類應(yīng)用的核心場景之一,涉及付款方扣減金額和收款方增加金額兩個關(guān)鍵操作。在開發(fā)中需解決以下問題:
- 業(yè)務(wù)層與數(shù)據(jù)層解耦:通過分層架構(gòu)(Service 層調(diào)用 Dao 層)實現(xiàn)邏輯分離。
- 數(shù)據(jù)庫事務(wù)管理:確保兩個操作要么同時成功,要么同時失敗,避免資金不一致。
- 代碼復(fù)用與簡化:利用 Spring 框架的模板類和依賴注入機制,減少樣板代碼。
二、基于 Spring 的轉(zhuǎn)賬業(yè)務(wù)開發(fā)(第一種方式)
1. 分層架構(gòu)設(shè)計
(1)Service 層:定義業(yè)務(wù)邏輯
// 接口:聲明轉(zhuǎn)賬方法 public interface AccountService { void pay(String out, String in, double money); // 付款人、收款人、金額 } // 實現(xiàn)類:調(diào)用Dao完成轉(zhuǎn)賬 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; // 注入Dao層對象 public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void pay(String out, String in, double money) { accountDao.outMoney(out, money); // 付款方扣款 accountDao.inMoney(in, money); // 收款方加款 } }
(2)Dao 層:操作數(shù)據(jù)庫
// 接口:定義數(shù)據(jù)庫操作 public interface AccountDao { void outMoney(String out, double money); // 扣款 void inMoney(String in, double money); // 加款 } // 實現(xiàn)類:使用JdbcTemplate操作數(shù)據(jù)庫 public class AccountDaoImpl implements AccountDao { private JdbcTemplate jdbcTemplate; // 注入JdbcTemplate public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public void outMoney(String out, double money) { jdbcTemplate.update("update account set money=money-? where name=?", money, out); } @Override public void inMoney(String in, double money) { jdbcTemplate.update("update account set money=money+? where name=?", money, in); } }
2. Spring 配置文件(applicationContext_dao1.xml)
<beans ...> <!-- 加載數(shù)據(jù)庫配置 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 配置Druid連接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- 配置JdbcTemplate --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置Service和Dao,注入依賴 --> <bean id="accountService" class="com.qcbyjy.demo2.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <bean id="accountDao" class="com.qcbyjy.demo2.AccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>
3. 測試代碼
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext_dao1.xml") public class Demo2 { @Autowired private AccountService accountService; @Test public void testPay() { accountService.pay("熊大", "熊二", 100); // 模擬熊大給熊二轉(zhuǎn)賬100元 } }
三、Dao 層的簡化寫法(第二種方式)
1. 繼承 JdbcDaoSupport
Spring 提供JdbcDaoSupport
抽象類,內(nèi)置JdbcTemplate
管理,可簡化 Dao 層代碼:
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void outMoney(String out, double money) { // 通過getJdbcTemplate()獲取模板對象 this.getJdbcTemplate().update("update account set money=money-? where name=?", money, out); } @Override public void inMoney(String in, double money) { this.getJdbcTemplate().update("update account set money=money+? where name=?", money, in); } }
2. Spring 配置優(yōu)化
無需顯式配置JdbcTemplate
,直接注入數(shù)據(jù)源(dataSource
)即可:
<bean id="accountDao" class="com.qcbyjy.demo3.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> <!-- 注入數(shù)據(jù)源 --> </bean>
原理:JdbcDaoSupport
會自動根據(jù)dataSource
創(chuàng)建JdbcTemplate
,避免手動綁定模板對象。
四、轉(zhuǎn)賬業(yè)務(wù)的事務(wù)管理(關(guān)鍵!)
為什么需要事務(wù)?
假設(shè)轉(zhuǎn)賬過程中,付款方扣款成功后系統(tǒng)崩潰,收款方未加款,會導(dǎo)致資金丟失。事務(wù)確保兩個操作要么都成功,要么都回滾。
1. 未加事務(wù)的問題演示
- 場景:付款方余額不足時,扣款操作拋出異常,但收款方已加款。
- 代碼問題:默認(rèn)情況下,Spring 的
JdbcTemplate
操作是自動提交的,異常不會回滾。
2. 添加事務(wù)控制
(1)在 Spring 配置中啟用事務(wù)管理
<beans ...> <!-- 配置事務(wù)管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> <!-- 綁定數(shù)據(jù)源 --> </bean> <!-- 啟用注解事務(wù) --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
(2)在 Service 方法上添加@Transactional
注解
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; // 聲明該方法需要事務(wù)支持 @Transactional @Override public void pay(String out, String in, double money) { accountDao.outMoney(out, money); // 若此處拋出異常,兩個操作都會回滾 accountDao.inMoney(in, money); } }
3. 事務(wù)關(guān)鍵特性
- 原子性:兩個更新操作視為一個不可分割的整體。
- 隔離性:避免其他事務(wù)看到中間狀態(tài)(通過數(shù)據(jù)庫隔離級別實現(xiàn))。
- 傳播行為:默認(rèn)
REQUIRED
(當(dāng)前有事務(wù)則加入,無則創(chuàng)建新事務(wù))。
五、兩種 Dao 實現(xiàn)方式對比
六、總結(jié)與最佳實踐
核心流程總結(jié)
- 分層設(shè)計:Service 層處理業(yè)務(wù)邏輯,Dao 層封裝數(shù)據(jù)庫操作。
- 依賴注入:通過 Spring 配置文件管理對象依賴,避免硬編碼。
- 事務(wù)控制:使用
@Transactional
注解確保數(shù)據(jù)一致性,必須配置事務(wù)管理器。 - 連接池優(yōu)化:優(yōu)先使用 Druid 等開源連接池,提升數(shù)據(jù)庫性能。
常見問題與解決方案
- 事務(wù)不生效:檢查是否配置事務(wù)管理器、注解是否添加在 public 方法上、是否使用了代理對象。
- SQL 注入風(fēng)險:使用
JdbcTemplate
的參數(shù)化查詢(?
占位符),避免拼接 SQL。 - 異常處理:在 Service 層捕獲異常并按需回滾(通過
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
)。
到此這篇關(guān)于Spring 模擬轉(zhuǎn)賬開發(fā)實戰(zhàn)小結(jié)的文章就介紹到這了,更多相關(guān)Spring 模擬轉(zhuǎn)賬開發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java?JIT調(diào)優(yōu)的實現(xiàn)
JIT編譯器調(diào)優(yōu)方法包括啟用JIT日志、優(yōu)化熱點代碼、循環(huán)展開、內(nèi)聯(lián)優(yōu)化、逃逸分析以及使用性能分析工具等,本文主要介紹了java?JIT調(diào)優(yōu)的實現(xiàn),感興趣的可以了解一下2025-02-02基于spring+hibernate+JQuery開發(fā)之電子相冊(附源碼下載)
本篇文章介紹了,基于spring+hibernate+JQuery開發(fā)之電子相冊(附源碼下載)。需要的朋友參考下2013-05-05SpringBoot整合Flyway實現(xiàn)數(shù)據(jù)庫的初始化和版本管理操作
Flyway?是一款開源的數(shù)據(jù)庫版本管理工具,它可以很方便的在命令行中使用,或者在Java應(yīng)用程序中引入,用于管理我們的數(shù)據(jù)庫版本,這篇文章主要介紹了SpringBoot整合Flyway實現(xiàn)數(shù)據(jù)庫的初始化和版本管理,需要的朋友可以參考下2023-06-06Java?Stream比較兩個List的差異并取出不同的對象四種方法
今天開發(fā)一個需求時要對A和B兩個List集合遍歷,并比較出集合A有,而集合B沒有的值,下面這篇文章主要給大家介紹了關(guān)于Java?Stream比較兩個List的差異并取出不同對象的四種方法,需要的朋友可以參考下2024-01-01