家居网购项目实现011

以下皆为部分代码,详见 https://github.com/liyuelian/furniture_mall.git

27.功能25-事务管理

27.1下订单问题思考

在生成订单的功能中,系统会去同时修改数据库中的order,order_item,furn三张表,如果有任意一个表修改失败,就会出现数据不一致问题。因此出现了事务控制问题。

27.2思路分析

之前,我们每次调用底层的dao操作,每次进行的都是独立事务,因此一但在一次业务中调用了多个dao操作,就不能保证多表的事务一致性。

因为JDBC局部事务是控制是由java.sql.Connection来完成的,要保证多个DAO的数据访问处于一个事务中,我们需要保证他们使用的是同一个java.sql.Connection.

要保证数据一致性,就要使用事务。使用事务的前提是保证同一个连接connection。我们的想法是,在进行dao操作的前面就开启事务,然后在进行各种dao操作后,如果没有出现异常,则手动进行事务提交,否则进行回滚。

现在的问题是:

q1. 我们之前使用数据库连接池,无法保证每次进行dao操作都是同一个connection连接对象

q2. 设置开启手动提交事务以及事务回滚的时机

解决方法:

  1. 使用Filter+ThreadLocal进行事务管理
  2. 在一次http请求,servlet-service-dao的调用过程,始终是一个线程,这是使用ThreadLocal的前提
  3. 使用ThreadLocal来确保所有dao操作都在同一个Connection连接对象中完成
  4. 根据过滤器的机制,在所有代码都走完之后会回来走过滤器的chain.dofilter()的后置代码,这个特性非常适合进行事务管理

27.3代码实现

27.3.1uilts包

重写JDBCUtilsByDruid,修改getConnection方法,同时设置手动提交事务

