JAVA-EE使用ThreadLocal保证事务一致性完成转账业务------Java-Web项目

admin2024-08-22  19

Dao层

package com.bjpowernode.Bank.Dao;

import com.bjpowernode.Bank.Pojo.Account;

import java.util.List;

public interface AccountDao
{
    //DAO,Data Access Object数据访问对象
    //取名规范取决于处理的表名字
    int insert(Account account);
    int deleteByActno(Long id);
    int update(Account account);
    Account selectByActno(String actno);
    List<Account> selectAll();
}

Dao实现类

package com.bjpowernode.Bank.Dao.Impl;

import com.bjpowernode.Bank.Dao.AccountDao;
import com.bjpowernode.Bank.Pojo.Account;
import com.bjpowernode.Bank.utils.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class AccountDaoImpl implements AccountDao
{
    @Override
    public int insert(Account account)
    {
        ResultSet resultSet = null;
        PreparedStatement statement = null;
        Connection connection = null;
        int count = 0;
        try
        {
            connection = DBUtil.getConnection();
            String sql = "insert into t_act(id,actno,balance) values(?,?,?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1,null);
            statement.setString(2,account.getActno());
            statement.setDouble(3,account.getBalance());
            count = statement.executeUpdate();
        }
        catch (SQLException e)
        {
            throw new RuntimeException(e);
        }
        finally
        {
            DBUtil.close(null,statement,resultSet);
        }
        return count;
    }

    @Override
    public int deleteByActno(Long id) {
        ResultSet resultSet = null;
        PreparedStatement statement = null;
        Connection connection = null;
        int count = 0;
        try
        {
            connection = DBUtil.getConnection();
            String sql = "delete from t_act where id = ?";
            statement = connection.prepareStatement(sql);
            statement.setLong(1,id);
            count = statement.executeUpdate();
        }
        catch (SQLException e)
        {
            throw new RuntimeException(e);
        }
        finally
        {
            DBUtil.close(null,statement,resultSet);
        }
        return count;
    }

    @Override
    public int update(Account account) {
        ResultSet resultSet = null;
        PreparedStatement statement = null;
        Connection connection = null;
        int count = 0;
        try
        {
            connection = DBUtil.getConnection();
            String sql = "update t_act set actno = ?,balance = ? where id = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,account.getActno());
            statement.setDouble(2,account.getBalance());
            statement.setLong(3,account.getId());
            count = statement.executeUpdate();
        }
        catch (SQLException e)
        {
            throw new RuntimeException(e);
        }
        finally
        {
            DBUtil.close(null,statement,resultSet);
        }
        return count;
    }

    @Override
    public Account selectByActno(String actno) {
        ResultSet resultSet = null;
        PreparedStatement statement = null;
        Connection connection = null;
        Account account = new Account();
        try
        {
            connection = DBUtil.getConnection();
            String sql = "select * from t_act where actno = ?";
            statement = connection.prepareStatement(sql);
            statement.setString(1,actno);
            resultSet = statement.executeQuery();
            if(resultSet.next())
            {
                account.setActno(actno);
                Long id = resultSet.getLong("id");
                account.setId(id);
                double balance = resultSet.getDouble("balance");
                account.setBalance(balance);
            }
        }
        catch (SQLException e)
        {
            throw new RuntimeException(e);
        }
        finally
        {
            DBUtil.close(null,statement,resultSet);
        }
        return account;
    }

    @Override
    public List<Account> selectAll() {
        ResultSet resultSet = null;
        PreparedStatement statement = null;
        Connection connection = null;
        List<Account> list = null;
        try
        {
            connection = DBUtil.getConnection();
            String sql = "select * from t_act";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while(resultSet.next())
            {
                list = new ArrayList<Account>();
                Account account = new Account();
                String actno = resultSet.getString("actno");
                account.setActno(actno);
                Long id = resultSet.getLong("id");
                account.setId(id);
                double balance = resultSet.getDouble("balance");
                account.setBalance(balance);
                list.add(account);
            }
        }
        catch (SQLException e)
        {
            throw new RuntimeException(e);
        }
        finally
        {
            DBUtil.close(null,statement,resultSet);
        }
        return list;
    }
}

自定义App异常类

package com.bjpowernode.Bank.exception;

public class AppException extends Exception
{
    public AppException(){}

    public AppException(String message) {
        super(message);
    }
}

自定义转账异常类

package com.bjpowernode.Bank.exception;

public class MoneyNotEnoughException extends Exception
{
    public MoneyNotEnoughException(){}
    public MoneyNotEnoughException(String msg){super(msg);}
}

