事务示例之转账

事务示例之转账

1.创建数据库,填充数据:

数据库名称db_account,表名tb_account,插入几条数据。

image

2.配置数据库信息

在项目的WebContent/META-INF目录下创建一个context.xml文件。如图:
image

在context.xml文件中配置:

<?xml version="1.0" encoding="UTF-8"?>
<Context>      
    <Resource name="jdbc/account" auth="Container" type="javax.sql.DataSource"
    maxActive="100" maxIdle="30" maxWait="10000"
    username="root" password="root" driverClassName="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/db_account"/>
</Context>

这里采用的是Javaweb自带的DBCP配置,详细参考Javaweb配置常用的数据源配置

3.创建项目结构

如图:

image

  • AccountDao

    public interface AccountDao {
        /**
         * 更新账户
         * @param account
         */
        public boolean updateAccount(Account account) throws SQLException;  
    
        /**
         * 查找账户
         * @param account
         * @return
         */
        public Account findAccountByName(String account);
    }
  • AccountDaoImpl

    public class AccountDaoImpl implements AccountDao{
    
        public AccountDaoImpl() {
    
        }
    
        @Override
        public boolean updateAccount(Account account) {
            Connection conn = ManagerThreadLocal.getConn();
            if(conn==null){
                throw new NullPointerException("Connection is null");
            }
    
            PreparedStatement ps = null;
            try {
                 ps = conn.prepareStatement("update tb_account set money=? where name=?");
                 if(ps==null){
                     throw new NullPointerException("PreparedStatement is null");
                 }
                 ps.setDouble(1, account.getMoney());
                 ps.setString(2, account.getName());
                 int i = ps.executeUpdate();
                 return i>0;
    
            } catch (SQLException e) {
                e.printStackTrace();
            }
    
            return false;
        }
    
        @Override
        public Account findAccountByName(String account) {
    
            Connection conn = ManagerThreadLocal.getConn();
    
            if(conn==null){
                throw new NullPointerException("Connection is null");
            }
    
            Account a = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                 ps = conn.prepareStatement("select name,money from tb_account where name=?");
                 if(ps==null){
                     throw new NullPointerException("PreparedStatement is null");
                 }
                 ps.setString(1, account);
    
                 rs = ps.executeQuery();
                 if(rs==null){
                     throw new NullPointerException("ResultSet is null");
                 }
    
                 if(rs.next()){
                     a = new Account();
                     a.setName(rs.getString(1));
                     a.setMoney(rs.getDouble(2));
                     return a;
                 }
            } catch (SQLException e) {
                e.printStackTrace();
            }
    
            return a;
        }
    }
    
  • Account

    public class Account {
        private int id;
        private String name;
        private double money;
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public double getMoney() {
            return money;
        }
        public void setMoney(double money) {
            this.money = money;
        }
        @Override
        public String toString() {
            return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
        }   
       }
  • AccountService

     public interface AccountService {
            /**
             * 转账
             * @param fromName
             * @param toName
             * @param money
             */
            public void transfer(String fromName,String toName,double money);
        }
  • AccountServiceImpl

    public class AccountServiceImpl implements AccountService{
    
        @Override
        public void transfer(String fromName, String toName, double money) {
    
            AccountDao accountDao = new AccountDaoImpl();
            Account fromAccount = accountDao.findAccountByName(fromName);
            if(fromAccount==null){
                throw new RuntimeException("你的账户不存在");
            }
            if(money > fromAccount.getMoney()){
                throw new RuntimeException("你的账户的钱不够");
            }
            fromAccount.setMoney(fromAccount.getMoney() - money);
    
            Account toAccount = accountDao.findAccountByName(toName);
            if(toAccount==null){
                throw new RuntimeException("对方的账户不存在");
            }
            toAccount.setMoney(toAccount.getMoney() + money);
    
            //开启事务
            ManagerThreadLocal.startTransaction();
    
            try {
                        accountDao.updateAccount(fromAccount);
                //int i = 10/0; //测试事务代码
                accountDao.updateAccount(toAccount);
                //提交事务
                ManagerThreadLocal.commit();
            } catch (SQLException e) {
                //事务回滚
                ManagerThreadLocal.rollback();
                e.printStackTrace();
            }finally {
                ManagerThreadLocal.close();
            }
        }
    }
  • TransferServlet

        @WebServlet({ "/TransferServlet", "/transfer" })
    public class TransferServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
    
            public TransferServlet() {
                super();
    
            }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            AccountService accountService = new AccountServiceImpl();
            accountService.transfer("10010", "10000", 100);
    
    
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
    }
  • ManagerThreadLocal

    public class ManagerThreadLocal {
    
        private static ThreadLocal<Connection> tl = new ThreadLocal<>();
    
        private static Connection getConnection(){
            Connection conn  = null;
            try {
                Context context = new InitialContext();
                DataSource dataSource = (DataSource) context.lookup("java:/comp/env/jdbc/account");
                conn = (Connection) dataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (NamingException e) {
                e.printStackTrace();
            }
            return conn;
        }
    
        public static Connection getConn(){
            Connection conn = null;
            conn = tl.get();
            if(conn==null){
                conn = getConnection();
                tl.set(conn);
            }
            return conn;
        }
    
        public static void startTransaction(){
            try {
                getConn().setAutoCommit(false);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        public static void commit(){
            try {
                getConn().commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        public static void rollback(){
            try {
                getConn().rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        public static void close(){
            try {
                getConn().close();
                tl.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

ManagerThreadLocal的作用是通过ThreadLocal类(内部是由Map实现)缓存一个Connection对象,保证连接的conn对象是同一个,将Connection对象解耦出来,实现事务操作(保证了事务的conn对象是同一个)

执行结果:注释AccountServiceImpl类中transfer方法中的如下代码,数据库数据同时改变,注释打开,数据保持不变。

//int i = 10/0; //测试事务代码
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值