10.转账案例-引入事务

10.转账案例-引入事务

需求:使用Spring框架整合DBUtils技术,实现转账功能。

步骤分析:

  1. 创建java项目,导入坐标
  2. 编写Account实体类
  3. 编写AccountDao接口和实现类
  4. 编写AccountService接口和实现类
  5. 编写spring核心配置文件
  6. 编写测试代码

1.创建项目,导入依赖

 <dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.15</version>
    </dependency>

    <dependency>
        <groupId>commons-dbutils</groupId>
        <artifactId>commons-dbutils</artifactId>
        <version>1.6</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
    </dependencies>

2.编写Account实体类

public class Account {
  private Integer id;
  private String name;
  private Double money;
 
  // setter getter....
}

3.编写AccountDao接口和实现类

public interface AccountDao {
//    转出操作
     void out(String  outUser,Double money);

//    转入操作
    void in(String outUser , Double money);
}
@Repository("accountDao") //使用在dao层实例化bean对象;()里不写默认是该类的名字,首字母小写accountDaoImpl
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner queryRunner;


    @Override
    public void out(String outUser, Double money) {
        String sql = "update account set money = money - ? where name = ?";
        try {
            queryRunner.update(sql,money,outUser);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void in(String outUser, Double money) {
        String sql = " update account set money = money + ? where name = ? ";
        try {
            queryRunner.update(sql,money,outUser);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

4.编写AccountService接口和实现类

public interface AccountService {

//转账方法
    public void transfer(String name1 , String name2 , Double money);
}

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void transfer(String name1, String name2,Double money) {
        //一个账号转出资金
        accountDao.out(name1,money);
        //另一个账号资金就增加
        accountDao.in(name2 ,money );
    }
}

5.编写Spring核心配置文件

<!--    开启注解扫描-->
    <context:component-scan base-package="com.lagou"></context:component-scan>

<!--    加载jdbc配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" ></context:property-placeholder>

<!--    配置数据源-->
    <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>
    <!--配置QueryRunner,它是jar包封装好的,实例化时需要传入数据源作为参数-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

6.编写测试代码

@RunWith(SpringJUnit4ClassRunner.class)//指定junit运行环境为spring
@ContextConfiguration({"classpath:applicationContext.xml"})//加载spring和核心配置文件
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void test1(){
        accountService.transfer("tom","jery",100d);
    }

}

7.测试结果

初始值:

image-20220304092620295

程序运行后:tom转给jery100元image-20220304092737987

8.出现异常

  • 当一个账户实现转账功能后,程序突然出现异常,情况如下所示,tom的账户的钱已经被转出了,但是jery的账户却没到账。
    @Override
    public void transfer(String name1, String name2,Double money) {
        //一个账号转出资金
        accountDao.out(name1,money);
        int num = 100;
        num = num / 0;
        //另一个账号资金就增加
        accountDao.in(name2 ,money );
    }

image-20220304093336903

image-20220304093245146

9.问题分析

  • 上面的代码事务都在dao层,转出转入操作都是一个独立的事务。
  • 其次在dao层获取数据库连接,它的属性Connection就不是一个线程安全的变量。当多并发场景下,每个线程都需要使用Connection,并且个字使用各自的,很容易在转账后,另一个线程又把该对象的余额修改了。

10.解决办法

引入事务,由于篇幅问题,具体解决办法请看第11章节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员阿红

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值