WEB23_数据库事务

项目结构图:主要是模拟银行转账

我们需要使用bank.jsp作为显示页面,在web中与jsp进行交互,使用service进行复杂业务处理,使用dao进行数据库操作

 

事务

什么是事务?

简单理解就是,我执行一系列的sql语句,要么全部执行成功,要么全部执行失败.

JDBC事务操作

自动提交

executeUpdate(),executeQuery()

我们一般执行语句的时候,都是一条sql语句.而这其实也是一个事务,会默认的自动提交

我们这里主要学习手动事务操作的API:

开启事务:Connection conn.setAutoCommit(false);

提交事务:conn.commit();

回滚事务:conn.rollback();

注意:执行sql语句的connection与开启事务的connection必须是同一个.

DBUtil对事务的使用

主要讲一下DBUtil的核心类QueryRunner

QueryRunner有有参构造与无参构造.

我们如果要手动开启事务的话就应该使用无参构造了!

当我们使用有参构造时会传一个DataSource来自己从连接池中获得连接,而获得的连接是哪一个就不好说了.

当我们使用无参构造时,我们不传参数因此在使用数据库操作方法时,需要手动指定使用那个Connection.比如这样

int result = qr.update(conn, sql,money,roolAcc);

开启事务,作为一个复杂的业务,我们要放在service中处理,而只有使用Connection对象才能开启事务,所以我们在service层拿到Connection对象开启事务,为了能让dao层使用与开启事务时同一个Connection对象,因此我们需要把Connection对象传到dao层.因此service层代码就成了这样

public class BankService2 {
	private BankDao bd;
	public BankService2() {
		bd = new BankDao();
	}

	/**
	 * service层处理复杂事务
	 * @param roolAcc
	 * @param intoAcc
	 * @param money
	 * @return
	 */
	public boolean transfer(String roolAcc, String intoAcc, double money) {
		Connection conn = BankUtil.getConnection();
		boolean b = true;
		//开启事务
		try {
			conn.setAutoCommit(false);
			if(bd.out(conn,roolAcc, money)<0||bd.in(conn,intoAcc, money)<0){
				b=false;
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			//回滚
			try {
				b = false;
				conn.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		} finally{
			try {
				conn.commit();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return false;
	}
}

ThreadLocal的使用

从上边的代码看,我们对于JavaEE的三层架构理解就不到位了.Connection作为dao层的对象,其实是不应该出现在service层的.而我们的service层又需要进行事务的开启.因此这里就需要使用到ThreadLocal的知识了.

ThreadLocal可以理解为是一个线程中的一个map集合.

我们将数据存储到Thead中后便可以在该线程其他位置取到数据了.这样同时也满足了service与dao需要使用一个Connection的要求.

我们只需要在util包中封装好开启事务,回滚事务,提交事务的方法,并将一个连接封装到ThreadLocal中就可以了

类似这样

/**
 * 
 */
package com.yl.transfer.util;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * @author YL
 * @date 2019年3月17日
 * @version  
 * @message 
 */
public class BankUtil {
	//核心类
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
	//存储开启事务的Connection
	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
	//获取DataSource
	public static DataSource getDataSource(){
		return dataSource;
	}
	//获取连接
	public static Connection getConnection() {
		Connection conn = null;
		if(conn == null){
			try {
				conn = dataSource.getConnection();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return conn;
	}
	//获得Current连接
	public static Connection getCurrentConnection() {
		Connection conn = tl.get();
		if(conn==null){
			conn = getConnection();
			tl.set(conn);
		}
		return conn;
	}
	//开始事务
	public static void startTransaction() throws SQLException{
		Connection conn = getCurrentConnection();
		conn.setAutoCommit(false);
	}
	//提交事务
	public static void commitTransaction() throws SQLException {
		Connection conn = getCurrentConnection();
		conn.commit();
	}
	//回滚事务
	public static void rollbackTransaction() throws SQLException {
		Connection conn = getCurrentConnection();
		conn.rollback();
	}
}

而我们的service层的代码也就可以和dao层解耦了

/**
 * 
 */
package com.yl.transfer.service;

import java.sql.SQLException;

import com.yl.transfer.dao.BankDao;
import com.yl.transfer.util.BankUtil;

/**
 * @author YL
 * @date 2019年3月17日
 * @version  
 * @message 
 */
public class BankService {
	private BankDao bd;
	public BankService() {
		bd = new BankDao();
	}

	/**
	 * service层处理复杂事务
	 * @param roolAcc
	 * @param intoAcc
	 * @param money
	 * @return
	 */
	public boolean transfer(String roolAcc, String intoAcc, double money) {
		// TODO Auto-generated method stub
		boolean result = true;
		try {
			//开事务
			BankUtil.startTransaction();
			//数据库操作失败
			if(bd.out(roolAcc,money)<0||bd.in(intoAcc,money)<0){
				result = false;
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			result=false;
			try {
				//回滚
				BankUtil.rollbackTransaction();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		} finally{
			try {
				//提交
				BankUtil.commitTransaction();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return result;
	}

}

有了上面的代码铺垫.就可以更好的理解数据库的事务特性与隔离级别了

数据库事务特性

事务的特性ACID

原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作 要么都发生,要么都不发生。 转账要不然就A钱减少,B钱增加,不然就操作失败,A钱不减,B钱不加.

一致性(Consistency)一个事务中,事务前后数据的完整性必须保持一致。不可以出现A钱减少了,B钱没有增加!

隔离性(Isolation)多个事务,事务的隔离性是指多个用户并发访问数据库时, 一个用户的 事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。A这边还没有提交,B这边提交了,A不会被干扰

持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变 就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响.A提交后,数据库出现异常了,A的数据不受干扰.

并发访问问题--由事务的隔离性引发

1.脏读:B事务根据需求,要读取A的数据,而却可以读取到A事务未提交的数据

2.不可重复读:在一次事务中,前后读取的两次数据不同.就是第一次读money是100,然后另一个事务更改了money=2000,本事务再次读取时,money=2000了!

3.幻读/虚读:在一次事务中,前后读取的两次数据的量不同.就是第一次读是一行数据,然后另一个事务insert了一行数据,本事务再次读取时,数据变成了两行!

事务的隔离级别

1.read uncommitted:读取尚未提交的数据---效率高,不安全

2.read committed:读取已提交的数据---解决脏读---oracle默认级别

3.repeatable read :重复读取---解决不可重复读与脏读---mysql默认级别

4.serializable:串行化---解决脏读,不可重复读,幻读\虚读---效率极低,安全极高,就是synchronized的意思.

查看数据库默认隔离级别:select @@tx_isolation

设置数据库默认隔离级别:set session transaction isolation level 要设置的事务隔离级别

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值