小demo(续)----------转账思想的简单实现(加入事务机制、ThreadLocal,异常处理优化)

上篇博文链接:点击传送
此篇博文是对上篇博文中代码进行的改造

主要是加入了事务机制,简单来说就是将用到数据库的部分进行了事务的统一处理,使得用到的都是同一个事务(要把自动提交事务关闭,手动地进行提交,加入了ThreadLocal也保证了事务的统一性)
加入了ThreadLocal:ThreadLocal是一种基于当前线程安全的存取机制。我们把值存到ThreadLocal对象中时,只要当前线程还在,那么ThreadLocal对象中的值就还在。

数据库部分

与上篇博文中的数据库无异

前端页面部分

与上篇博文中的无异

引入MVC

MVC是一种基于Web开发的设计模式。该模式最重要的作用是分层开发思想。

M:model 模型 与数据相关的操作
V:view 视图 与页面相关的操作
C:controller 控制器 与调度相关的操作

在这里插入图片描述MVC在开发中具体对应的层
M层(模型层):service+dao
V层(视图层):一些页面,例如jsp
C层(控制器层):controller(也就是相当于servlet)
在这里插入图片描述

引入service层

引入了service层进行业务的处理
之前的业务处理写在了controller中会很杂乱,现在为了使得体现分层的开发思想,用service层来处理业务,controller来处理调度
面向接口编程
TaccountService.java

package com.service;

public interface TaccountService {
	//处理业务的接口
	public void taccount(String zcAccount,String zrAccount,String zzBalanceStr);
	
}

TaccountServiceImpl.java

package com.service.impl;

import java.sql.Connection;
import java.sql.SQLException;

import com.dao.TaccountDao;
import com.dao.impl.TaccountDaoImpl;
import com.service.TaccountService;
import com.util.DBUtil;

public class TaccountServiceImpl implements TaccountService {

