事务提交与回滚,要么同时成功,要么同时失败,防止order和order_item一个有数据,一个因为发生异常没有数据
public class OrderServlet extends BaseServlet {
OrderService orderService = new OrderServiceImpl();
public void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cart cart = (Cart) req.getSession().getAttribute("cart");
User loginUser = (User) req.getSession().getAttribute("user");
//法一: if (loginUser == null) {
// req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
// return;
// }
//法二:filter
Integer userId = loginUser.getId();
String orderId = null;
try {
orderId = orderService.createOrder(cart, userId);
JdbcUtils.commitAndClose();
} catch (Exception e) {
e.printStackTrace();
JdbcUtils.rollbackAndClose();
}
req.setAttribute("orderId",orderId);
req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req, resp);
req.getSession().setAttribute("orderId",orderId);
resp.sendRedirect("http://localhost:8080/pages/cart/checkout.jsp");
}
}
使用ThreadLocal确保所有操作都使用同一个Connection来实现事务管理
public class JdbcUtils {
private static DruidDataSource dataSource; // 德鲁伊数据库连接池
private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
static {
try {
// 读取配置文件
Properties properties = new Properties();
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
properties.load(inputStream);
// 创建数据库连接池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接池的连接
* @return 如果返回null,说明连接失败,有值则成功
*/
public static Connection getConnection(){
Connection connection = conns.get();
if (connection == null) {
try {
connection = dataSource.getConnection();
conns.set(connection); // 保存到ThreadLocal中,供后面的jdbc操作
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
return connection;
}
/**
* 提交事务并关闭释放连接
*/
public static void commitAndClose() {
Connection connection = conns.get();
if (connection != null) {
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
conns.remove(); // Tomcat底层使用线程池技术
}
/**
* 回滚事务并关闭释放连接
*/
public static void rollbackAndClose() {
Connection connection = conns.get();
if (connection != null) {
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
conns.remove(); // Tomcat底层使用线程池技术
}
/*
public static void close(Connection connection){
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
*/
}
使用filter给Service方法都加上try-catch
public class TransactionFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(servletRequest, servletResponse);
JdbcUtils.commitAndClose();
} catch (Exception e) {
JdbcUtils.rollbackAndClose();
e.printStackTrace();
}
}
}
web.xml
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.lwt.filter.admin.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在BaseServlet中 向外抛出异常。防止发生异常后,还能进行事务提交操作。
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
这个报错信息表示 当使用filter过滤器后,如果已经使用了转发或重定向,随之会进行事务提交,则之后再不能够再操作request域对象