数据库创建两张表进行测试
mysql> create table student(id int , name varchar(50));
mysql> create table classroom(id int , name varchar(50));
首先写从数据库获取连接的ConnectionUtil.java
package com.zf.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public final class ConnectionUtil { private final static String username = "root"; private final static String password = "root"; private final static String url = "jdbc:mysql:///threadlocal"; private ConnectionUtil(){ } static{ try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection openConnection(){ Connection conn = null ; try { conn = DriverManager.getConnection(url, username, password); } catch (SQLException e) { e.printStackTrace(); } return conn ; } }
然后写管理Connection的SessionManager 类似于hibernate 的 SessionFactory
package com.zf.util; import java.sql.Connection; import java.sql.SQLException; /** * 单例实现 * @author zhoufeng * */ public final class SessionManager { private static ThreadLocal<Connection> session = new ThreadLocal<Connection>(); private static final SessionManager sessionManager = new SessionManager() ; private SessionManager(){} ; /** * 在获取SessionManager时 ,就绑定Connection到ThreadLocal中 * @return */ public static SessionManager newInstance(){ try { if(session.get() == null || session.get().isClosed()){ session.set(ConnectionUtil.openConnection()) ; } } catch (SQLException e) { e.printStackTrace(); } return sessionManager; } public Connection getCurrentSession(){ return session.get() ; } public void beginTransaction(){ try { session.get().setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } public void rollback(){ try { session.get().rollback() ; session.get().close(); } catch (SQLException e) { e.printStackTrace(); } } public void commit(){ try { session.get().commit() ; session.get().close(); } catch (SQLException e) { e.printStackTrace(); } } }
然后是操作数据库两张表的Dao
package com.zf.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import com.zf.util.SessionManager; public class ClassRoomDao { public boolean addClassRoom(int id , String name) throws SQLException{ String sql = "insert into classroom values(? , ?)"; Connection conn = SessionManager.newInstance().getCurrentSession() ; System.out.println(Thread.currentThread().getId() + " addClassRoom " + conn); PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, id); ps.setString(2, name); int count = ps.executeUpdate(); return count > 0 ; } }
package com.zf.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import com.zf.util.SessionManager; public class StudentDao { public boolean addStudent(int id , String name) throws SQLException{ String sql = "insert into student values(? , ?)"; Connection conn = SessionManager.newInstance().getCurrentSession() ; System.out.println(Thread.currentThread().getId() + " addStudent " + conn); PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, id); ps.setString(2, name); int count = ps.executeUpdate(); return count > 0 ; } }
接下来就定义Service接口 并实现
package com.zf.service; import java.sql.SQLException; public interface SCService { void addStudent(int id , String name) throws SQLException; }
package com.zf.service; import java.sql.SQLException; import com.zf.dao.ClassRoomDao; import com.zf.dao.StudentDao; import com.zf.util.SessionManager; public class SCServiceImpl implements SCService{ public void addStudent(int id , String name) throws SQLException{ StudentDao sd = new StudentDao() ; ClassRoomDao cr = new ClassRoomDao() ; sd.addStudent(id, name); if(id > 8) //测试是否会回滚事务 throw new RuntimeException("my exception"); cr.addClassRoom(id, name); } }
package com.zf.util; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 用该类来代理Service , 为需要代理的Service加入事务 。 * 实现AOP事务管理 * @author zhoufeng * */ public class ServiceFactory { public static Object proxyService(final Object obj , final Class<?>[] inf){ Object proxyService = Proxy.newProxyInstance(obj.getClass().getClassLoader(), inf , new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SessionManager sm = SessionManager.newInstance(); Object result = null ; try { sm.beginTransaction() ; result = method.invoke(obj, args); sm.commit(); } catch (Exception e) { sm.rollback(); e.printStackTrace(); } return result; } }) ; return proxyService ; } }
测试
package com.zf.test; import java.sql.SQLException; import com.zf.service.SCService; import com.zf.service.SCServiceImpl; import com.zf.util.ServiceFactory; public class Test01 { public static void main(String[] args) { final SCService ss = (SCService) ServiceFactory.proxyService(new SCServiceImpl() , new Class[]{SCService.class}); /** * 用两条线程分别执行service方法 * 结果会看到同一条线程中 各个Dao中获取的Connection是相同的 * 第二条线程发生了异常,并且事务回滚了 */ new Thread(new Runnable() { @Override public void run() { try { ss.addStudent(8, "name8"); } catch (SQLException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { ss.addStudent(9, "name9"); } catch (SQLException e) { e.printStackTrace(); } } }).start(); } }