转账基本流程:fromAccount 转入 toAccount 账户中一定金额
- 验证 fromAccount 是否存在
- 验证 fromAccount 是否密码正确
- 验证当前账户余额是否充足
- 验证 toAccount 是否存在
- 减少 fromAccount 的余额
- 增加 toAccount 的余额
1. ThreadLocal 类的使用
ThreadLocal 可以创建一个绑定了当前线程
与1个泛型对象
的键值对的对象(该类使用map键值对方式实现)。
ThreadLocal 很多地方叫做线程本地变量,也有些地方叫做线程本地存储
,ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。
ThreadLocalMap(ThreadLocal 类的一个静态内部类):
- 每个线程中都有一个自己的 ThreadLocalMap 类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
- 将一个
共用的 ThreadLocal 静态实例作为 key
,将不同对象的引用保存到不同线程的 ThreadLocalMap 中
,然后在线程执行的各处通过这个静态 ThreadLocal 实例的 get()方法取
得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
- ThreadLocalMap 其实就是线程里面的一个属性,它在 Thread 类中定义
ThreadLocal.ThreadLocalMap threadLocals = null;
因此 ThreadLocal 对象在使用时天生线程安全
。
常用方法:
- public void
set
(T value); // 设置当前线程绑定的对象
- public T
get
(); // 返回当前线程绑定的对象
- public void
remove
(); // 移除当前线程的绑定对象
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
public class DBUtils { private static final Properties properties = new Properties(); private static final ThreadLocal<Connection> THREAD_LOCAL = new ThreadLocal<>();
static { try { InputStream is = DBUtils.class.getResourceAsStream("/db.properties"); properties.load(is); Class.forName(properties.getProperty("driver")); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }
public static Connection getConnection() { Connection connection = THREAD_LOCAL.get(); try { if (connection == null) { connection = DriverManager.getConnection(properties.getProperty("url"), properties.getProperty("username"), properties.getProperty("password")); THREAD_LOCAL.set(connection); } } catch (SQLException e) { e.printStackTrace(); } return connection; }
public static void closeAll(Connection connection, Statement s, ResultSet r) { try { if (r != null) { r.close(); } if (s != null) { s.close(); } if (connection != null) { connection.close(); THREAD_LOCAL.remove(); } } catch (SQLException e) { e.printStackTrace(); } } }
|
2. 转账中的事务流程体现
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| public class T_AccountServiceImpl {
public String transfer(String fromNo, String pwd, String toNo, double money) { String result = "转账失败"; T_AccountDaoImpl t_accountDao = new T_AccountDaoImpl();
Connection connection = null;
try { connection = DBUtils.getConnection(); connection.setAutoCommit(false);
T_Account fromAcc = t_accountDao.select(fromNo); if(fromAcc == null) { throw new RuntimeException("::卡号不存在::"); } if (!fromAcc.getPassword().equals(pwd)) { throw new RuntimeException("::密码错误::"); } if (fromAcc.getBalance() < money) { throw new RuntimeException("::余额不足::"); } T_Account toAcc = t_accountDao.select(toNo); if (toAcc == null) { throw new RuntimeException("::对方卡号不存在::"); } fromAcc.setBalance(fromAcc.getBalance() - money); t_accountDao.update(fromAcc);
int i = 10/0;
toAcc.setBalance(toAcc.getBalance() + money); t_accountDao.update(toAcc);
result = "转账成功";
connection.commit(); } catch (RuntimeException | SQLException e) { try { if (connection != null) { System.out.println("出现了异常,回滚整个事务!"); connection.rollback(); } } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { DBUtils.closeAll(connection, null, null); } return result; } }
|