首页IT科技功能实现方式是什么(day12-功能实现11)

功能实现方式是什么(day12-功能实现11)

时间2025-04-29 21:23:43分类IT科技浏览3532
导读:家居网购项目实现011 以下皆为部分代码,详见 https://github.com/liyuelian/furniture_mall.git...

家居网购项目实现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. 设置开启手动提交事务以及事务回滚的时机

解决方法:

使用Filter+ThreadLocal进行事务管理 在一次http请求         ,servlet-service-dao的调用过程               ,始终是一个线程        ,这是使用ThreadLocal的前提 使用ThreadLocal来确保所有dao操作都在同一个Connection连接对象中完成 根据过滤器的机制      ,在所有代码都走完之后会回来走过滤器的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需求分析/图解

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

28.2思路分析

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

28.3代码实现

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

页面代码:略               。

在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               ,访问结果:

内部发生错误:

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
最早的笔记本电脑(最早的笔记本是什么) 长沙最大的烂尾楼在哪里(最大的LAN联机派对是什么)