package com.li.furns.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties; /**
* 基于Druid数据库连接池的工具类
*/
public class JDBCUtilsByDruid { private static DataSource ds;
//定义属性ThreadLocal,这里存放一个Connection
private static ThreadLocal<Connection> threadLocalConn = new ThreadLocal<>(); //在静态代码块完成ds的初始化
//静态代码块在加载类的时候只会执行一次,因此数据源也只会初始化一次
static {
Properties properties = new Properties();
try {
//因为我们是web项目,它的工作目录不在src下面,文件的加载需要使用类加载器
properties.load(JDBCUtilsByDruid.class.getClassLoader()
.getResourceAsStream("druid.properties"));
//properties.load(new FileInputStream("src\\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
} // //编写getConnection方法
// public static Connection getConnection() throws SQLException {
// return ds.getConnection();
// } /**
* 获取连接方法
* 从ThreadLocal中获取connection,
* 从而保证在同一个线程中获取的是同一个Connection
*
* @return
* @throws SQLException
*/
public static Connection getConnection() {
Connection connection = threadLocalConn.get();
if (connection == null) {//说明当前的threadLocalConn没有连接
//就从数据库连接池中获取一个连接,放到ThreadLocal中
try {
connection = ds.getConnection();
//设置为手动提交,即不要自动提交
connection.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
threadLocalConn.set(connection);
}
return connection;
} /**
* 提交事务
*/
public static void commit() {
Connection connection = threadLocalConn.get();
if (connection != null) {//确保该连接是有效的
try {
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();//将连接释放回连接池
} catch (SQLException e) {
e.printStackTrace();
}
}
//1.当提交后,需要把connection从threadLocalConn中清除掉
//2.否则会造成ThreadLocalConn长时间持有该连接,会影响效率
//3.也因为我们Tomcat底层使用的是线程池技术
threadLocalConn.remove();
}
} /**
* 回滚,回滚的是和connection相关的dml操作
*/
public static void rollback() {
Connection connection = threadLocalConn.get();
if (connection != null) {//保证当前的连接是有效的
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
threadLocalConn.remove();
} //关闭连接(注意:在数据库连接池技术中,close不是真的关闭连接,而是将Connection对象放回连接池中)
public static void close(ResultSet resultSet, Statement statemenat, Connection connection) {
try {
if (resultSet != null) {
resultSet.close();
}
if (statemenat != null) {
statemenat.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

因为现在连接的关闭是在commit或者rollback中发生的,因此BasicDAO中写的关闭连接已经没有意义了,将其删掉即可。

27.3.2filter

配置TransactionFilter

<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.li.furns.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!--这里我们对所有请求都进行事务管理-->
<url-pattern>/*</url-pattern>
</filter-mapping>

TransactionFilter:

package com.li.furns.filter;

import com.li.furns.utils.JDBCUtilsByDruid;

import javax.servlet.*;
import java.io.IOException; /**
* 管理事务
*
* @author 李
* @version 1.0
*/
public class TransactionFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
} public void destroy() {
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
try {
//先放行
chain.doFilter(request, response);
//统一提交
JDBCUtilsByDruid.commit(); } catch (Exception e) {
//只有在try{}中出现了异常,才会进行catch{}
//这里想要捕获异常,前提是底层的代码没有将抛出的异常捕获
JDBCUtilsByDruid.rollback();//回滚
e.printStackTrace();
}
}
}

由于之前在BasicServlet中捕获了异常,因此需要修改BasicServlet,将捕获的异常抛出给Filter,否则无法在出现异常时进行回滚。

27.4完成测试

为了测试,在FurnDAOImpl操作中写入错误的sql语句,模拟表操作失败

现在来测试一下,当发生dao操作失败后会产生什么现象。

登录用户,点击添加某个家居,点击购物车生成订单,因为生成订单涉及到furn表的操作,因此可以看到点击后页面没有跳转到正常的显示订单页面

查看后台输出,发现抛出异常

查看数据库:

相关的表没有进行改动,说明事务管理起作用了。

order_item表:

order表:

furn表:(操作前后的sales和stock字段一致)

28.功能26-统一错误提示页面

28.1需求分析/图解

  1. 如果在访问/操作网站时,出现了内部错误,统一显示 500.jsp
  2. 如果访问/操作不存在的页面/servlet时,统一显示 404.jsp

28.2思路分析

  1. 在发生错误/异常时,将错误/异常 抛给tomcat
  2. 在web.xml配置不同的错误显示不同的页面即可

28.3代码实现

404.jsp用于显示404错误;500.jsp用于显示服务器内部错误。

  1. 页面代码:略。

  2. 在web.xml文件中配置错误提示页:

<!--404错误提示页面-->
<error-page>
<error-code>404</error-code>
<location>/views/error/404.jsp</location>
</error-page>
<!--500错误提示页面-->
<error-page>
<error-code>500</error-code>
<location>/views/error/500.jsp</location>
</error-page>

如果在代码中捕获了异常,那么将不会起到效果,应该要将异常抛出给tomcat,让tomcat可以根据不同的异常进行页面展示。

TransactionFilter:

28.4完成测试

在浏览器中输入一个项目不存在的资源http://localhost:8080/furniture_mall/abc.jsp,访问结果:

内部发生错误:

day12-功能实现11的更多相关文章

  1. 数据库——mysql内置功能(11)

    1.视图 视图是一个虚拟表(非真实存在),其本质是(根据SQL语句获取动态的数据集,并未其命名),用户使用时只需使用(名称)即可获取结果集,可以将该结果集当做表来使用 使用视图我们可以把查询过程中的临 ...

  2. Java 11 新功能来了!

    关键时刻,第一时间送达! 目前 Oracle 已经发布了 Java Development Kit 10,下个版本 JDK 11 也即将发布.本文介绍 Java 11 的新功能. 根据Oracle新出 ...

  3. Java 11新功能抢先了解

    目前 Oracle 已经发布了 Java Development Kit 10,下个版本 JDK 11 也即将发布.本文介绍 Java 11 的新功能. 根据Oracle新出台的每6个月发布一次Jav ...

  4. Rspec: everyday-rspec实操: 第10章测试其他功能,第11章TDD 第12章总结。

    10.测试文件上传 作者推荐的Paperclip,官方维护组已经不推荐使用deprecated. 推荐使用rails自带的 ActiveStorage. Active Storage: 推进文件上传到 ...

  5. Eclipse功能集合

    大家好,这篇博客的目的是总结一下Eclipse这个软件中一些不为常用的功能.与大家分享.谢谢~ 1.利用one hour看了一下Eclipse的使用,用two hour写了这篇blog. 2.在现实项 ...

  6. (视频) 《快速创建网站》 2.3 WordPress初始化和功能简介

    本文是<快速创建网站>系列的第4篇,如果你还没有看过之前的内容,建议你点击以下目录中的章节先阅读其他内容再回到本文. 访问本系列目录,请点击:http://devopshub.cn/tag ...

  7. 《图解HTTP》 第11章 web的攻击技术

    11.1 针对Web的攻击技术 简单的HTTP协议本身并不存在安全性问题,所以协议本身几乎不会成为攻击的对象. 11.1.1 HTTP不具备必要的安全功能 11.1.2 在客户端即可篡改请求 在HTT ...

  8. 13.Django1.11.6文档

    第一步 入门 检查版本 python -m django --version 创建第一个项目 django-admin startproject mysite 运行 python manage.py ...

  9. 深入理解C++11【5】

    [深入理解C++11[5]] 1.原子操作与C++11原子类型 C++98 中的原子操作.mutex.pthread: #include<pthread.h> #include <i ...

  10. 【整理】Java 11新特性总结

    闲语 2018年9月25日,Java 11正式发布,与JDK 10不同,JDK 11将提供长期支持,还将作为Java平台的参考实现以及标准版(Java SE)11.Oracle直到2023年9月都会为 ...

随机推荐

  1. P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)

    显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...

  2. 齐博x1注意事项:再强调严禁用记事本改任何文件

    提醒大家,X1任何文件,不要用记事本修改.比如这个用户就改出问题了 导致后台不能升级. 当然这是问题之一, 还有其它意料之外的问题.还没发现. 这个用户做一个测试风格. 配置文件可能是用记事本修改的. ...

  3. 基于YOLO和PSPNet的目标检测与语义分割系统(python)

    基于YOLO和PSPNet的目标检测与语义分割系统 源代码地址 概述 这是我的本科毕业设计 它的主要功能是通过YOLOv5进行目标检测,并使用PSPNet进行语义分割. 本项目YOLOv5部分代码基于 ...

  4. 十七、Job与Cronjob

    Job 与 Cronjob 一.Job ​Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束. 特殊说明: 1.spec.template 格式同 Pod ​2 ...

  5. 谣言检测(RDCL)——《Towards Robust False Information Detection on Social Networks with Contrastive Learning》

    论文信息 论文标题:Towards Robust False Information Detection on Social Networks with Contrastive Learning论文作 ...

  6. 部署grafana+telegraf+influxdb 及 配置 jmeter后端监听

    搞性能测试,可以搭建Grafana+Telegraf+InfluxDB 监控平台,监控服务器资源使用率.jmeter性能测试结果等. telegraf: 是一个用 Go 编写的代理程序,可收集系统和服 ...

  7. WinDBG详解进程初始化dll是如何加载的

    一:背景 1.讲故事 有朋友咨询个问题,他每次在调试 WinDbg 的时候,进程初始化断点之前都会有一些 dll 加载到进程中,比如下面这样: Microsoft (R) Windows Debugg ...

  8. java将秒数转换为时分秒格式

    /** * 转换时间格式为xx小时xx分xx秒 * @param second xxxxx */ public String changeTimeFormat(String second) { Int ...

  9. perl使用print输入数据到文件

    #!usr/bin/perl use utf8; #引入utf8模块 脚本内的字符串使用utf8作为编码格式 binmode(STDOUT,":encoding(gbk)"); # ...

  10. Feign 实现 GET 方法传递 POJO

    Feign 实现 GET 方法传递 POJO 作者:Grey 原文地址: 博客园:Feign 实现 GET 方法传递 POJO CSDN:Feign 实现 GET 方法传递 POJO 需求 Sprin ...