项目实战系列三: 家居购项目 第五部分

🌳显示订单[订单管理]

🌳暂时缺货

需求分析
1.如果某家居库存为0, 首页的"Add to Cart" 按钮显示为"暂时缺货"
2.后台也加上校验. 只有在 库存>0 时, 才能添加到购物车

在这里插入图片描述

代码实现
1.修改web/views/customer/index.jsp

<c:if test="${furn.inventory <= 0}">
    <button disabled title="Add To Cart" class="add-to-cart">Add
        To Cart[缺货]
    </button>
</c:if>
<c:if test="${furn.inventory > 0}">
    <button furnId="${furn.id}" title="Add To Cart" class="add-to-cart">Add
        To Cart
    </button>
</c:if>

2.修改src/com/zzw/furns/web/CartServlet.java, 当添加购物车时, 要保证商品的库存>购物车中的已有的商品数量, 不然库存不够没法继续添加

//添加一个添加家居到购物车的方法
protected void addItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    int id = DataUtils.parseInt(request.getParameter("id"), 1);//家居id
    //根据id获取对应的家居信息
    Furn furn = furnService.queryFurnById(id);
    //先把正常的逻辑走完, 再处理异常的情况
    if (furn != null && furn.getInventory() <= 0) {
        response.sendRedirect(request.getHeader("Referer"));
        return;
    }

    HttpSession session = request.getSession();
    Cart cart = (Cart) session.getAttribute("cart");
    //得到购物车 有可能是空的,也有可能是上次的
    if (cart == null) {
        cart = new Cart();
        session.setAttribute("cart", cart);
    }
    Map<Integer, CartItem> items = cart.getItems();
    if (cart.isEmpty()) {
        CartItem cartItem = new CartItem(id, furn.getName(), 1, furn.getPrice(), furn.getPrice());
        cart.addItem(cartItem, furn.getInventory());
    }
    //构建一条家居明细: id,家居名,数量, 单价, 总价
    //count类型为Integer, 不赋值默认值为null
    CartItem cartItem = new CartItem(id, furn.getName(), 1, furn.getPrice(), furn.getPrice());
    //将家居明细加入到购物车中. 如果家居id相同,数量+1;如果是一条新的商品,那么就新增
    cart.addItem(cartItem, furn.getInventory());
    System.out.println("cart= " + cart);

    String referer = request.getHeader("referer");
    response.sendRedirect(referer);
}

3…修改src/com/zzw/furns/entity/Cart.java

//添加购物车, 只有在结账的时候库存变化.
public void addItem(CartItem cartItem, Integer inventory) {
    //检查要添加的商品是否已存在购物车中
    //如果没有, 则添加; 如果有, 则增加购物车中商品的数量.
    //这里默认添加的数量为1
    CartItem item = items.get(cartItem.getId());
    if (item == null) {
        items.put(cartItem.getId(), cartItem);
    } else if (item.getCount() < inventory){//添加购物车时, 要保证商品的库存>购物车中商品数量, 不然没法下单发货
        item.setCount(item.getCount() + 1);
        //修改总价,新的数量✖单价
        item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));
    }
}

需求分析
1.购物车里, 更新家居数量时,前台加以限制.
2.后台也加上校验. 只有在 库存>0 时, 才能更新家居数量

代码实现
1.修改src/com/zzw/furns/web/CartServlet.java

//修改指定cartItem的数量和总价
protected void updateCount(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //传 商品id, 商品count
    Integer id = DataUtils.parseInt(request.getParameter("id"), 1);
    Integer count = DataUtils.parseInt(request.getParameter("count"), 0);

    //得到商品库存
    Furn furn = furnService.queryFurnById(id);
    if (furn != null && !(furn.getInventory() > 0 && count <= furn.getInventory())) {
        response.sendRedirect(request.getHeader("Referer"));
        return;
    }

    //得到购物车
    HttpSession session = request.getSession();
    Cart cart = (Cart) session.getAttribute("cart");
    if (cart != null) {
        cart.updateCount(id, count);
    }
    response.sendRedirect(request.getHeader("Referer"));
}

🌳管理订单

需求分析
1.完成订单管理-查看
2.具体流程参考显示家居
3.静态页面order.html 和 order_detail.html 已提供

程序框架图

在这里插入图片描述

代码实现
1.修改com.zzw.furns.OrderDAO

//根据memberId返回Order对象
public List<Order> queryOrderByMemberId(Integer memberId);

2.修改com.zzw.furns.impl.OrderDaoImpl

