家居项目--事务管理

需求(现状):

1.在进行生成订单时,同时还需要清空购物车、更新商品的库存和销量。是对多个表进行操作,需要进行事务管理,保证数据的一致性。
2.在此过程中, 每次对数据库的操作使用的connection都是从  工具类(基于druid数据库连接池技术)拿的,connection不一定是同一个。 

思路分析:

实现功能程序框架图

1. 使用 Filter + ThreadLocal 进行事务管理
2. 说明: 在一次 http 请求,servlet-service-dao 的调用过程,始终是一个线程, 这是使用 ThreadLocal 的前提

实现要点:

1.保证在一次请求中,拿到的connection是同一个,并提前关闭自动提交。

因为原先在对多个表操作时,每次操作使用的connection都是从工具类(基于druid数据库连接池技术)拿的,每次拿到的connection不一定相同,无法进行事务管理。

所以,可以使用ThreadLocal,存放一个connection对象,保证在一次http请求中使用的connection是同一个。并在将connection放入ThreadLocal前将其自动提交关闭  conn.setAutoCommit(false);

关键代码:

    private static DataSource ds;
    public static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    //在静态代码块完成 ds初始化
    static {
        Properties properties = new Properties();
        try {
            //因为这里是web项目,它的工作目录是out,文件加载,需要使用类加载器
            //properties.load(new FileInputStream("src\\druid.properties"));
            properties.load(JDBCUtilsByDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //编写getConnection方法
    public static Connection getConnection() throws SQLException {
        Connection conn = threadLocal.get();
        if (conn == null) {
            conn = ds.getConnection();
            //设置手动提交, 事务管理需要
            conn.setAutoCommit(false);
            threadLocal.set(conn);
        }

        return conn;
    }

2.再编写提交事务和回滚事务的方法

    //提交事务
    public static void commit() {
        Connection connection = threadLocal.get();
        if (connection != null) {
            try {
                connection.commit();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }finally {
                //关闭连接
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        //说明
        //1. 当提交后,需要把connection从 threadLocalConn 清除掉
        //2. 不然,会造成 threadLocalConn 长时间持有该连接, 会影响效率
        //3. 也因为我们Tomcat底层使用的是线程池技术
        threadLocal.remove();
    }

    //回滚事务
    public static void rollback() {
        Connection connection = threadLocal.get();
        if (connection != null) {
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        threadLocal.remove();
    }

2.使用Filter对事务进行控制提交

TransactionFilter 代码如下:

package com.jiamian.furns.filter;

import com.jiamian.furns.utils.JDBCUtilsByDruid;

import javax.servlet.*;
import java.io.IOException;

public class TransactionFilter implements Filter {
    public void destroy() {
    }

    
    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();//回滚
            e.printStackTrace();

            //抛出异常, 给tomcat, tomcat会根据errorpage 来显示对应页面
            //这里 体会: 异常机制是可以参与业务逻辑
            throw new RuntimeException(e);
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }

}

 TransactionFilter 的配置如下:

    <!--配置过滤器,该过滤器用于事务管理-->
    <filter>
        <filter-name>TransactionFilter</filter-name>
        <filter-class>com.jiamian.furns.filter.TransactionFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>TransactionFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

 注意事项(异常的处理):

要将事务异常抛给 TransactionFilter(一直抛,直到抛给TransactionFilter ),TransactionFilter捕获到异常才会回滚事务。若不抛给TransactionFilter,就无法进行事务管理。

最后,放上 基于druid数据库连接池的工具类第二版(事务管理版)

package com.jiamian.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;

/**
 * @author 韩顺平
 * @version 1.0
 * 基于druid数据库连接池的工具类
 */
public class JDBCUtilsByDruid {

    private static DataSource ds;
    public static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    //在静态代码块完成 ds初始化
    static {
        Properties properties = new Properties();
        try {
            //因为这里是web项目,它的工作目录是out,文件加载,需要使用类加载器
            //properties.load(new FileInputStream("src\\druid.properties"));
            properties.load(JDBCUtilsByDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //编写getConnection方法
    public static Connection getConnection() throws SQLException {
        Connection conn = threadLocal.get();
        if (conn == null) {
            conn = ds.getConnection();
            //设置手动提交, 事务管理需要
            conn.setAutoCommit(false);
            threadLocal.set(conn);
        }

        return conn;
    }

    //提交事务
    public static void commit() {
        Connection connection = threadLocal.get();
        if (connection != null) {
            try {
                connection.commit();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }finally {
                //关闭连接
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        //说明
        //1. 当提交后,需要把connection从 threadLocalConn 清除掉
        //2. 不然,会造成 threadLocalConn 长时间持有该连接, 会影响效率
        //3. 也因为我们Tomcat底层使用的是线程池技术
        threadLocal.remove();
    }

    //回滚事务
    public static void rollback() {
        Connection connection = threadLocal.get();
        if (connection != null) {
            try {
                connection.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

        threadLocal.remove();
    }

    //关闭连接, 老师再次强调: 在数据库连接池技术中,close 不是真的断掉连接
    //而是把使用的Connection对象放回连接池
    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);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值