引入
以下的注册界面,在注册过程中不应该出现function1成功,而function2不成功的现象,而事务的完成应该以业务为单位进行操作。
- 获取链接,取消自动提交conn.setAutoCommit(false)
- 执行相应的操作
- 提交事务 conn.commit()
- 回滚事务 conn.rollback
所以说我们应该在service上开启事务,进行管理,而为了更好的进行判断,我们将此代码前置到filter中
1 2 3 4 5 6 7
| try{ autoCommit(false); 放行(); commit(); }catch(){ rollback; }
|
但是需要将function1于function2处于同一个链接中
首先我们需要设置过滤器:
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
| @WebFilter("*.do") public class OpenSessionInViewFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { Filter.super.init(filterConfig); }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { try{ System.out.println("开始事务"); TransactionManager.beginTrans(); filterChain.doFilter(servletRequest,servletResponse); System.out.println("提交事务"); TransactionManager.commit(); }catch (Exception e){ e.printStackTrace(); System.out.println("回滚事务"); try { TransactionManager.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } try { ConnUtil.closeConn(); } catch (SQLException e) { e.printStackTrace(); }
}
@Override public void destroy() { Filter.super.destroy(); } }
|
而在这里负责事务的发送等操作,使过滤器进行调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class TransactionManager {
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
public static void beginTrans() throws SQLException { ConnUtil.getConnection().setAutoCommit(false); }
public static void commit() throws SQLException { Connection connection= ConnUtil.getConnection(); connection.commit();
}
public static void rollback() throws SQLException { Connection connection= ConnUtil.getConnection(); connection.rollback(); } }
|
ThreadLocal
本地线程,可以通过set方法在当前线程上存储数据,get可以获取数据
1 2 3 4 5 6 7 8 9 10 11 12
| public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { map.set(this, value); } else { createMap(t, value); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
|
而一个简易的ThreadLocal可以理解为下:
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:
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
| public class MyThreadLocal { private final ConcurrentHashMap<Thread, Object> valueMap = new ConcurrentHashMap<>(); public void set(Object newValue) { valueMap.put(Thread.currentThread(), newValue); } public Object get() { Thread currentThread = Thread.currentThread(); Object o = valueMap.get(currentThread); if (o == null && !valueMap.containsKey(currentThread)) { o = initialValue(); valueMap.put(currentThread, o); } return o; } public void remove() { valueMap.remove(Thread.currentThread()); } public Object initialValue() { return null; } }
|