javawebday51(BaseServlet 对JbdcUtils进行修改完善 事务处理)

BaseServlet
1、希望在一个Servlet中可以有多个请求处理方法
2、客户端发送请求时,必须多给出一个参数,用来说明要调用的方法
    请求处理方法的签名必须与service相同,即返回值和参数,以及声明的异常都相同
3、客户端必须传递名为method的参数
domain:User
dao:UserDao
service:UserService
servlet:UserService

vouid init(ServletConfig config)
void destory()
void service(ServletRequest,ServletResponse)
    throws IOException,ServletException{
        在这里让他去调用其他的方法
        要求:用户发出请求时,需要给出一个参数,来说明要调用哪一个方法
        //获取参数,通过参数名称来确定要调用的方法
}
http://localhost:8080/xxx/AServlet?m=addUser

Service事务
    在Service使用ThreadLocal来完成事务,为学习Spring事务做准备
1、DAO中的事务
    比较容易处理,但不应该存在业务,而只是对数据库的基本访问
2、Service才是处理事务的地方
    DAO不是处理事务的地方,因为DAO中的每一个方法都是对数据库的一次操作,而Service中的方法才是对应一个业务逻辑,
    也就是说我们需要在Service中的一方法中调用DAO的多个方法,而这些方法应该在一个事务中

    怎么才能让DAO的多个方法使用相同的Connection呢。方法不能再自己来获得Connection,而是由外界传递进去

    在Service中调用DAO的多个方法时,传递相同的Connection就可以了

    但是,在Service中不应该出现Connection,应该只在DAO中出现,因为是JDBC的东西,JDBC的东西是用来连接数据库的,
    连接数据库是DAO的事,但是事务是Service的事,不能放到DAO中

3、修改JdbcUtils
    把对事务的开启和关闭放到JdbcUtils中,在Service中调用JdbcUtils的方法来完成事务的处理,但在Service中就不会再出现Connection了

    DAO中的方法不用再让Service来传递Connection了。DAO会主动从JdbcUtils中获取Connection对象,这样JdbcUtils成为DAO和Service的中介了

    我们在JdbcUtils中添加beginTransaction()和rollbackTransaction()以及commitTransaction()方法      
public class BaseServlet extends HttpServlet {
    public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
         * 1、获取参数,用来识别用户想请求的方法 需要在地址栏加?method=xxx
         * 2、然后判断是哪一个方法,是哪一个我们就调用哪一个 重用性差
         */
        String methodName = req.getParameter("method");
//      if(methodName.equals("addUser")){
//          addUser(req,resp);
//      } else if(methodName.equals("editUser")){
//          editUser(req, resp);
//      } else if(methodName.equals("deleteUser")){
//          deleteUser(req, resp);
//      }
        if(methodName==null || methodName.trim().isEmpty()){
            throw new RuntimeException("没有传递method参数 无法确定要调用的方法");
        }
        /*
         * 得到方法名称,是否可以通过反射来调用方法
         * 1、得到方法名,通过方法名再得到Method类的对象
         *  需要得到class,然后调用它的方法进行查询。得到Method
         *  我们要查询是当前类的方法,所以我们需要得到当前类的Class
         */
        Class c=  this.getClass();//得到当前类的class对象
        Method method = null;
        try {
            method = c.getMethod(methodName, 
                    HttpServletRequest.class,HttpServletResponse.class);
        } catch (Exception e) {
            throw new RuntimeException("要调用的方法:"+methodName+"(HttpServletRequest,HttpResponse),不存在");
        } 
        /*
         * 调用method表示的方法
         */
        try {
            //反射调用
            String result = (String) method.invoke(this, req,resp);//this.addUser(req,resp) 
            /*
             * 获取请求处理方法执行后返回的字符串,表示转发或重定向的路径
             * 完成转发或重定向
             */
            /*
             * 如果用户返回的字符串为null,或为"",那么什么也不做
             */
            if(result == null || result.trim().isEmpty()){
                return;
            }
            /*
             * 查看返回的字符串是否包含冒号,如果没有,表示转发
             * 如果有:使用冒号分割字符串,得到前缀和后缀
             * 其中前缀如果是f,表示转发,如果是r表示重定向,后缀就是要转发或重定向的连接 
             */
            if(result.contains(":")){
                //使用冒号分割字符串,得到前缀和后缀
                int index = result.indexOf(":");//获取冒号的位置
                String s = result.substring(0,index);//截取出前缀 表示操作
                String path = result.substring(index+1);//截取出后缀 表示路径
                if(s.equalsIgnoreCase("r")){//如果前缀是r那么重定向
                    resp.sendRedirect(req.getContextPath()+path);
                }else if(s.equalsIgnoreCase("f")){
                    req.getRequestDispatcher(path).forward(req, resp);
                } else{
                    throw new RuntimeException("指定的操作"+s+"当前版本还不支持");
                }
            } else{//没有冒号 默认为转发
                req.getRequestDispatcher(result).forward(req, resp);
            }
        } catch (Exception e) {
            System.out.println("调用的"+methodName+",内部抛出了异常");
            throw new RuntimeException(e);
        }

    }
}
/**
 * 给出多个请求处理方法
 * 请求处理方法:除了名称以外,都与service方法相同
 * @author Administrator
 *
 */
public class AServlet extends BaseServlet {


    public void addUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("addUser");
        //throw new IOException("测试");
    }


    public void editUser(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("editUser");

    }
    public void deleteUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("deleteUser");
    }


}
public class BServlet extends BaseServlet {

