事务、数据库连接池

事务

指的是一组操作,里面包含许多个单一的逻辑,如果都成功了,就执行提交(commit)只要有一个逻辑没有执行成功,那么都算失败,所有的数据都回归到最初的状态(回滚rollback)

为什么要有事务

为了确保逻辑的成功


使用代码方式演示事务

代码里面的事务,主要是针对连接来的,通过conn,setAutoCommit(false)来关闭自动提交的设置

public void testTransaction(){
	Connection conn = null;
	PreparedStatement ps = null;
	ResultSet rs = null;
	try{
		conn = JDBCUtil.getConn();
		
		//连接,事务默认就是自动提交的,关闭自动提交
		conn.setAutoCommit(false);

		String sql = "update account set money = money - ? where id = ?"
		ps = conn.prepareStatement(sql);

		//扣钱
		ps.setInt(1,100);
		ps.setInt(2,1);
		ps.executeUpdate();

		//此处为演示数据库错误
		int a = 10/0

		//加钱,给ID为2 加100块钱
		ps.setInt(1,-100);
		ps.setInt(2,2);
		ps.executeUpdate();

		
	}catch(SQLException e){
		try{
			conn.rollback();
		}catch(SQLException e1){
			e1.printStackTrace();
		}
		e1.printStackTrace();
	}finally{
		JDBCUtil.release(conn,ps,rs);
	}
}

事务的特性ACID

1.原子性

指的是事务中包含的逻辑不可分割

2.一致性

指的是事务执行前后,数据完整性

3.隔离性

指的是事务在执行期间不应该受到其他事务的影响

4.持久性

指的是事务执行成功,那么数据应该持久保存到磁盘上

事务的安全隐患

读:
1.脏读

一个事务读到另外一个事务还未提交的数据
A,B两个窗口,当A窗口的隔离级别为Read Uncommitted(读未提交),B窗口正在执行更新,但是还未提交,此时A窗口已经进行查询

解决办法:

设置A窗口的隔离级别为读已提交(这个隔离级别能够屏蔽脏读的现象,但是引发另一个问题,不可重复读
A,B两个窗口都开启事务,在B窗口执行更新操作
在A窗口执行的两次查询结果不一致,一次是在B窗口提交事务之前,一次是在B窗口提交事务之后


2.不可重复读

一个事务读到了另外一个事务提交的数据,造成了前后两次查询结果不一致


3.幻读

一个事务读到了另一个事务已提交的插入的数据,导致多次查询结果不一致


如果想把以上问题都解决,那么要用到最高的隔离级别:Serializable(可串行化)

set session transaction isolayion level serializable;

如果有一个连接的隔离级别设置了串行化,那么谁先打开了事务,谁就有了先执行的权利,后来打开事务的只能等前面的事务提交或者回滚才能执行。但是这种隔离级别一般比较少用,容易造成性能上的问题,效率比较低

按效率分,隔离级别从高到低:
读未提交>读可提交>可重复读>可串行化

按拦截程度分,隔离级别从高到低
可串行化>可重复读>读已提交>读未提交

写:
丢失更新

A和B同时开启事务,A先提交更新,B后提交更新,B的更新会冲掉A的更新

解决方法:
1.悲观锁(排他锁)

select * from account for update
A和B同时开启事务,如果A先查询,此时B界面会卡住,无法查询,要等到A提交或者回滚,B才可以进行更新数据

2.乐观锁
想要实现需要程序员自己写代码

select * from account
A和B同时开启事务,A事务先提交,数据库version变为1,此时B事务提交的时候,要比对数据库的version和自己的version,如果不一样,不允许提交


事务总结

1.常用代码

conn.setAutoCommit(false);

conn.commit();
conn.rollback();

2.事务只是针对连接对象,如果再开一个连接对象,那么默认提交
3.事务是会自动提交的
4.安全隐患有读和写两方面,读会产生脏读、不可重复读、幻读等隐患,写会产生丢失更新的隐患
5.隔离级别有四种,读未提交引发脏读),读已提交(解决脏读,引发不可重复读),可重复读(解决脏读、不可重复读,未解决的是幻读),可串行化(解决:脏读、不可重复读、幻读)
6.mySql默认的隔离级别是可重复读,Oracle默认的隔离级别是读已提交

数据库连接池

一开始先在内存中开辟一块空间(集合),先往池子里面放置多个连接对象,后面需要连接的话,直接拿池子中的练级对象,不要自己创建连接对象,使用完毕要记得归还连接,确保对象能循环利用

数据库连接池的简单搭建
/*
 *1.开始创建10个连接
 *2.来的程序通过getConnection获取连接
 *3.用完归还
 *4.扩容
 */
puublic class MyDataSource implements DataSource{
	public MyDataSource(){
		for(int i=0;i<10;i++){
			//JDBCUtil是自己写的jdbc工具类
			Connection conn = JDBCUtil.getConnection();
			list.add(conn);
		}
	}
	public Connection getConnection()throws SQLException{
		//检查池子里面还有没有连接
		if(list.size()==0){
			for(int i=0;i<5;i++){
			//JDBCUtil是自己写的jdbc工具类
			Connection conn = JDBCUtil.getConnection();
			list.add(conn);
		}
		Connection conn = list.remove(0);
		return conn;
	}

	//归还
	public void addBack(Connection conn){
		list.add(conn);
	}
}
数据库连接池的简单使用
public void testPool(){
	Connection conn = null;
	PreparedStatement ps = null;
	MyDataSource dataSource = new MyDataSource();
	try{
		conn = dataSource.getConnection();

		String sql = "insert into account values(null,"xxx",10)";
		ps = con.prepareStatement(sql);
		ps.executeUpdate();
	}catch(SQLException e){
		e.printStackTrace();
	}finally{
		try{
			ps.close();
		}catch(SQLException e){
			e.printStackTrace();
		}
	}

}

自定义数据库连接池的缺点

以上代码其实是我们自己写的一个数据库连接池,它实现了一个addBack()方法,但是这个方法DataSource接口中并没有定义,如果要用这个方法还得另外去记。
所以,这种自定义数据库的写法的缺点是无法面向接口编程。

面向接口编程:
在这里插入图片描述

解决方法:
装饰者模式----面向接口编程
写一个ConnectionWrap.class

public class ConnectionWrap implements Connection{
	Connection connection = null;
	List<Connection> list;
	public ConnectionWrap(Connection connection,List<Connection> list){
		super();
		this.connection = connection;
		this.list = list
	}

	//重写close()方法
	public void close() throws SQLException{
		connection.close();
	}
}

修改自定义的MyDataSource.class

puublic class MyDataSource implements DataSource{
	public MyDataSource(){
		for(int i=0;i<10;i++){
			//JDBCUtil是自己写的jdbc工具类
			Connection conn = JDBCUtil.getConnection();
			list.add(conn);
		}
	}
	public Connection getConnection()throws SQLException{
		//检查池子里面还有没有连接
		if(list.size()==0){
			for(int i=0;i<5;i++){
			//JDBCUtil是自己写的jdbc工具类
			Connection conn = JDBCUtil.getConnection();
			list.add(conn);
		}
		Connection conn = list.remove(0);
		
		//把这个对象抛出去的时候,对这个对象进行包装
		Connection connection = New ConnectionWrap(conn,list);
		return connection;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值