ORM对象映射类

package com.bjpowernode.Bank.Pojo;

public class Account
{
    private Long id;
    private String actno;
    private Double balance;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", actno='" + actno + '\'' +
                ", balance=" + balance +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public Double getBalance() {
        return balance;
    }

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

    public Account(Long id, String actno, Double balance) {
        this.id = id;
        this.actno = actno;
        this.balance = balance;
    }

    public Account() {
    }
}

Service类

package com.bjpowernode.Bank.service;

import com.bjpowernode.Bank.Dao.AccountDao;
import com.bjpowernode.Bank.exception.MoneyNotEnoughException;

public interface AccountService
{
    //业务类,专注于业务处理
    //不写别的,只处理业务信息
    void transfer(String fromActno,String toActno,double money) throws Exception;
}

Service实现类

package com.bjpowernode.Bank.service.Impl;

import com.bjpowernode.Bank.Dao.AccountDao;
import com.bjpowernode.Bank.Dao.Impl.AccountDaoImpl;
import com.bjpowernode.Bank.Pojo.Account;
import com.bjpowernode.Bank.exception.AppException;
import com.bjpowernode.Bank.exception.MoneyNotEnoughException;
import com.bjpowernode.Bank.service.AccountService;
import com.bjpowernode.Bank.utils.DBUtil;

import java.sql.Connection;
import java.sql.SQLException;

public class AccountServiceImpl implements AccountService
{
    private AccountDao accountDao = new AccountDaoImpl();
    @Override
    public void transfer(String fromActno, String toActno, double money) throws Exception {
        //在service层开启事务
        try
        {
            Connection connection = DBUtil.getConnection();
            connection.setAutoCommit(false);
            Account fromAccount = accountDao.selectByActno(fromActno);
            if(fromAccount.getBalance() < money)
            {
                throw new MoneyNotEnoughException("余额不足");
            }
            Account toAccount = accountDao.selectByActno(toActno);
            fromAccount.setBalance(fromAccount.getBalance() - money);
            toAccount.setBalance(toAccount.getBalance() + money);
            int count = accountDao.update(fromAccount);
            count = count + accountDao.update(toAccount);
            if(count != 2)
            {
                throw new AppException("转账失败");
            }
            connection.commit();
        }
        catch(SQLException e)
        {
            throw new AppException("转账异常,请联系客服");
        }
        finally
        {
            DBUtil.close(DBUtil.getConnection(),null,null);
        }
    }
}

Controller或Servlet

package com.bjpowernode.Bank.servlet;

import com.bjpowernode.Bank.service.AccountService;
import com.bjpowernode.Bank.service.Impl.AccountServiceImpl;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/bank/transfer")
public class AccountServlet extends HttpServlet
{
    private AccountService accountService = new AccountServiceImpl();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        //接收数据
        String fromActno = request.getParameter("fromActno");
        String toActno = request.getParameter("toActno");
        Double value = Double.valueOf(request.getParameter("Value"));
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try
        {
            accountService.transfer(fromActno,toActno,value);
            out.println("转账成功");
        }
        catch (Exception e)
        {
            out.println(e.getMessage());
        }
    }
}

使用ThreadLocal改造过的工具类

package com.bjpowernode.Bank.utils;

import java.sql.*;
import java.util.ResourceBundle;

public class DBUtil
{
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
    private static ResourceBundle bundle = ResourceBundle.getBundle("resources.jdbc");
    private static String driver = bundle.getString("driver");
    private static String url = bundle.getString("url");
    private static String user = bundle.getString("user");
    private static String password = bundle.getString("password");
    //JDBC工具类
    static
    {
        //注册驱动
        try
        {
//            Class.forName("com.mysql.jdbc.Driver");//避免写死
            Class.forName(driver);
        }
        catch (ClassNotFoundException e)
        {
            throw new RuntimeException(e);
        }
    }
    public static Connection getConnection() throws SQLException {
        //获取链接
        Connection connection = threadLocal.get();
        if(connection == null)
        {
            connection = DriverManager.getConnection(url,user,password);
            threadLocal.set(connection);
        }
        return connection;
    }
    //释放资源
    public static void close(Connection connection, Statement statement, ResultSet resultSet)
    {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (connection != null) {
            try {
                connection.close();
                //因为Tomcat服务器内置了一个线程池
                //线程池内置了很多创建好的线程对象
                //存在重复使用的可能,如果没有移除,就会导致另一个分配到这个线程的人
                //会导致获取到已经关闭的connection对象
                threadLocal.remove();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!