事务

  • 事务概述

什么是事务

一件事情有n个组成单元 要不这n个组成单元同时成功 要不n个单元就同时失败

就是将n个组成单元放到一个事务中

mysql的事务

默认的事务:一条sql语句就是一个事务 默认就开启事务并提交事务

手动事务:

1)显示的开启一个事务:start transaction

2)事务提交:commit代表从开启事务到事务提交 中间的所有的sql都认为有效 真正的更新数据库

3)事务的回滚:rollback 代表事务的回滚 从开启事务到事务回滚 中间的所有的   sql操作都认为无效数据库没有被更新

  • JDBC事务操作

默认是自动事务:

执行sql语句:executeUpdate()  ---- 每执行一次executeUpdate方法 代表     事务自动提交

通过jdbc的API手动事务:

开启事务:conn.setAutoComnmit(false);

提交事务:conn.commit();

回滚事务:conn.rollback();

注意:控制事务的connnection必须是同一个

执行sql的connection与开启事务的connnection必须是同一个才能对事务进行控制

用JDBC操作:

package com.itheima.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo {

	public static void main(String[] args) {
		
		//通过jdbc去控制事务
		Connection conn = null;
		//1、注册驱动
		try {
			Class.forName("com.mysql.jdbc.Driver");
			//2、获得connection
			conn = DriverManager.getConnection("jdbc:mysql:///web19", "root", "root");
			
			//手动开启事务
			conn.setAutoCommit(false);
			
			//3、获得执行平台
			Statement stmt = conn.createStatement();
			
			//4、操作sql
			stmt.executeUpdate("update account set money=5000 where name='tom'");
			
			
			//提交事务
			conn.commit();
			
			stmt.close();
			conn.close();
		} catch (Exception e) {
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
		
	}
	
}
  • DBUtils事务操作

QueryRunner

有参构造:QueryRunner runner = new QueryRunner(DataSource dataSource);

有参构造将数据源(连接池)作为参数传入QueryRunner,QueryRunner会从连 接池中获得一个数据库连接资源操作数据库,所以直接使用无Connection参数  的update方法即可操作数据库

无参构造:QueryRunner runner = new QueryRunner();

无参的构造没有将数据源(连接池)作为参数传入QueryRunner,那么我们在使   用QueryRunner对象操作数据库时要使用有Connection参数的方法

  • 使用ThreadLocal绑定连接资源

采用线程进行业务分离,不需要键名

事务_第1张图片

用DButils:

如果不需要用事务时,只需获取连接池就行

package com.itheima.dbutils;

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

import org.apache.commons.dbutils.QueryRunner;

import com.itheima.utils.DataSourceUtils;

public class DBUtilsDemo {

	public static void main(String[] args) {
		
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			
			//runner.update("update account set money=15000 where name='tom'");
			//获得一个Connection,因为runner中没有开启事务的方法,只能从连接池中找到相对应的连接
			conn = DataSourceUtils.getConnection();
			
			//开启事务
			conn.setAutoCommit(false);
			
			runner.update(conn, "update account set money=15000 where name='tom'");
			//提交或回滚事务
			conn.commit();
			
		} catch (SQLException e) {
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}

	}
	
}


//封装DataSourceUtils.getConnection()方法
package com.itheima.utils;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class DataSourceUtils {

	private static DataSource dataSource = new ComboPooledDataSource();

	private static ThreadLocal tl = new ThreadLocal();

	// 直接可以获取一个连接池
	public static DataSource getDataSource() {
		return dataSource;
	}
	
	public static Connection getConnection() throws SQLException{
		return dataSource.getConnection();
	}

	// 获取连接对象
	public static Connection getCurrentConnection() throws SQLException {

		Connection con = tl.get();
		if (con == null) {
			con = dataSource.getConnection();
			tl.set(con);
		}
		return con;
	}

	// 开启事务
	public static void startTransaction() throws SQLException {
		Connection con = getCurrentConnection();
		if (con != null) {
			con.setAutoCommit(false);
		}
	}

	// 事务回滚
	public static void rollback() throws SQLException {
		Connection con = getCurrentConnection();
		if (con != null) {
			con.rollback();
		}
	}

	// 提交并且 关闭资源及从ThreadLocall中释放
	public static void commitAndRelease() throws SQLException {
		Connection con = getCurrentConnection();
		if (con != null) {
			con.commit(); // 事务提交
			con.close();// 关闭资源
			tl.remove();// 从线程绑定中移除
		}
	}

	// 关闭资源方法
	public static void closeConnection() throws SQLException {
		Connection con = getCurrentConnection();
		if (con != null) {
			con.close();
		}
	}

	public static void closeStatement(Statement st) throws SQLException {
		if (st != null) {
			st.close();
		}
	}

	public static void closeResultSet(ResultSet rs) throws SQLException {
		if (rs != null) {
			rs.close();
		}
	}

}

事务的特性和隔离级别(概念性问题---面试)

事务的特性ACID

1)原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 

2)一致性(Consistency)一个事务中,事务前后数据的完整性必须保持一致。

3)隔离性(Isolation)多个事务,事务的隔离性是指多个用户并发访问数据库时,一个用户的 事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

4)持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变

就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

 

并发访问问题----由隔离性引起