    public String fun1(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("fun1()");
        return "f:/index.jsp";
    }
    public String fun2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        return "r:/index.jsp";
    }
    public String fun3(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("fun1()");
        return "d:/WEB-INF/files/a.rmvb";
    }
}

JdbcUtils

public class JdbcUtils {
    //配置文件的默认配置。要求必须给出c3p0-config.xml
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    //是事务专用连接   每个事务有自己独立的线程
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    /**
     * 使用连接池返回一个连接对象
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException{
        Connection con = tl.get();
        //当con 不等于null,说明已经嗲用过beginTransaction(),表示开启事务
        if(con !=null) return con;
        return dataSource.getConnection();
    }
    /**
     * 返回连接池对象
     */
    public static DataSource getDataSource(){
        return dataSource;
    }
    /**
     * 开启事务
     * 1、获取一个Connection 设置它的setAutoCommit(false)
     * 2、还要保证dao中使用的连接是我们刚刚创建的
     * --------------------------------------------------
     * 1、创建一个Connection,设置为手动提交
     * 2、把这个Connection给dao用
     * 3、还要让commitTransaction或rollbackTransaction可以获取到
     * @throws SQLException 
     */
    public static void beginTransaction() throws SQLException{
        Connection con = tl.get();
        if(con != null) throw new SQLException("已经事务,不要重复开启");
        /*
         * 1、给con赋值
         * 2、给con设置为手动提交
         */
        con = getConnection();//给con赋值,表示事务已经开始
        con.setAutoCommit(false);

        tl.set(con);//把当前线程的连接保存起来
    }
    /**
     * 提交事务
     * 1、获取beginTransaction提供的Connection,然后调用commit方法 
     * @throws SQLException 
     */
    public static void commitTransaction() throws SQLException{
        Connection con = tl.get();//获取当前线程的专用连接
        if(con == null) throw new SQLException("还没开启事务,不能提交");
        /*
         * 1、直接使用con.commit()
         */
        con.commit();
        con.close();
        //重新创建Connection 不然获取的是已经关闭的
        //把它设置为null,表示事务已经结束了,下次再去调用getConnection()返回的就不是con了
        //con = null;
        tl.remove();//从tl中移除连接
    }
    /**
     * 提交事务
     * 1、获取beginTransaction提供的Connection,然后调用commit方法 
     * @throws SQLException 
     */
    public static void rollbackTransaction() throws SQLException{
        Connection con = tl.get();
        if(con == null) throw new SQLException("还没开启事务,不能回滚");
        /*
         * 1、直接使用con.rollback()
         */
        con.rollback();
        con.close();
        tl.remove();
    }
    /**
     * 释放连接
     * @param con
     * @throws SQLException 
     */
    public static void releaseConnection(Connection connection) throws SQLException{
        Connection con = tl.get();
        /*
         * 判断是不是事务专用,如果是,就不关闭,
         * 如果不是,那么就要关闭
         */
        //如果con==null 那么说明现在没有事务,那么connection一定不是事务专用
        if(con == null) connection.close();
        //如果con!=null 说明有事务,那么需要判断参数连接是否域con相等。若不等,说明参数连接不是事务专用连接
        if(con != connection) connection.close();
    }
}
public class AccountDao {
    public static void update(String name,double money) throws SQLException{
        QueryRunner qr = new TxQueryRunner();
        String sql = "update account set balance=balance+? where aname=?";
        Object[] params ={money,name};
        /*//需要自己来提供连接,保证多次调用的是使用同一连接 直接使用QueryRunner
        Connection con = JdbcUtils.getConnection();
        qr.update(con,sql,params);

        JdbcUtils.releaseConnection(con);*/
        //使用TxQueryRunner后
        qr.update(sql,params);
    }
}
public class Demo1 {
    private AccountDao dao = new AccountDao();
    @Test
    public void serviceMethod() throws SQLException{
        try {
            JdbcUtils.beginTransaction();
            dao.update("zs",100);
            //if(true) throw new RuntimeException();
            dao.update("ls",-100);
            JdbcUtils.commitTransaction();

            JdbcUtils.getConnection();
        } catch (SQLException e) {
            try {
                JdbcUtils.rollbackTransaction();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            throw e;
        }
    }
}
/**
 * 这个类中的方法,自己来处理连接的问题
 * 无需外界的传递
 *  通过JdbcUtils.getConnection()得到连接,有可能是事务连接,也可能是普通的连接
 *  JdbcUtils.releaseConnection()完成对连接的释放,如果是普通连接 关闭之
 */
public class TxQueryRunner extends QueryRunner{

    @Override
    public int[] batch(String sql, Object[][] params) throws SQLException {
        /*
         * 1、得到连接
         * 2、执行父类方法,传递连接对象
         * 3、释放连接
         * 4、返回值
         */
        Connection con = JdbcUtils.getConnection();
        int[] result = super.batch(sql, params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> List<T> execute(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        List<T> result = super.execute(con,sql,rsh,params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T insert(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        T result = super.insert(con,sql,rsh,params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T insert(String sql, ResultSetHandler<T> rsh) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        T result = super.insert(con,sql,rsh);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T insertBatch(String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        T result = super.insertBatch(con,sql,rsh,params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        T result = super.query(con,sql, rsh,params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        T result = super.query(con,sql, rsh);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object... params) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        int result = super.update(con,sql,params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object param) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        int result = super.update(con,sql,param);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        int result = super.update(con,sql);
        JdbcUtils.releaseConnection(con);
        return result;
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值