JDBC高级开发事务

JDBC高级开发事务

1 事务管理

1.1 概述:

​ 事务指的是逻辑上的一组操作,组成这组操作的各个单元要么全都成功,要么全都失败.

​ 事务作用:保证在一个事务中多次操作要么全都成功,要么全都失败.

事务特性(ACID):

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

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

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

​ 持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发 生故障也不应该对其有任何影响。

1.2 mysql事务操作

sql语句描述
start transaction;开启事务
commit;提交事务
rollback;回滚事务

MYSQL中可以有两种方式进行事务的管理:

​ 自动提交:MySql默认自动提交。及执行一条sql语句提交一次事务。

​ 手动提交(两种方式):先开启,再提交

​ 方式1:手动提交

start transaction;

update account set money=money-1000 where name='jack';

update account set money=money+1000 where name='rose';

commit;

--回滚

rollback;

​ 方式2:自动提交,通过修改mysql全局变量“autocommit”进行控制

--查看事务
show variables like '%commit%';

-- 设置自动提交的参数为OFF: 0:OFF  1:ON
set autocommit = 0; 

扩展:Oracle数据库事务不自动提交

1.3 JDBC事务操作

Connection对象的方法名描述
conn.setAutoCommit(false)开启事务
conn.commit()提交事务
conn.rollback()回滚事务

1.4 DBUtils事务操作

Connection对象的方法名描述
conn.setAutoCommit(false)开启事务
new QueryRunner()创建核心类,不设置数据源(手动管理连接)
query(conn , sql , handler, params ) 或update(conn, sql , params)手动传递连接
DbUtils.commitAndClose(conn) 或DbUtils.rollbackAndClose(conn)提交并关闭连接回滚并关闭连接

1.5 分层思想

开发中,常使用分层思想

​ 不同的层次结构分配不同的解决过程,各个层次间组成严密的封闭系统

​ 不同层级结构彼此平等

分层的目的:解耦、可维护性、可扩展性、可重用性

不同层次,使用不同的包表示

com.itheima   	公司域名倒写

com.itheima.dao		dao层 (Data access Object)

com.itheima.service	service层

com.itheima.domain	javabean

com.itheima.utils		工具

1.6 ThreadLocal类

​ java.lang.ThreadLocal 该类提供了线程局部 (thread-local) 变量,用于在当前线程中共享数据。

​ 如图:
在这里插入图片描述

2. 转账案例分层代码实现(事务)

2.1 web层:

package com01.itheima.web;

import java.util.Scanner;

import com02.itheima.service.AccountService;

/*
 * Web层:
 *  1:获取用户输入的数据
 *  2:调用Service层方法,并传入用户输入的数据
 */
public class AccountWeb {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		//1:获取用户输入的数据
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入源账户名:");
		String fromName = sc.nextLine();
		
		System.out.println("请输入目标账户名:");
		String toName = sc.nextLine();
		
		System.out.println("请输入转账金额:");
		double money = Double.parseDouble(sc.nextLine());
		
		//2:调用Service层方法,并传入用户输入的数据
		AccountService service = new AccountService();
		boolean bl = service.transAccount(fromName,toName,money);
		if(bl){
			System.out.println("转账成功!");
		}else{
			System.out.println("转账失败!");
		}
		
	}

}

2.2 service层:

package com02.itheima.service;

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

import com03.itheima.dao.AccountDao;
import com04.itheima.utils.ConnectionManager;
import com04.itheima.utils.MyC3P0Utils;

public class AccountService {
	public boolean transAccount(String fromName, String toName, double money){
		try {
			AccountDao dao = new AccountDao();
			//开启事务
			ConnectionManager.begin();
			//判断余额
			 double findMoney = dao.findMoney(fromName);
			 if(findMoney >= money){
				 int i = dao.fromAccount(fromName, money);
				 int j = dao.toAccount(toName, money);
				 if(i > 0 && j > 0){
					 //t提交事务
					 ConnectionManager.commit();
					 return true;
				 }
			 }else{
				 System.out.println("余额不足");
			 }
		} catch (Exception e) {
			try {
				//回滚事务
				ConnectionManager.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}
		return false;
	}
}

2.3 dao层:

package com03.itheima.dao;

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

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import com04.itheima.utils.ConnectionManager;
import com04.itheima.utils.MyC3P0Utils;

/*
 * 优化的目的:
 *  1:不让Service层给Dao层传Connection对象,让Dao层自己去获取Connection对象
 *  2:必须保证Dao层Connection和Serivice的Connection是同一个
 */
public class AccountDao {
	public int fromAccount(String fromName, double money) throws SQLException{
		QueryRunner qr = new QueryRunner();
		int i = qr.update(ConnectionManager.getConnection(),"update account set money = money - ? where aname = ?",money,fromName);
		return i;
	}
	
