java web 转账事务控制

11 篇文章 1 订阅

转账事务控制

1.数据准备

CREATE TABLE `account` (
  `aid` int(11) NOT NULL AUTO_INCREMENT,
  `uname` varchar(255) DEFAULT NULL,
  `upwd` varchar(255) DEFAULT NULL,
  `balance` decimal(8,2) DEFAULT NULL,
  PRIMARY KEY (`aid`)
) ENGINE=InnoDB;

INSERT INTO `account` VALUES (1, 'tesDest',
'112342', 1200.00);
INSERT INTO `account` VALUES (2, 'segvcw', '223352',
680.50);

2.项目准备

创建Web项目,技术栈:JDBC + druid连接池 + Servlet + Ajax 使用带事务处理的JDBC工具类,

3.项目文件

在这里插入图片描述
在这里插入图片描述

4.完整代码

TransactionDao

package com.huawei.servlet;

import com.huawei.entity.Account;
import com.huawei.service.TransactionService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @avthor HuangJun
 * @date 2022/5/2 14:58
 */
@WebServlet(value = "/transfer.do")
public class TransferServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.接收表单参数
        String fromAcc = req.getParameter("fromAcc");
        String moneyStr = req.getParameter("money");
        String toAcc = req.getParameter("toAcc");
        resp.setContentType("text/json;charset=utf-8");
        double money = 0.0;
        if (moneyStr != null && !"".equals(moneyStr)) {
            money = Double.parseDouble(moneyStr);
        }
        PrintWriter out = resp.getWriter();
        //2.创建业务层对象 调用业务层方法
        TransactionService tranService = new TransactionService();
        if (tranService.chageAccount(fromAcc)) {
            if (tranService.chageAccount(toAcc)) {
                if (tranService.balanceVerify(fromAcc, money)) {
                    //3.根据业务方法的返回值进行处理
                    boolean isFlag = tranService.transactionAccount(fromAcc, toAcc, money);
                    if (isFlag) {
                        System.out.println("转账成功!");
                        //以响应输出成功标志到前端
                        out.println("<h3>转账成功</h3>");
                    } else {
                        System.out.println("转账失败!");
                        //以响应输出失败标志到前端
                        out.println("<h3>转账失败</h3>");
                        throw new RuntimeException("转账失败");//TODO 如果没有手动引发异常 转账过滤器是无法回滚的
                    }
                } else {
                    out.println("<h3>" + fromAcc + "账户余额不足</h3>");
                }
            } else {
                out.println("<h3>" + toAcc + "账户不存在</h3>");
            }
        } else {
            out.println("<h3>" + fromAcc + "账户不存在</h3>");
        }

    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }
}

Account

package com.huawei.entity;

/**
 * @avthor HuangJun
 * @date 2022/5/2 15:05
 */

public class Account {
    private int aid;
    private String uname;
    private String upwd;
    private double balance;

    public Account() {
    }

    public int getAid() {
        return aid;
    }

    public void setAid(int aid) {
        this.aid = aid;
    }

    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public String getUpwd() {
        return upwd;
    }

    public void setUpwd(String upwd) {
        this.upwd = upwd;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "aid=" + aid +
                ", uname='" + uname + '\'' +
                ", upwd='" + upwd + '\'' +
                ", balance=" + balance +
                '}';
    }
}

TransactionFilter

package com.huawei.filter;

import com.huawei.util.DBUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @avthor HuangJun
 * @date 2022/5/1 19:04
 */
@WebFilter(urlPatterns = "/*")
public class TransactionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            //TODO 只会针对insert update delete
            System.out.println("---------请求执行--------");
            DBUtils.beginTransaction();//开启事务
            filterChain.doFilter(servletRequest, servletResponse);
            DBUtils.commitAndClose();// 提交事务
            System.out.println("---------给出响应--------");
        } catch (Exception e) {
            DBUtils.rollbackAndClose();//回滚事务
            e.printStackTrace();
        }
    }

    @Override
    public void destroy() {

    }
}

TransactionService

package com.huawei.service;

import com.huawei.dao.TransactionDao;
import com.huawei.entity.Account;

/**
 * @avthor HuangJun
 * @date 2022/5/2 15:01
 */

public class TransactionService {
    private TransactionDao tranDao;

    public TransactionService() {
        tranDao = new TransactionDao();
    }

    //确认账户是否存在
    public boolean chageAccount(String accNo) {
        //TODO
        boolean flag = false;
        Account account = tranDao.queryAccountByWhere(accNo);
        if (account != null) {
            flag = true;
        }
        return flag;
    }

    //确认账户余额是否足够
    public boolean balanceVerify(String accNo, double balance) {
        //TODO
        boolean flag = false;
        Account account = tranDao.queryAccountByWhere(accNo);
        if (account == null) {
            return flag;
        } else {
            if (account.getBalance() < balance) {
                return flag;
            } else {
                flag = true;
            }
        }
        return flag;
    }

