开发者

Spring 模拟转账开发实战小结

开发者 https://www.devze.com 2025-05-19 10:36 出处:网络 作者: lyrhhhhhhhh
目录一、转账业务场景分析二、基于 Spring 的转账业务开发(第一种方式) 1. 分层架构设计2. Spring 配置文件(applicationContext_dao1.XML)3. 测试代码三、Dao 层的简化写法(第二种方式) 1. 继承 Jdb
目录
  • 一、转账业务场景分析
  • 二、基于 Spring 的转账业务开发(第一种方式)
    •  1. 分层架构设计
    • 2. Spring 配置文件(applicationContext_dao1.XML)
    • 3. 测试代码
  • 三、Dao 层的简化写法(第二种方式)
    •  1. 继承 JdbcDaoSupport
    • 2. Spring 配置优化
  • 四、转账业务的事务管理(关键!)
    • 为什么需要事务?
    • 1. 未加事务的问题演示
    • 2. 添加事务控制 
    • 3. 事务关键特性
  • 五、两种 Dao 实现方式对比
    • 六、总结与最佳实践
      • 核心流程总结
      • 常见问题与解决方案

    一、转账业务场景分析

    转账是金融类应用的核心场景之一,涉及付款方扣减金额收款方增加金额两个关键操作。在开发中需解决以下问题:

    • 业务层与数据层解耦:通过分层架构(Service 层调用 Dao 层)实现逻辑分离。
    • 数据库事务管理:确保两个操作要么同时成功,要么同时失败,避免资金不一致。
    • 代码复用与简化:利用 Spring 框架的模板类和依赖注入机制,减少样板代码。

    二、基于 Spring 的转账业务开发(第一种方式)

     1. 分层架构设计

     (1)Service 层:定义业务逻辑

    // 接口:声明转账方法
    public interface AccountService {
        void pay(String out, String in, double money); // 付款人、收款人、金额
    }
    
    // 实现类:调用Dao完成转账
    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 层:操作数据库

    // 接口:定义数据库操作
    public interface AccountDao {
        void outMoney(String out, double money); // 扣款
        void inMoney(String in, double money); // 加款
    }
    
    // 实现类:使用JdbcTemplate操作数据库
    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 ...>
        <!-- 加载数据库配置 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        
        <!-- 配置Druid连接池 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDajstaSource">
            <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); // 模拟熊大给熊二NlTxOcnp转账100元
        }
    }

    三、Dao 层的简化写法(第二种方式)

     1. 继承 JdbcDaoSupport

    Spring 提供JdbcDaoSupport抽象类,内置JdbcTejsmplate管理,可简化 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 配置优化

     无需显式配置JdbcTemplate,直接注入数据源(dataSource)即可:

    <bean id="accountDao" class="com.qcbyjy.demo3.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"/> <!-- 注入数据源 -->
    </bean>

    原理JdbcDaoSupport会自动根据dataSource创建JdbcTemplate,避免手动绑定模板对象。

    四、转账业务的事务管理(关键!)

    为什么需要事务?

    假设转账过程中,付款方扣款成功后系统崩溃,收款方未加款,会导致资金丢失。事务确保两个操作要么都成功,要么都回滚

    1. 未加事务的问题演示

    • 场景:付款方余额不足时,扣款操作抛出异常,但收款方已加款。
    • 代码问题:默认情况下,Spring 的JdbcTemplate操作是自动提交的,异常不会回滚。

    2. 添加事务控制 

     (1)在 Spring 配置中启用事务管理

    <beans ...>
        <!-- 配置事务管理器 -->
        <bean id="transactionManager" 
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            &l编程客栈t;property name="dataSource" ref="dataSource"/> <!-- 绑定数据源 -->
        </bean>
    
        <!-- 启用注解事务 -->
        <tx:annotation-driven transaction-manager="transactionManager"/>
    </beans>

     (2)在 Service 方法上添加@Transactional注解

    @Service
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        // 声明该方法需要事务支持
        @Transactional
        @Override
        public void pay(String out, String in, double money) {
            accountDao.outMoney(out, money); // 若此处抛出异常,两个操作都会回滚
            accountDao.inMoney(in, money);
        }
    }

    3. 事务关键特性

    • 原子性:两个更新操作视为一个不可分割的整体。
    • 隔离性:避免其他事务看到中间状态(通过数据库隔离级别实现)。
    • 传播行为:默认REQUIRED(当前有事务则加入,无则创建新事务)。

    五、两种 Dao 实现方式对比

    Spring 模拟转账开发实战小结

    六、总结与最佳实践

    核心流程总结

    • 分层设计:Service 层处理业务逻辑,Dao 层封装数据库操作。
    • 依赖注入:通过 Spring 配置文件管理对象依赖,避免硬编码。
    • 事务控制:使用@Transactional注解确保数据一致性,必须配置事务管理器。
    • 连接池优化:优先使用 Druid 等开源连接池,提升数据库性能。

    常见问题与解决方案

    • 事务不生效:检查是否配置事务管理器、注解是否添加在 public 方法上、是否使用了代理对象。
    • SQL 注入风险:使用JdbcTemplate的参数化查询(?占位符),避免拼接 SQL。
    • 异常处理:在 Service 层捕获异常并按需回滚(通过TransactionASPectSupport.pythoncurrentTransactionStatus().setRollbackOnly())。

    到此这篇关于Spring 模拟转账开发实战小结的文章就介绍到这了,更多相关Spring 模拟转账开发内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)! 

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号