Java -- JDBC学习笔记6、事务

1、事务

数据库系统保证在一个事务中的所有SQL要么全部执行成功,要么全部不执行。就像转账一样、任何一方出现异常,那么转账就无法成功。

1.1、JDBC事务

JDBC事务,就是在Java中用来控制数据库事务的。JDBC的一切行为包括事务是基于一个Connection的,通过Connection对象进行事务管理。常用的和事务相关的方法是: setAutoCommit、commit、rollback等。

1.1.1、setAutoCommit()

  • 开启事务的关键代码是conn.setAutoCommit(false),表示关闭自动提交。

1.1.2、commit()

  • 提交事务的代码在执行完指定的若干条SQL语句后,调用conn.commit()提交事务。

1.1.3、rollback()

  • 如果出现异常,就使用rollback()方法回滚事务。

2、案列

新建Java项目、模拟银行转账功能、使用JDBC事务保证数据的完整性。

2.1、具体实现

  • 在数据库中新建Account表,里边四个字段、分别是:主键(卡号)、密码、金额、姓名。再添加两条数据,如图:
  • 新建实体类,添加字段、get和set方法、构造方法。
  • 完善DBTutils工具类,再添加三个方法、分别是:开启事务、提交事务、回滚事务。另外,将首次获取的连接对象存放到ThreadLocal中,那么本次操作至始至终就用这一个连接对象。
public class DBUtils
{
    private static final ResourceBundle resourceBundle;
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    static
    {
        resourceBundle = ResourceBundle.getBundle("db");
        try
        {
            Class.forName(resourceBundle.getString("dirver"));
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
    }

    public static Connection getConnection()
    {
        Connection conn = threadLocal.get();
        try
        {
            if (conn == null)
            {
                conn = DriverManager.getConnection(resourceBundle.getString("url"), resourceBundle.getString("user"), resourceBundle.getString("password"));
                threadLocal.set(conn);
            }
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        return conn;
    }

    public static void closeDb(Connection conn, Statement statement, ResultSet rs)
    {
        try
        {
            if (conn != null)
            {
                conn.close();
                threadLocal.remove();//释放连接后,将threadlocal中的连接对象移除
            }
            if (statement != null)
            {
                statement.close();
            }
            if (rs != null)
            {
                rs.close();
            }
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
    }
    //开启事务
    public static void begin()
    {
        try
        {
            Connection conn = getConnection();
            conn.setAutoCommit(false);
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
    }
    public static void commit()
    {
        Connection conn = null;
        try
        {
            conn = getConnection();
            conn.commit();
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        finally
        {
            //提交事务后释放连接资源
            closeDb(conn, null, null);
        }
    }
    public static void rollback()
    {
        Connection conn = null;
        try
        {
            conn = getConnection();
            conn.rollback();
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        finally
        {
            //回滚后释放连接资源
            closeDb(conn, null, null);
        }
    }
  • 在Dao层新建接口AccountDao,定义两个方法、分别是查询和修改,如下:
public interface AccountDao
{
    //根据id查询表数据
    public Account select(int id);
    //修改Account表数据
    public int update(Account account);
}
  • 实现AccountDao接口
public class AccountDaoImpl implements AccountDao
{
    @Override
    public Account select(int id)
    {
        //实例化Account对象
        Account account = new Account();
        PreparedStatement preparedStatement = null;
        ResultSet rs = null;
        try
        {
            //根据卡号查询表数据
            String sql = "select id,pwd,balance,name from Account where id=?";
            Connection conn = DBUtils.getConnection();
            preparedStatement = conn.prepareStatement(sql);
            preparedStatement.setInt(1, id);

            rs = preparedStatement.executeQuery();

            while (rs.next())
            {
                int aid = rs.getInt(1);
                String pwd = rs.getString(2);
                double balance = rs.getDouble(3);
                String name = rs.getString(4);
                account.setId(aid);
                account.setPwd(pwd);
                account.setBalance(balance);
                account.setName(name);
            }
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        finally
        {
            DBUtils.closeDb(null, preparedStatement, rs);
        }
        return account;
    }

    public int update(Account account)
    {
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        String sql = "update Account set pwd=?,balance=?,name=? where id=?";
        try
        {
            conn = DBUtils.getConnection();
            preparedStatement = conn.prepareStatement(sql);
            preparedStatement.setString(1, account.getPwd());
            preparedStatement.setDouble(2, account.getBalance());
            preparedStatement.setString(3, account.getName());
            preparedStatement.setInt(4, account.getId());
            int result = preparedStatement.executeUpdate();
            return result;
        }
        catch (SQLException sqlException)
        {
            sqlException.printStackTrace();
        }
        finally
        {
            DBUtils.closeDb(null, preparedStatement, null);
        }
        return 0;
    }
  • 在service层中创建接口AccountService
public interface AccountService
{
    /**
     *
     * @param fromId:转账卡号
     * @param toId:接收转账卡号
     * @param pwd:密码
     * @param money:转账金额
     */
    public void transfer(int fromId, int toId, String pwd, double money);
}
  • 实现service接口,如下:
@Override
    public void transfer(int fromId, int toId, String pwd, double money)
    {
        AccountDao accountDao = new AccountDaoImpl();
        try
        {
            //开启事务
            DBUtils.begin();

            Account account = accountDao.select(fromId);
            //判断卡号是否正确
            if (account == null)
            {
                throw new RuntimeException("卡号有误");
            }
            //判断密码是否正确
            if (!pwd.equals(account.getPwd()))
            {
                throw new RuntimeException("密码有误");
            }
            //判断金额是否充足
            if (account.getBalance() < money)
            {
                throw new RuntimeException("余额不足");
            }
            Account toAccount = accountDao.select(toId);
            //判断对方卡号是否正确
            if (toAccount == null)
            {
                throw new RuntimeException("对方卡号不存在");
            }
            //修改账户金额、减去转账金额
            account.setBalance(account.getBalance() - money);
            accountDao.update(account);

            //修改对方账户金额,加上转入金额
            toAccount.setBalance(toAccount.getBalance() + money);
            accountDao.update(toAccount);
            System.out.println("转账成功");

            //提交事务
            DBUtils.commit();
        }
        catch (RuntimeException e)
        {
            System.out.println("转账失败");
            DBUtils.rollback();//回滚事务
            e.printStackTrace();
        }
    }

概括来讲、就是先设置conn.setAutoCommit(false)、代码顺利执行完成后使用conn.commit()提交事务,如果有异常就rollback()回滚事务。另外、不管是提交事务还是回滚事务,都要将连接对象释放。

posted @ 2021-05-05 10:40  初晨~  阅读(337)  评论(0编辑  收藏  举报