如果不考虑隔离性,事务存在3中并发访问问题。

  1. 脏读:B事务读取到了A事务尚未提交的数据   ------  要求B事务要读取A事     务提交的数据
  2. 不可重复读:一个事务中 两次读取的 数据的内容  不一致  ----- 要求的是一个事 务中多次读取时数据是一致的  --- unpdate
  3. 幻读/虚读:一个事务中 两次读取的 数据的数量  不一致  ----- 要求在一个事务多次读取的数据的数量是一致的 --insert  delete

事务的隔离级别

  1. read uncommitted : 读取尚未提交的数据 :哪个问题都不能解决
  2. read committed:读取已经提交的数据 :可以解决脏读 ---- oracle默认的
  3. repeatable read:重读读取:可以解决脏读 和 不可重复读 ---mysql默认的
  4. serializable:串行化:可以解决 脏读 不可重复读 和 虚读---相当于锁表

注意:mysql数据库默认的隔离级别

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

事务_第2张图片

设置mysql的隔离级别:set session transaction isolation level 设置事务隔离级别

事务_第3张图片

总结:

               mysql的事务控制:

                      开启事务:start transaction;

                      提交:commit;

                      回滚:rollback;

               JDBC事务控制:

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

                     提交:conn.commit();

                     回滚:conn.rollback();

                 DBUtils的事务控制 也是通过jdbc

                     

                 ThreadLocal:实现的是通过线程绑定的方式传递参数

 

                 概念:

                      事务的特性ACID

                      并发问题:脏读、不可重读、虚读\幻读

                      解决并发:设置隔离级别

                            read uncommitted

                            read committed

                            repeatable read (mysql默认)

                            serialazable

 

                      隔离级别的性能:

                            read uncommitted>read committed>repeatable read>serialazable

                      安全性:

                            read uncommitted

 

转账案例:

事务_第4张图片

//transfer.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




Insert title here


	
转出账户:
转入账户:
转账金额:

//web层 package com.itheima.transfer.web; 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.itheima.transfer.service.TransferService; public class TransferServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //接受转账的参数 String out = request.getParameter("out"); String in = request.getParameter("in"); String moneyStr = request.getParameter("money"); double money = Double.parseDouble(moneyStr); //调用业务层的转账方法 TransferService service = new TransferService(); boolean isTransferSuccess = service.transfer(out,in,money); response.setContentType("text/html;charset=UTF-8"); if(isTransferSuccess){ response.getWriter().write("转账成功!!!"); }else{ response.getWriter().write("转账失败!!!"); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } //service层 package com.itheima.transfer.service; import java.sql.Connection; import java.sql.SQLException; import com.itheima.transfer.dao.TransferDao; import com.itheima.utils.DataSourceUtils; import com.itheima.utils.MyDataSourceUtils; public class TransferService { public boolean transfer(String out, String in, double money) { TransferDao dao = new TransferDao(); boolean isTranferSuccess = true; //Connection conn = null; try { //开启事务 //conn = DataSourceUtils.getConnection(); //conn.setAutoCommit(false); //开启事务 MyDataSourceUtils.startTransaction(); //转出钱的方法 dao.out(out,money); //int i = 1/0; //转入钱的方法 dao.in(in,money); } catch (Exception e) { isTranferSuccess = false; //回滚事务 try { MyDataSourceUtils.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); } finally{ try { MyDataSourceUtils.commit(); } catch (SQLException e) { e.printStackTrace(); } } return isTranferSuccess; } } //dao层 package com.itheima.transfer.dao; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbutils.QueryRunner; import com.itheima.utils.DataSourceUtils; import com.itheima.utils.MyDataSourceUtils; public class TransferDao { public void out(String out, double money) throws SQLException { QueryRunner runner = new QueryRunner(); Connection conn = MyDataSourceUtils.getCurrentConnection(); String sql = "update account set money=money-? where name=?"; runner.update(conn, sql, money,out); } public void in(String in, double money) throws SQLException { QueryRunner runner = new QueryRunner(); Connection conn = MyDataSourceUtils.getCurrentConnection(); String sql = "update account set money=money+? where name=?"; runner.update(conn, sql, money,in); } } //MyDataSourceUtils package com.itheima.utils; import java.sql.Connection; import java.sql.SQLException; import com.mchange.v2.c3p0.ComboPooledDataSource; public class MyDataSourceUtils { //获得Connection ----- 从连接池中获取 private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); //创建ThreadLocal private static ThreadLocal tl = new ThreadLocal(); //开启事务 public static void startTransaction() throws SQLException{ Connection conn = getCurrentConnection(); conn.setAutoCommit(false); } //获得当前线程上绑定的conn public static Connection getCurrentConnection() throws SQLException{ //从ThreadLocal寻找 当前线程是否有对应Connection Connection conn = tl.get(); if(conn==null){ //获得新的connection conn = getConnection(); //将conn资源绑定到ThreadLocal(map)上 tl.set(conn); } return conn; } public static Connection getConnection() throws SQLException{ return dataSource.getConnection(); } //回滚事务 public static void rollback() throws SQLException { getCurrentConnection().rollback(); } //提交事务 public static void commit() throws SQLException { Connection conn = getCurrentConnection(); conn.commit(); //将Connection从ThreadLocal中移除 tl.remove(); conn.close(); } }

事务_第5张图片

事务_第6张图片

你可能感兴趣的:(Mysql,java)