@Override
public List<Order> queryOrderByMemberId(Integer memberId) {
    String sql = "SELECT id, `create_time` as createTime, price, `count`, " +
            "`status`, member_id as memberId FROM `order` WHERE member_Id=?" +
            " order by createTime desc";
    List<Order> orderList = queryMany(sql, Order.class, memberId);
    return orderList;
}

3.测试, 修改OrderDaoTest

@Test
public void queryOrderByMemberId() {
    List<Order> orderList = orderDAO.queryOrderByMemberId(9);
    for (Order order : orderList) {
        System.out.println(order);
    }
}

4.修改com.zzw.furns.OrderService

//显示订单
public List<Order> queryOrderByMemberId(Integer memberId);

5.修改com.zzw.furns.impl.OrderServiceImpl

@Override
public List<Order> queryOrderByMemberId(Integer memberId) {
    List<Order> list = orderDAO.queryOrderByMemberId(memberId);
    return list;
}

6.测试,修改com.zzw.furns.impl.OrderServiceTest

@Test
public void queryOrderByMemberId() {
    List<Order> orders = orderService.queryOrderByMemberId(9);
    for (Order order : orders) {
        System.out.println(order);
    }
}

7.web层 - 修改src/com/zzw/furns/web/OrderServlet.java, 增加listByMemberId方法

protected void listByMemberId(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    //获取到登陆的member对象
    Member member = (Member) req.getSession().getAttribute("member");
    if (member == null) {//说明用户没有登录
        //重定向到登陆页面
        req.getRequestDispatcher("/views/member/login.jsp")
                .forward(req, resp);
        return;//直接返回
    }

    List<Order> orders = orderService.queryOrderByMemberId(member.getId());
    //把订单集合放入到request域中
    req.setAttribute("orders", orders);
    //请求转发到order.jsp
    req.getRequestDispatcher("/views/order/order.jsp")
            .forward(req, resp);
}

8.新增web/views/order/order.jsp

<c:forEach items="${requestScope.orders}" var="order">
    <tr>
        <td class="product-name">${order.id}</td>
        <td class="product-name">${order.createTime}</td>
        <td class="product-price-cart"><span class="amount">${order.price}</span></td>
        <td class="product-name"><a href="#">
            <c:choose>
                <c:when test="${order.status == 1}">未发货</c:when>
                <c:when test="${order.status == 2}">已发货</c:when>
                <c:when test="${order.status == 3}">未结账</c:when>
                <c:otherwise>错误</c:otherwise>
            </c:choose>
        </a></td>
        <td class="product-remove">
            <a href="#"><i class="icon-eye"></i></a>
        </td>
    </tr>
</c:forEach>

🌳管理订单项

程序框架图

在这里插入图片描述

代码实现
1.修改com.zzw.furns.OrderDAO

//根据id返回Order对象
public Order queryOrderById(String id);

2.修改com.zzw.furns.impl.OrderDaoImpl

@Override
public Order queryOrderById(String id) {
    String sql = "SELECT id, `create_time` AS createTime, price, `count`, `status`, " +
            "member_id AS memberId FROM `order` WHERE id= ?";
    Order order = querySingle(sql, Order.class, id);
    return order;
}

3.测试, 修改OrderDaoTest

@Test
public void queryOrderById() {
    Order order = orderDAO.queryOrderById("968d9002-a92e-445f-b77e-7654d7a7598e");
    System.out.println(order);
}

4.修改com.zzw.furns.OrderItemDAO

//返回所有OrderItem对象
public List<OrderItem> queryOrderItemByOrderId(String orderId);

5.修改com.zzw.furns.impl.OrderItemDaoImpl

@Override
public List<OrderItem> queryOrderItemByOrderId(String orderId) {
    String sql = "SELECT id, `name`, `count`, price, total_price as totalPrice, " +
            "order_id as orderId FROM order_item WHERE order_id = ?";
    List<OrderItem> orderItemList = queryMany(sql, OrderItem.class, orderId);
    return orderItemList;
}

6.测试, 修改OrderItemDaoTest

@Test
public void queryOrderItemByOrderId() {
    String orderId = "54062db9-e514-4218-b784-a0f5db91ef41";
    List<OrderItem> orderItemList = orderItemDAO.queryOrderItemByOrderId(orderId);
    for (OrderItem orderItem : orderItemList) {
        System.out.println(orderItem);
    }
}

7.修改com.zzw.furns.OrderService

//根据id返回Order对象
public Order queryOrderById(String id);

8.修改com.zzw.furns.impl.OrderServiceImpl