    //转账方法
    public boolean transactionAccount(String accNo, String toAccount, double balance) {
        //TODO
        boolean flag = false;
        try {
            tranDao.transationOut(accNo, balance);
//            int i=1/0;//手动引发异常
            tranDao.transationIn(toAccount, balance);
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }
}

TransferServlet

package com.huawei.servlet;

import com.huawei.entity.Account;
import com.huawei.service.TransactionService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @avthor HuangJun
 * @date 2022/5/2 14:58
 */
@WebServlet(value = "/transfer.do")
public class TransferServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.接收表单参数
        String fromAcc = req.getParameter("fromAcc");
        String moneyStr = req.getParameter("money");
        String toAcc = req.getParameter("toAcc");
        resp.setContentType("text/json;charset=utf-8");
        double money = 0.0;
        if (moneyStr != null && !"".equals(moneyStr)) {
            money = Double.parseDouble(moneyStr);
        }
        PrintWriter out = resp.getWriter();
        //2.创建业务层对象 调用业务层方法
        TransactionService tranService = new TransactionService();
        if (tranService.chageAccount(fromAcc)) {
            if (tranService.chageAccount(toAcc)) {
                if (tranService.balanceVerify(fromAcc, money)) {
                    //3.根据业务方法的返回值进行处理
                    boolean isFlag = tranService.transactionAccount(fromAcc, toAcc, money);
                    if (isFlag) {
                        System.out.println("转账成功!");
                        //以响应输出成功标志到前端
                        out.println("<h3>转账成功</h3>");
                    } else {
                        System.out.println("转账失败!");
                        //以响应输出失败标志到前端
                        out.println("<h3>转账失败</h3>");
                        throw new RuntimeException("转账失败");//TODO 如果没有手动引发异常 转账过滤器是无法回滚的
                    }
                } else {
                    out.println("<h3>" + fromAcc + "账户余额不足</h3>");
                }
            } else {
                out.println("<h3>" + toAcc + "账户不存在</h3>");
            }
        } else {
            out.println("<h3>" + fromAcc + "账户不存在</h3>");
        }

    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

DaoTest

package com.huawei.test;

import com.huawei.dao.TransactionDao;
import com.huawei.entity.Account;

/**
 * @avthor HuangJun
 * @date 2022/5/2 15:15
 */

public class DaoTest {
    public static void main(String[] args) {
        TransactionDao dao=new TransactionDao();
        Account account= dao.queryAccountByWhere("test1");
        System.out.println(account);

//        dao.transationIn("test1",200);
        dao.transationOut("test1",200);
    }
}

db.properties

jdbc.url=jdbc:mysql://localhost:3306/teach?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
jdbc.username=root
jdbc.password=123456
jdbc.driverClassName=com.mysql.cj.jdbc.Driver

druid.url=jdbc:mysql://localhost:3306/teach?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
druid.username=root
druid.password=123456
druid.driverClassName=com.mysql.cj.jdbc.Driver
druid.maxActive=8
druid.maxWait=5000

DBUtils

package com.huawei.util;


import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * Jdbc工具类[加入事务控制+线程+Druid连接池]
 *
 * @avthor HuangJun
 * @date 2022/3/30 10:36
 */

public class DBUtils {
    static String USER = null;
    static String USER_PASS = null;
    static String DB_URL = null;
    static String DRIVER_CLASS = null;
    //容器变量      [ThreadLocal用于隔离多线程并发 产生的数据混乱]
    private static ThreadLocal<Connection> conns = new ThreadLocal<>();
    private static DataSource dataSource;