	public int toAccount( String toName, double money) throws SQLException{
		QueryRunner qr = new QueryRunner();
		int i = qr.update(ConnectionManager.getConnection(),"update account set money = money + ? where aname = ?",money,toName);
		return i;
	}
	
	public double findMoney(String fromName) throws SQLException{
		QueryRunner qr = new QueryRunner();
		double money  = (double)qr.query(ConnectionManager.getConnection(),"select money from account where aname = ?", new ScalarHandler(),fromName);
		return money;
	}
	
}

2.4 Utils层:

MyC3P0Utils:
package com04.itheima.utils;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/*
 * C3P0连接池的工具类:
 *  创建连接池:
 *  1:准备一个配置文件:xxx.xml文件 (写四大信息)
 *  2:创建CombopooledDataSource类对象
 */

public class MyC3P0Utils {
	/*
	 * 1:在bin目录中找到xml文件,解析xml文件
	 * 2:获取四大信息:
	 *   1)注册驱动
	 *   2)创建一个连接池,并存放连接15个连接
	 */
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
	
	//封装一个方法,对外提供dataSource
	public static DataSource getDataSource(){
		return dataSource;
	}
	//封装一个方法,让别人从连接池中获取一个连接
	public static Connection getConnection() throws SQLException{
		return dataSource.getConnection();
	}
}

连接管理器:ConnectionManager:

package com04.itheima.utils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;

import org.apache.commons.dbutils.DbUtils;

public class ConnectionManager {
	// 定义一个集合,这个集合是存Connection对象
	// private static HashMap<Thread, Connection> map= new HashMap<>();
	private static ThreadLocal<Connection> tl = new ThreadLocal<>();

	public static Connection getConnection() throws SQLException {
		// 第一个调用该 方法,集合中内容是空的
		// Connection conn = map.get(Thread.currentThread());
		Connection conn = tl.get();

		if (conn == null) {
			// 如果集合中没有连接,则获取一个连接存入Map
			conn = MyC3P0Utils.getConnection();
			// map.put(Thread.currentThread(), conn);
			tl.set(conn);
		}
		return conn;
	}

	// 开启事务
	public static void begin() throws SQLException {
		ConnectionManager.getConnection().setAutoCommit(false);
	}

	// 提交事务
	public static void commit() throws SQLException {
		DbUtils.commitAndClose(ConnectionManager.getConnection());
	}

	// 回滚事务
	public static void rollback() throws SQLException {
		DbUtils.rollbackAndClose(ConnectionManager.getConnection());
	}
}

3. 事务总结:

3.1 并发访问问题

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

  1. 脏读:一个事务读到了另一个事务未提交的数据.

  2. 不可重复读:一个事务读到了另一个事务已经提交(update)的数据。引发另一个事务,在事务中的多次查询结果不一致。

  3. 虚读 /幻读:一个事务读到了另一个事务已经提交(insert)的数据。导致另一个事务,在事务中多次查询的结果不一致。

3.2 隔离级别:解决问题

l数据库规范规定了4种隔离级别,分别用于描述两个事务并发的所有情况。

  1. read uncommitted 读未提交,一个事务读到另一个事务没有提交的数据。

    ​ a) 存在:3个问题(脏读、不可重复读、虚读)。

    ​ b) 解决:0个问题

  2. read committed 读已提交,一个事务读到另一个事务已经提交的数据。

    ​ a) 存在:2个问题(不可重复读、虚读)。

    ​ b) 解决:1个问题(脏读)

  3. repeatable read :可重复读,在一个事务中读到的数据始终保持一致,无论另一个事务是否提交。

    ​ a) 存在:1个问题(虚读)。

    ​ b) 解决:2个问题(脏读、不可重复读)

  4. serializable 串行化,同时只能执行一个事务,相当于事务中的单线程。

    a) 存在:0个问题。

    b) 解决:3个问题(脏读、不可重复读、虚读)

安全和性能对比

​ 安全性:serializable > repeatable read > read committed > read uncommitted

​ 性能 : serializable < repeatable read < read committed < read uncommitted

常见数据库的默认隔离级别:

​ MySql:repeatable read

​ Oracle:read committed

3.3 演示

  1. 查询数据库的隔离级别
show variables like '%isolation%';

或

select @@tx_isolation;
  1. 设置数据库的隔离级别
 set session transaction isolation level 级别字符串

级别字符串:read uncommittedread committedrepeatable readserializable

例如:set session transaction isolation level read uncommitted;

数据库的默认隔离级别:

​ MySql:repeatable read

​ Oracle:read committed

3.3 演示

  1. 查询数据库的隔离级别
show variables like '%isolation%';

或

select @@tx_isolation;
  1. 设置数据库的隔离级别
 set session transaction isolation level 级别字符串

级别字符串:read uncommittedread committedrepeatable readserializable

例如:set session transaction isolation level read uncommitted;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐_僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值