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()回滚事务。另外、不管是提交事务还是回滚事务,都要将连接对象释放。