	@Override
	public void taccount(String zcAccount, String zrAccount, String zzBalanceStr) {
		
		int zzBalance = Integer.valueOf(zzBalanceStr);
		
		/*
		     表单数据取得后,按照6步操作,来完成转账业务逻辑
		    (1)验证转出账号有没有
			(2)验证转入账号有没有
			(3)根据转出账号取出转出账号余额,看看钱够不够
			(4)更新转出账号余额(扣钱)
			(5)根据转入账号取出转入账号余额
			(6)更新转入账号余额(加钱)
		 */
		 
		TaccountDao taccountDao = new TaccountDaoImpl();
		
		
		Connection conn = null;
		try{
			//此时还没有conn,就会创建一个并保存到t(ThreadLocal)对象中
			conn = DBUtil.getConn();
			System.out.println("service:"+conn);
			//把自动提交事务变成手动提交事务
			conn.setAutoCommit(false);
			
			//下面开始进行业务处理时,保证是同一个conn事务
			System.out.println("开始转账");
			//开始处理业务逻辑
			//开始接触dao层
			//dao层中用到conn时,用的都是service创建(t对象中)的conn,保证了事务统一性
			//(1)验证转出账号有没有
			if(taccountDao.checkAccount(zcAccount)){
				
				//(2)验证转入账号有没有
				if(taccountDao.checkAccount(zrAccount)){
					
					//(3)根据转出账号取出转出账号余额,看看钱够不够
					int zcBalance = taccountDao.getBalanceByAccount(zcAccount);
					
					if(zcBalance >= zzBalance){
						
						//(4)更新转出账号余额(扣钱)
						taccountDao.updateBalanceByAccount(zcAccount, zcBalance-zzBalance);
						
						//(5)根据转入账号取出转入账号余额
						int zrBalance = taccountDao.getBalanceByAccount(zrAccount);
						
						//(6)更新转入账号余额(加钱)
						taccountDao.updateBalanceByAccount(zrAccount, zrBalance+zzBalance);
						
					}
					
				}
				
			}
			//手动提交事务
			conn.commit();
			/*异常处理优化,就是说,如果在dao层进行操作时,
			例如进行数据库查询账户是否存在,SQL语句出现错误,就会抛出一个运行时异常(要进行一个手动抛出)
			然后在处理业务时(运行taccountDao.checkAccount())会接收到这个异常,
			就跳到catch进行回滚处理
			*/
			//使用Exception可以接收出现的所有异常,然后进行回滚处理
		}catch(Exception e){
			try {
				//回滚,当出现运行时异常时就跳转到这进行回滚(一般是在进行数据操作(dao层)时出现异常)
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally{
			try {
				DBUtil.myClose(conn, null, null);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		
		

	}

}

工具类部分

因为引入ThreadLocal,所以会进行一个改造

package com.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBUtil {
	
	private DBUtil(){}
	
	static{
		
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}
	
	private static final String URL = "jdbc:mysql://localhost:3306/test";
	private static final String USER = "root";
	private static final String PASSWORD = "root";
	
	//指定了泛型,是Connection的
	private static ThreadLocal<Connection> t = new ThreadLocal<Connection>();
	
	//创建/取得连接
	/**
	 * service层是进行业务处理的,dao层是进行数据操作的,所以是先进行service层再到dao层的
	 * 所以肯定是service层先用到conn
	 * 当service层调用到getConn方法时,创建一个连接返回给service层,同时将此连接存放到t对象中
	 * 当dao层调用到getConn方法时,t对象中一定有conn了(service层执行时创建并保存的conn,同时也是service层开启事务的conn)
	 *                            将conn从t中取得,直接返回即可
	 */
	public static Connection getConn() throws SQLException{
		//这个就是进入到dao层操作后,要用到getConn()方法时conn拿到的存放在t(ThreadLocal)里面的conn
		Connection conn = t.get();
		
		//也就是service层中第一次用到getConn()时是没有conn的,就创建并保存到ThreadLocal对象中
		if (conn == null) {
		    //创建并保存conn
			conn = DriverManager.getConnection(URL, USER, PASSWORD);
			t.set(conn);
		}
		
		return conn;
		
	}
	
	
	//关闭资源
	public static void myClose(Connection conn,PreparedStatement ps,ResultSet rs) throws SQLException{
		
		//关闭的顺序为按照 创建的顺序  逆序关闭
		if(rs!=null){
			
			rs.close();
			
		}
		
		if(ps!=null){
			
			ps.close();
			
		}

		if(conn!=null){
	
			conn.close();
			
			//必须加
			t.remove();
	
		}
		
	}
		
}

dao层

数据操作部分,也就是进行jdbc的部分
面向接口编程
TaccountDao.java

package com.dao;

public interface TaccountDao {
	
	//验证账号有没有
	public boolean checkAccount(String account);
	
	//根据账号取余额
	public int getBalanceByAccount(String account);
	
	//根据账号更新余额
	public void updateBalanceByAccount(String account,int balance);
	
}

TaccountDaoImpl.java

package com.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.dao.TaccountDao;
import com.util.DBUtil;

//对接口进行实现,然后对里面的方法进行重写来实现几个操作
public class TaccountDaoImpl implements TaccountDao{
	
	//验证账号有没有
	@Override
	public boolean checkAccount(String account){
		
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		String sql = "select count(*) from t_account where account=?";
		boolean flag = true;
		
		try{
			
			//这个conn都是service层创建(t对象中)的conn
			conn = DBUtil.getConn();
			//验证事务统一性时做的验证处理
			System.out.println("checkAccount:"+conn);
			ps = conn.prepareStatement(sql);
			ps.setString(1,account);
			rs = ps.executeQuery();
			
			if(rs.next()){
				
				int count = rs.getInt(1);
				if(count!=1){
					
					flag = false;
					
				}
				
			}
			
			
		}catch(SQLException e){
			e.printStackTrace();
			//手动抛出异常,也就是说,如果发生异常,就手动抛出,然后在service层运行到这个方法时进行接收,然后进行回滚
			throw new RuntimeException();
		}finally{
			
			try {
				DBUtil.myClose(null, ps, rs);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
		}
		return flag;
	}

	//根据账户取余额
	@Override
	public int getBalanceByAccount(String account) {
		
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		String sql = "select balance from t_account where account=?";
		int balance = 0;
		
		try{
			
			//这个conn都是service层创建(t对象中)的conn
			conn = DBUtil.getConn();
			System.out.println("getBalanceByAccount:"+conn);
			ps = conn.prepareStatement(sql);
			ps.setString(1,account);
			rs = ps.executeQuery();
			
			if(rs.next()){
				
				balance = rs.getInt(1);
					
			}
			
			
		}catch(SQLException e){
			e.printStackTrace();
			//手动抛出异常
			throw new RuntimeException();
		}finally{
			
			try {
				DBUtil.myClose(null, ps, rs);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
		}
		return balance;
	}

	//根据账户更新余额
	@Override
	public void updateBalanceByAccount(String account, int balance) {
		
		Connection conn = null;
		PreparedStatement ps = null;
		String sql = "update t_account set balance=? where account=?";
		
		try{
			
			//这个conn都是service层创建(t对象中)的conn
			conn = DBUtil.getConn();
			System.out.println("updateBalanceByAccount:"+conn);
			ps = conn.prepareStatement(sql);
			ps.setInt(1,balance);
			ps.setString(2,account);
			
			ps.executeUpdate();
			
		}catch(SQLException e){
			e.printStackTrace();
			//手动抛出异常
			throw new RuntimeException();
		}finally{
			
			try {
				DBUtil.myClose(null, ps, null);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
		}
		
	}

}

事务统一性验证结果:可以看到都是同一个事务,也就是service层用到getConn()创建并保存到ThreadLocal对象中的conn,后面接触到dao层时,用到的conn都是ThreadLocal对象中的conn,以此保证了事务的统一性

在这里插入图片描述

controller层

调度层

TaccountController.java (实则是一个servlet)

package com.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.service.TaccountService;
import com.service.impl.TaccountServiceImpl;

public class TaccountController extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		System.out.println("开始执行转账操作");
		
		//接收表单参数
		String zcAccount = request.getParameter("zcAccount");
		String zrAccount = request.getParameter("zrAccount");
		String zzBalanceStr = request.getParameter("zzBalance");
		
		TaccountService ts = new TaccountServiceImpl();
		//处理业务 ---- 这里就是调度
		//相当于接收请求,然后把这个请求交给别的地方进行处理
		ts.taccount(zcAccount, zrAccount, zzBalanceStr);
		
		response.sendRedirect(request.getContextPath() + "/success.jsp");
		
		
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		this.doGet(request, response);
	
	}

}

结束语

天晴的时候,记得给自己储备一点阳光。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值