@Override
public Order queryOrderById(String id) {
    Order order = orderDao.queryOrderById(id);
    List<OrderItem> orderItems = orderItemDao.queryOrderItemByOrderId(id);
    order.setItems(orderItems);
    return order;
}

9.测试,修改com.zzw.furns.impl.OrderServiceTest

@Test
public void queryOrderById() {
    Order order = orderService.queryOrderById("968d9002-a92e-445f-b77e-7654d7a7598e");
    System.out.println(order);
}

10.web层 - 修改src/com/zzw/furns/web/OrderServlet.java, 增加listOrderItemByOrderId方法

protected void listOrderItemByOrderId(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String orderId = req.getParameter("orderId");
    orderId = (orderId == null) ? "" : orderId;

    Order order = orderService.queryOrderById(orderId);

    req.setAttribute("order", order);
    req.getRequestDispatcher("/views/order/order_detail.jsp").forward(req, resp);
}

11.修改前端order.jsp

<td class="product-remove">
    <a href="orderServlet?action=listOrderItemByOrderId&orderId=${order.id}"><i class="icon-eye"></i></a>
</td>

12.新增并修改order_detail.jsp

<c:forEach items="${requestScope.order.items}" var="orderItem">
    <tr>
        <td class="product-name"><a href="#">${orderItem.name}</a></td>
        <td class="product-price-cart"><span class="amount">$${orderItem.price}</span></td>
        <td class="product-quantity">${orderItem.count}</td>
        <td class="product-subtotal">$${orderItem.totalPrice}</td>
    </tr>
</c:forEach>
<div class="cart-shiping-update-wrapper">
    <h4>共${requestScope.order.totalCount}件商品 总价 ${requestScope.order.price}元</h4>
    <div class="cart-clear">
        <a href="#">继 续 购 物</a>
    </div>
</div>

🌈过滤器权限验证

需求分析
1.加入过滤器权限验证
2.如果没有登陆, 查看购物车和添加到购物车, 就会自动转到会员登陆页面

代码实现
1.修改web.xml, 配置过滤器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--过滤器一般我们配置在上面-->
    <filter>
        <filter-name>AuthFilter</filter-name>
        <filter-class>com.zzw.furns.filter.AuthFilter</filter-class>
        <init-param>
        	   <!--这里配置了后, 还需要在过滤器中处理-->
            <param-name>excludedUrls</param-name>
            <param-value>/views/manage/manage_login.jsp,/views/member/login.jsp</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>AuthFilter</filter-name>
        <!--这里配置要验证的url
            1.在filter-mapping中的url-pattern配置 要拦截/验证的url
            2.对于我们不去拦截的url, 就不配置
            3.对于要拦截的目录中的某些要放行的资源, 再通过配置指定
        -->
        <url-pattern>/views/cart/*</url-pattern>
        <url-pattern>/views/manage/*</url-pattern>
        <url-pattern>/views/member/*</url-pattern>
        <url-pattern>/views/order/*</url-pattern>
        <url-pattern>/cartServlet</url-pattern>
        <url-pattern>/manage/furnServlet</url-pattern>
        <url-pattern>/orderServlet</url-pattern>
    </filter-mapping>
</web-app>

2.新建src/com/zzw/furns/filter/AuthFilter.java,过滤器逻辑判断

/**
 * 这是用于权限验证的过滤器, 对指定的url进行验证
 * 如果登陆过, 就放行; 如果没有登陆, 就回到登陆页面
 *
 * @author 赵志伟
 * @version 1.0
 */
public class AuthFilter implements Filter {

    private List<String excludedUrls;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //获取到配置的excludedUrls
        String strExcludedUrls = filterConfig.getInitParameter("excludedUrls");
        //分割 /views/manage/manage_login.jsp,/views/member/login.jsp
        String[] split = strExcludedUrls.split(",");
        //将 splitUrl 转成 list
        excludedUrls = Arrays.asList(split);
        System.out.println("excludedUrls= " + excludedUrls);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("请求/cartServlet 被拦截...");
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        //得到请求的url
        String url = request.getServletPath();
        System.out.println("url= " + url);

        //判断是否要验证
        if (!excludedUrls.contains(url)) {
            //获取到登陆的member对象
            Member member = (Member) request.getSession().getAttribute("member");
            if (member == null) {//说明用户没有登录
                //转发到登陆页面, 转发不走过滤器
                servletRequest.getRequestDispatcher("/views/member/login.jsp")
                        .forward(servletRequest, servletResponse);

                重定向-拦截-重定向-拦截-重定向-拦截
                //((HttpServletResponse) servletResponse)
                //        .sendRedirect(request.getContextPath() + "/views/member/login.jsp");
                return;//直接返回
            }

        }
        //验证通过, 放行
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("请求/cartServlet验证通过, 放行");
    }

    @Override
    public void destroy() {

    }
}

