数据库事务操作之完成银行转账功能,附详细代码

这个demo的基本思想是用户A转账给用户B,对应数据库操作是用户A修改金额减少Y元,而用户B修改金额增加Y元,这就要保证这两个操作必须在一个事务中,要么全部成功,要么全部失败,而按照三层开发模式,操作数据库在Dao层,但是开启事务应该在service层,dao层应该只专注处理数据库,然后将处理的数据返回,要想保证是一个事务,应该使用一个Connection,所以Connection不应该在dao层获取,因为事务处理应该在service层,所以应该service获取Connection,并通过方法参数传递到dao层,这样dao层使用传递过来的connection,就可以保证在同一个事务中了。

代码

jsp页面代码

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title></head><body><form action="${pageContext.request.contextPath }/transfer" method="post">转出账户:<input type="text" name="out"><br>转入账户:<input type="text" name="in"><br>转账金额:<input type="text" name="money"><br><input type="submit" value="确认转账"><br></form></body></html>

数据库事务操作之完成银行转账功能,附详细代码


servlet

package com.itheima.transfer.web;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.itheima.transfer.service.TransferService;public class TransferServlet extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//接受转账的参数String out = request.getParameter("out");String in = request.getParameter("in");String moneyStr = request.getParameter("money");double money = Double.parseDouble(moneyStr);//调用业务层的转账方法TransferService service = new TransferService();boolean isTransferSuccess = service.transfer(out,in,money);response.setContentType("text/html;charset=UTF-8");if(isTransferSuccess){response.getWriter().write("转账成功!!!");}else{response.getWriter().write("转账失败!!!");}}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

service

package com.itheima.transfer.service;import java.sql.Connection;import java.sql.SQLException;import com.itheima.transfer.dao.TransferDao;import com.itheima.utils.DataSourceUtils;import com.itheima.utils.MyDataSourceUtils;public class TransferService {public boolean transfer(String out, String in, double money) {TransferDao dao = new TransferDao();boolean isTranferSuccess = true;//标记转账是否正确Connection conn = null;try {//开启事务conn = DataSourceUtils.getConnection();conn.setAutoCommit(false);//开启事务//转出钱的方法dao.out(out,money);//转入钱的方法dao.in(in,money);} catch (Exception e) {isTranferSuccess = false;//转入钱和转出钱应该是两个操作,如果这个操作出问题,会有异常,则 isTranferSuccess 标记为false,标记转账失败//回滚事务try {conn.rollback();//出现异常就要回滚} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();} finally{try {conn.commit();最后提交事务} catch (SQLException e) {e.printStackTrace();}}return isTranferSuccess;}}

回滚的理解方式,回滚在数据库层面可以理解为数据库操作失效,数据库没有变化,而在代码的理解角度可以看成,回滚是代码回滚到事务开启的地方,也就是说从开启事务开始conn.setAutoCommit(false);,代码往下执行,如果出现异常

conn.rollback();默认回滚到conn.setAutoCommit(false);这就相当于代码只执行到conn.setAutoCommit(false);然后finally运行conn.commit(),提交事务是可以提交的,这就相当于刚开启事务,然后就提交了事务,中间没有代码,数据库也没有数据改变。

回滚并不表示结束事务只有提交事务才可以认为是结束事务

dao

package com.itheima.transfer.dao;import java.sql.Connection;import java.sql.SQLException;import org.apache.commons.dbutils.QueryRunner;import com.itheima.utils.DataSourceUtils;import com.itheima.utils.MyDataSourceUtils;public class TransferDao {public void out(String out, double money) throws SQLException {QueryRunner runner = new QueryRunner();Connection conn = MyDataSourceUtils.getCurrentConnection();String sql = "update account set money=money-? where name=?";runner.update(conn, sql, money,out);}public void in(String in, double money) throws SQLException {QueryRunner runner = new QueryRunner();Connection conn = MyDataSourceUtils.getCurrentConnection();String sql = "update account set money=money+? where name=?";runner.update(conn, sql, money,in);}}

dao有两个方法,一个是转入,一个是转出,两个操作封装到两个方法中 ,service会调用这两个方法。

这个demo只是一种思想,但还有一些问题,下篇文章将优化这个demo,使得connection和service分离

相关推荐