    /**
     * 静态代码块加载属性配置文件
     */
    static {
        Properties config = new Properties();
        try {
            //找到文件
            InputStream is = DBUtils.class.getResourceAsStream("/db.properties");
            //并读取到内存
            config.load(is);
            //读取属性数据的key
            DRIVER_CLASS = config.getProperty("jdbc.driverClassName");
            //初始化Durid连接池数据
            DruidDataSource dds = new DruidDataSource();
            dds.configFromPropety(config);//加载属性文件到数据源对象
            dataSource = dds;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static DataSource getDataSource() {
        return dataSource;
    }

    /**
     * 注册驱动
     */
    static {
        try {
            Class.forName(DRIVER_CLASS);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConn() {
        if (conns.get() == null) {// 当前线程是否有连接对象
            Connection conn = null;
            try {
                conn = dataSource.getConnection();
                conns.set(conn);//放入当前线程对象中

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return conns.get();
    }

    /**
     * @throws Exception
     */
    public static void beginTransaction() throws Exception {
        getConn().setAutoCommit(false);
    }

    /**
     * 提交事务
     */
    public static void commit() {
        try {
            getConn().commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }

    /**
     * 回滚
     */
    public static void rollback() {
        try {
            getConn().rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }


    public static Connection getConn(String DB_URL) {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(DB_URL, USER, USER_PASS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }

    public static void close(Connection conn, Statement stmt, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (conn != null) {
            try {
                conn.setAutoCommit(true);
                conn.close();
                conns.remove();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通用的DML sql语句执行方法
     *
     * @param sql
     * @param params
     * @return
     */
    public static int update(String sql, Object... params) throws Exception {
        int result = 0;
        Connection conn = getConn();
        PreparedStatement psmt = conn.prepareStatement(sql);
        //获取参数化SQL中的? 个数
        int count = psmt.getParameterMetaData().getParameterCount();
        if (count != params.length) {
            return -1;
        }
        //遍历参数个数 从动态数组中取出
        for (int i = 1; i <= count; i++) {
            psmt.setObject(i, params[i - 1]);
        }
        //执行sql语句
        result = psmt.executeUpdate();
        close(conn, psmt, null);
        return result;
    }

    /**
     * 查询方法
     *
     * @param sql
     * @param params
     * @return
     * @throws Exception
     */
    public static ResultSet query(String sql, Object... params) throws Exception {
        ResultSet result = null;
        Connection conn = getConn();
        PreparedStatement psmt = conn.prepareStatement(sql);
        //获取参数化SQL中的? 个数
        int count = psmt.getParameterMetaData().getParameterCount();
        if (count != params.length) {
            return null;
        }
        //遍历参数个数 从动态数组中取出
        for (int i = 1; i <= count; i++) {
            psmt.setObject(i, params[i - 1]);
        }
        //执行sql语句
        result = psmt.executeQuery();
        return result;
    }

    /**
     * 获取数据库连接池中的连接
     *
     * @return 如果返回 null,说明获取连接失败<br/>有值就是获取连接成功
     */

    public static Connection getConnection() {
        Connection conn = conns.get();
        if (conn == null) {
            try {
                conn = dataSource.getConnection();//从数据库连接池中获取连接
                conns.set(conn); // 保存到 ThreadLocal 对象中, 供后面的jdbc 操作使用
                conn.setAutoCommit(false); // 设置为手动管理事务
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return conn;
    }

    /**
     * * 提交事务, 并关闭释放连接
     */
    public static void commitAndClose() {
        Connection connection = conns.get();
        if (connection != null) { // 如果不等于 null, 说明 之前使用过连接, 操作过数据库
            try {
                connection.commit(); // 提交 事务
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close(); // 关闭连接, 资源资源
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        //一定要执行 remove 操作,否则就会出错。 (因为 Tomcat 服务器底层使用了线程池技术)
        conns.remove();
    }

    /**
     * 回滚事务,并关闭释放连接
     */

    public static void rollbackAndClose() {
        Connection connection = conns.get();
        if (connection != null) { // 如果不等于 null, 说明 之前使用过连接,操作过数据库
            try {
                connection.rollback();//回滚事务
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close(); // 关闭连接, 资源资源
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        //一定要执行 remove操作,否则就会出错。 (因为 Tomcat服务器底层使用了线程池技术)
        conns.remove();
    }

    /**
     * 关闭连接, 放回数据库连接池
     *
     * @param conn
     */
    public static void close(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

transfer.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>转账</title>
    <script type="text/javascript" src="js/axios-v0.27.2.js"></script>
</head>
<body>
<h3>模拟银行转账</h3>
<form action="/transfer.do" method="post">

    <table width="450px" border="1" cellpadding="0" cellspacing="0">
        <tr>
            <td width="100px">转账人</td>
            <td><input  name="fromAcc"></td>
        </tr>
        <tr>
            <td>转账金额</td>
            <td><input name="money"></td>
        </tr>
        <tr>
            <td>收款人</td>
            <td><input name="toAcc"></td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="提交"></td>
        </tr>
    </table>
</form>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4fuol3rd-1651747441085)(C:\Users\hj\Desktop\2022KP java全栈-01\博客原件\转账事务控制\03.png)]

" src=“js/axios-v0.27.2.js”>

模拟银行转账

<table width="450px" border="1" cellpadding="0" cellspacing="0">
    <tr>
        <td width="100px">转账人</td>
        <td><input  name="fromAcc"></td>
    </tr>
    <tr>
        <td>转账金额</td>
        <td><input name="money"></td>
    </tr>
    <tr>
        <td>收款人</td>
        <td><input name="toAcc"></td>
    </tr>
    <tr>
        <td colspan="2"><input type="submit" value="提交"></td>
    </tr>
</table>
```

在这里插入图片描述

注意:如果输入负值自己会加钱
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值