🌈事务管理

数据不一致问题

1.将FurnDAOImpl.java的updateFurn方法的sql故意写错. [furnDAO.updateFurn(furn);由ctrl+alt+b定位到updateFurn的实现方法]
2.在OrderServiceImpl的saveOrder()方法内捕获一下异常, 目的是保证程序能够继续执行
3.查看数据库里的数据会有什么结果. 会出现数据不一致的问题.

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
我在首页购买了一个小台灯, 数据库中生成了对应的订单和订单项, 但家居表里该小台灯的销量和库存没有变化, 纹丝不动. 相当于客户下单了, 但没有给人家发货.

在这里插入图片描述

在这里插入图片描述

程序框架图

思路分析
1.使用 Filter + ThreadLocal 来进行事务管理
2.说明: 在一次http请求中, servlet-service-dao 的调用过程, 始终是一个线程, 这是使用ThreadLocal的前提
3.使用ThreadLocal来确保所有dao操作都在同一个Connection内

程序框架图

在这里插入图片描述

1.修改src/com/zzw/furns/utils/JdbcUtilsByDruid.java工具类

public class JdbcUtilsByDruid {
    private static DataSource dataSource;
    //定义属性ThreadLocal, 这里存放一个Connection
    private static ThreadLocal<Connection> threadlocalConn = new ThreadLocal<>();

