1、利用 ThreadLocal和Filter,管理Web开发中数据库事务的原理图解
2、TransactionFilter的实现
2.1 关于事务Filter的讲解
对于事务相关的Filter,应该在请求的路径,里面含数据库的操作时,才会开启事务。如果访问的是静态资源,例如,1.png就不应该开启事务。所以事务的开启,不是一进入到Filter就开启事务。因为,如果一进入到Filter就开启事务,这样的话请求静态资源,也会开启事务。所以将事物的开启,放到获取数据库的连接Connection时,才开启事务。也就是,只有在进行数据库相关操作时,才会开启事务。因为数据库的连接Connection,是放在ThreadLocal中的,所以第二个DAO获取的数据库连接Connection,也就是第一个DAO已经开启事务的连接。关于数据库连接Connection的管理,见JdbcUtils.java的实现。并且在Filter的最后,应该关闭连接,释放资源。
2.2 关于事务的执行
当在Service或者Servlet中,抛出运行时异常,那么Filter中,public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {}方法里面,在chain.doFilter(request, response)后面的语句,将不会执行。
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import cn.itcast.utils.JdbcUtils;
public class TransactionFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// index.jsp dao dao
// 1.jpg
try {
System.out.println("进入了事务拦截器");
// 1.拦截下来后:获取连接、开启事务、并把连接绑定到当前线程
// 1.1对于静态资源,不拦截;只有涉及到数据库操作时,才开启事务的管理。
// 1.2对于dao操作数据库时,在JdbcUtils.getConnection()时,会自动开启事务
// JdbcUtils.getConnection();
// JdbcUtils.StartTransaction();
chain.doFilter(request, response);
// 2.目标资源执行 dao1 conn1 = JdbcUtils.getConnection();
// dao2 conn2 = JdbcUtils.getConnection() 因为ThreadLoca,conn2就是上面开启事务的连接
// 3.获取当前线程上绑定的连接,提交事务,并关闭链接,释放连接与当前线程的绑定
System.out.println("关闭事务");
JdbcUtils.commitTransaction();
} finally {
JdbcUtils.closeConn();
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
}
3、利用ThreadLocal来管理,数据库连接池的实现
import java.sql.Connection;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* <pre>
* 用于保证一个请求,用一个数据库的Connection
* 再结合,事务拦截器,用于做事务管理
* </pre>
*
*/
public class JdbcUtils {
/**
* <pre>
* 用于保证一个请求,用一个数据库的Connection
* 再结合,事务拦截器,用于做事务管理
* </pre>
*
*/
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
/**
* 数据库的数据源
*/
private static DataSource ds;
/**
* 使用c3p0初始化,数据库连接源
*/
static {
ds = new ComboPooledDataSource();
}
public static DataSource getDataSource() {
return ds;
}
/**
* 获取和当前线程先关的数据库连接Connection
*
* @return
*/
public static Connection getConnection() {
try {
Connection conn = tl.get();
if (conn == null) {
conn = ds.getConnection();
// 自己管理事务
conn.setAutoCommit(false);
}
tl.set(conn);
return conn;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 手动的开启事务
*/
public static void StartTransaction() {
try {
Connection conn = getConnection();
// 自己管理事务,开启事务
conn.setAutoCommit(false);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 手动的提交事务
*/
public static void commitTransaction() {
try {
Connection conn = getConnection();
if (conn != null) {
// 关闭事务
conn.commit();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 手动的回滚
*/
public static void rollbackTransaction() {
try {
Connection conn = getConnection();
if (conn != null) {
// 回滚事务
conn.rollback();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 关闭连接,记得将ThreadLocal里面的Connection移除
*/
public static void closeConn() {
Connection conn = null;
try {
conn = getConnection();
if (conn != null) {
conn.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
tl.remove();
}
}
}
4、Servlet中,对于异常的处理
5、源码下载