    /**
     * 从ThreadLocal获取connection, 从而保证在一个线程中
     * 获取的是同一个Connection
     */
    public static Connection getConnection() {
        Connection connection = threadlocalConn.get();
        if (connection == null) {//说明当前的threadlocal没有这个连接
            try {
                //就从数据库连接池中取出连接放入threadlocal
                connection = dataSource.getConnection();
                //将连接设置为手动提交, 既不要让它自动提交
                connection.setAutoCommit(false);
                threadlocalConn.set(connection);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        return connection;
    }

    /**
     * 提交事务
     */
    public static void commit() {
        Connection connection = threadlocalConn.get();
        if (connection != null) {
            try {
                connection.commit();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            //1.当提交后, 需要把connection从threadlocalConn中清除掉
            //2.不然会造成threadlocalConn长时间持有该连接, 会影响效率
            //3.也因为Tomcat底层使用的是线程池技术
            threadlocalConn.remove();
        }
    }

    /**
     * 说明: 所谓回滚是 回滚/撤销 和connection管理的操作 删除/修改/添加
     */
    public static void rollback() {
        Connection connection = threadlocalConn.get();
        if (connection != null) {
            try {
                connection.rollback();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                }
            }
            threadlocalConn.remove();
        }
    }

    //public static Connection getConnection() {
    //    try {
    //        return dataSource.getConnection();
    //    } catch (SQLException e) {
    //        throw new RuntimeException(e);
    //    }
    //}

    static {
        try {
            Properties properties = new Properties();
            //properties.load(new FileInputStream("src/druid.properties"));
            //因为我们是web项目, 它的工作目录在out. 文件的加载,需要使用类加载器
            //需要找到我们的工作目录
            properties.load(JdbcUtilsByDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

2.修改src/com/zzw/furns/dao/BasicDAO.java
删掉各个方法finally代码块里的close方法. 只有在事务结束后才实施关闭连接的操作. 一是提交事务后关闭连接; 二是增删改出错后, 回滚关闭连接.

public class BasicDAO<T> {

    private QueryRunner queryRunner = new QueryRunner();

    /**
     * @param sql sql语句, 可以有 占位符?
     * @param clazz 传入一个类的Class对象, 例如User.class
     * @param objects 传入具体的值, 对应sql中的占位符?, 可以有多个
     * @return 根据对应的User.class, preparedStatement->resultSet->ArrayList, 返回最终的ArrayList集合
     */
    public List<T> queryMany(String sql, Class<T> clazz, Object... objects) {
        Connection connection = null;
        try {
            connection = JdbcUtilsByDruid.getConnection();
            List<T> tList =
                    queryRunner.query(connection, sql, new BeanListHandler<>(clazz), objects);
            return tList;
        } catch (SQLException e) {
            throw new RuntimeException(e);//编译异常->运行异常抛出
        }
    }

    //查询单行, 返回的是一个对象
    public T querySingle(String sql, Class<T> clazz, Object... objects) {
        Connection connection = null;
        try {
            connection = JdbcUtilsByDruid.getConnection();
            T object
                    = queryRunner.query(connection, sql, new BeanHandler<>(clazz), objects);
            return object;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //查询某一字段
    public Object queryScalar(String sql, Object... objects) {
        Connection connection = null;
        try {
            connection = JdbcUtilsByDruid.getConnection();
            Object query = queryRunner.query(connection, sql, new ScalarHandler(), objects);
            return query;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public int update(String sql, Object... objects) {
        Connection connection = null;
        try {
            //这里是从数据库连接池获取connection
            //注意:每次从连接池中取出connection, 不能保证是同一个
            //1.我们目前已经是从和当前线程关联的ThreadLocal获取的connection
            //2.所以可以保证是同一个连接[在同一个线程中/在同一个请求中 => 因为一个请求对应一个线程]
            connection = JdbcUtilsByDruid.getConnection();
            return queryRunner.update(connection, sql, objects);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

3.修改控制层src/com/zzw/furns/web/OrderServlet.java, 进行事务管理
前提OrderServiceImpl里报错的代码取消try-catch, 在OrderServlet控制层捕获

protected void saveOrder(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    HttpSession session = request.getSession();
    Cart cart = (Cart) session.getAttribute("cart");
    //如果购物车为空,或者购物车在session,没有家居信息
    if (cart == null || cart.isEmpty()) {
        response.sendRedirect(request.getHeader("Referer"));
        return;
    }

    //获取登录到的member
    Member member = (Member) session.getAttribute("member");
    if (member == null) {
        //用户未登录,不可点击订单管理,重定向到登陆页面
        request.getRequestDispatcher("/views/member/login.jsp").forward(request, response);
        return;
    }
    String orderId = "";
    try {
        orderId = orderService.saveOrder(cart, member.getId());
        JdbcUtilsByDruid.commit();
    } catch (Exception e) {
        JdbcUtilsByDruid.rollback();
        e.printStackTrace();
    }
    session.setAttribute("orderId", orderId);
    response.sendRedirect(request.getContextPath() + "/views/order/checkout.jsp");
}

Transaction过滤器

程序框架图

在这里插入图片描述

体会: 异常机制是可以参与业务逻辑的

在这里插入图片描述
在这里插入图片描述

代码实现
1.在OrderService控制层里取消捕获异常, 将代码重新改回下述模样
String orderId = orderService.saveOrder(cart, member.getId());

2.同时BasicServlet模板里也取消异常捕获, 或者将异常抛出, 代码如下

String action = req.getParameter("action");
System.out.println("action= " + action);//login register page
try {
    Method declaredMethod =
            this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
    System.out.println("declaredMethod= " + declaredMethod);
    System.out.println("this = " + this);//com.zzw.furns.web.MemberServlet@38f54ed7
    declaredMethod.invoke(this, req, resp);
    System.out.println("this.getClass() = " + this.getClass());
} catch (Exception e) {
    //将发生的异常, 继续throw
    throw new RuntimeException(e);
}

3.新建src/com/zzw/furns/filter/TransactionFilter.java.
在代码执行完毕后, 会运行到Transaction过滤器的后置代码, 在这里进行异常捕获, 如果发生异常, 则回滚.

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) {
        try {
            //放行
            chain.doFilter(request, response);
            JdbcUtilsByDruid.commit();//统一提交
        } catch (Exception e) {//出现了异常
            JdbcUtilsByDruid.rollback();//回滚
            e.printStackTrace();
        }
    }
}

配置web.xml

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

🌈统一错误页面

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

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

1.在/views/error引入404.html, 500.html, 修改成jsp文件

在这里插入图片描述

2.将跳转链接改成index.jsp

<a class="active" href="index.jsp">
    <h4 style="color: darkblue">您访问的页面不存在 返回首页</h4>
</a>
<a class="active" href="index.jsp">
    <h4 style="color: darkblue">sorry,您访问的页面出现了错误 返回首页</h4>
</a>

3.配置web.xml

<!--错误提示的配置一般写在web.xml的下面-->

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

4…修改事务过滤器, 将异常抛给tomcat

public class TransactionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            //放行
            filterChain.doFilter(servletRequest, servletResponse);
            JdbcUtilsByDruid.commit();//统一提交
        } catch (Exception e) {//出现了异常
            //只有在try{}中出现了异常, 才会进行catch{}
            //才会进行回滚
            JdbcUtilsByDruid.rollback();//回滚
            //抛出异常, 给tomcat. tomcat会根据error-page来显示对应页面
            throw new RuntimeException(e);
            //e.printStackTrace();
        }
    }

    @Override
    public void destroy() {

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~ 小团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值