笔记--事物和连接池

/*
*事物
	*概念:就是一件事情,组成这件事情可能有多个单元,要求这些单元要么全都成功,要么全都不成功
		   在开发中有事物的存在,可以保证数据的完整性
		   eg:银行的转账,如果你的钱取出来了,但是对方账户出问题了,不能转入
		       这时候必须两边都要失败,不然就会出现问题。
	*操作:
		*mysql下怎样操作
			*1)*start transaction  开启事物
					*开启事物之后执行的sql语句
					 如果没有进行事物的提交,都只是在表面上改变了,其实内存中是没变的
					 即,事物相当于一个缓冲区,之前都是在缓存区中
					 没有提交,就没有真正的刷新到内存,关闭当前窗口再打开,是没有变化的
				*rollback           事物回滚
				*commit             事物提交
				
				*其实,就算没有显示的开启事物,执行sql语句,都会有事物的
			*1)show variable like '%commit%' ; 查看当前autocommit值
				*在MySQL数据库中,默认值是on,代表自动事物
				 自动事物:执行任意一条sql语句都会自动提交事物
				*如果将autocommit设置为off,则关闭了自动事物
				 此时必须手动提交事物,不然sql执行后,是没有提交到数据库内部的
				 mysql数据库是on的,oracle中的autocommit就是off的
				 
		*jdbc下怎样操作
			*java.sql.Connection接口下有几个方法是用于操作事物的
				*setAutoCommit(boolean flag):   开启事物
				*rollback():                    事物回滚   回滚就相当于回到执行之前的状态,就是没有执行
					*这个方法应该放在catch语句块里面
					 因为捕获异常了,说明事物不成功,所以回滚
				*commit():                      事物提交
				
	*作用:
	*特性:
		*原子性:事务是不可分割的工作单位,事物中的操作要么都发发生,要么都不发生
			*Atomicity
		*一致性:事务执行前后,数据完整性必须保持一致
			*Consistency
		*隔离性:多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离
			*Isolations
		*持久性:事务一旦被提交,它对数据库中数据的改变就是永久的,接下来即使数据库发生故障,也不应该对其有任何影响
			*Durability
			
		****如果不考虑事务的隔离性,会出现的问题(昨天笔试考了,我今天才看,也是666,不过加深了印象,还可以)
			***脏读:一个事务读取到了另一个事务未提交的数据
			***不可重复读:在一个事务中,两次读取数据不一致(update),即单条或多条数据发生改变
			***虚读(幻读):两次读取的数据不一致(insert),数据的条目数发生改变
			***丢失更新:两个事务对同一条记录进行操作,后提交的事务将先提交的事务的修改覆盖了
		**解决:设置事务的隔离级别
			*事务的隔离级别:
				*Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)
				*Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读
				*Read committed:可避免脏读情况发生(读已提交)
				*Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

			*怎样设置事务的隔离级别
				*在mysql中设置
					*查看事务隔离级别
						select @@tx_isolation   查询的当前事务隔离级别
						mysql中默认事务隔离级别:Repeatable read
						oracle中默认事务隔离级别:Read uncommitted
					*设置隔离级别:set session transaction isolation level 级别
				*在jdbc中设置
					*java.sql.Connection接口中提供设置事务隔离级别的方法
						*void setTransactionIsolation(int level) throws SQLException
						参数level可以取以下值:
						level - 以下 Connection 常量之一:
						Connection.TRANSACTION_READ_UNCOMMITTED、
						Connection.TRANSACTION_READ_COMMITTED、
						Connection.TRANSACTION_REPEATABLE_READ 
						Connection.TRANSACTION_SERIALIZABLE。
						(注意,不能使用 Connection.TRANSACTION_NONE,因为它指定了不受支持的事务。) 

		*演示脏读:
			*设置A,B事务隔离级别为 Read uncommitted(即什么都不能解决的级别)
			*在A事务中开启事务,然后更新数据
			*在B事务中开启事务,查询数据,就会脏读
			 但是如果没开启事务直接查询,是不会查询到的。
			*A事务commit之前,执行rollback,B事务再次查询,就会发现数据又恢复到了A修改之前的状态
		*解决脏读:
			*将事务隔离级别设置为 Read committed 
		*解决不可重复读:将事务隔离级别设置为 Repeatable read来解决不可重复读
			*之前一直困扰,为什么两次不能读到不同的数据
			 后来听了才知道,是在一次事务中,如果一次事务的时间很长,你刚开始的时候查询是一个结果
			 过了一会儿之后去查,有其他人更新了数据,但是你要的是之前的数据,
			 你再去查,就是别人更新后的数据,这样肯定会出现问题
			 哎,是我自己没有理解好
			 在两次事务中,查询结果不一致,是肯定可以的,事务隔离都是对同一个事务的限制
		*设置事务隔离级别  Serializable  可以解决所有问题
			*设置为这个级别的事务,会锁表
			 我操作数据期间,任何其他事务都不能操作当前数据
			*显然,对于系统性能有很大影响,一个事务操作,其他事务全排队等待,慎用
			 只有对数据有严格要求的,才考虑使用
			 
		*总结:
			*脏读:一个事务读取到另一个事务未提交的数据
			*不可重复读:两次读取数据不一致  update 
			*虚读:两次读取数据不一致  insert
			
			*事务隔离级别 
				*read uncommitted 什么也解决不了
				*read committed 只可以解决脏读
				*repeatable read 可以解决脏读,不可重复读,不能解决虚读
				*serializable  可以解决所有问题,锁表
				 一般用中间那两个
				 安全性:serializable > repeatable read > read committed > read uncommitted 
				 性能 :serializable < repeatable read < read committed < read uncommitted 
				 实际开发中,通常不会选择 serializable 和 read uncommitted ,
  				 mysql默认隔离级别 repeatable read ,oracle默认隔离级别 read committed
	*案例:转账汇款
		*service调用了dao中两个方法完成了一个业务操作,如果其中一个方法执行失败怎样办?
		 需要事务控制
		*怎样进行事务控制?
		 我们在service层进行事务的开启,回滚以及提交操作。		
		*进行事务操作需要使用Connection对象,那么,怎样保证,在service中与dao中所使用的是同一个Connection.
		 在service层创建出Connection对象,将这个对象传递到dao层.
		*Connecton对象使用完成后,在service层的finally中关闭	
		 而每一个PreparedStatement它们在dao层的方法中用完就关闭.	 
		*关于程序问题
			*对于转入与转出操作,我们需要判断是否成功
			 因为如果转账的时候,如果有账户信息出现问题,没问题的账户会成功,那么转账这个过程就是有问题的
			 所以,如果失败了,可以通过抛出自定义异常在servlet中判断,
			 进行信息展示
		*ThreadLocal:可以理解成一个map集合
		 Map
   
   
    
    
		 set方法是向ThreadLocal中存储数据,那么当前key值就是当前线程对象
		 get方法是从ThreadLocal中获取数据,它是根据当前线程对象来获取值
		 
		 如果是在同一个线程中,只要在任意的一个位置存储了数据,在其他位置上,就可以获取到这个数据

**ThreadLocal:java.lang.ThreadLocal提供的,为解决多线程程序的并发问题提供的一种新思路
			   ThreadLocal并不是一个线程,而是线程的局部变量
			   *方法:void set(value)
					  void remove():删除当前线程局部变量的值,减少内存的占用
									 虽然自动回收机制会清楚线程,但是这个方法可以加快回收的速度
					  protected Object initialValue():返回该线程局部变量的初始值
		 
		 
	*丢失更新:多个事务对同一条记录进行操作,后提交的事务将先提交的事务的修改覆盖了
		*解决:
			*悲观锁    假设丢失更新一定会发生,利用数据库内部锁机制,管理事务
				*共享锁
					select * from table lock in share mode(读锁、共享锁)
				*排他锁
					select * from table for update (写锁、排它锁)
					update语句默认添加排他锁
			*乐观锁    假设丢失更新不会发生,采用程序总添加字段解决丢失更新
		
*连接池
	*概念
		*创建和管理一个连接的缓冲池技术
		*数据库连接池:以前用户每次请求都需要向数据库获得链接,而数据库创建连接需要消耗较大的资源,创建时间也较长
					   如果一个网站每天访问量很大,就需要创建很多连接,极大的浪得数据库资源,并极易造成数据库服务器内存溢出,拓机
					   
					   笔试题:什么叫数据库连接池(或什么叫数据源)
							*连接池:创建一个容器,用于装入多个connection对象
									 在使用连接对象时,从容器中获取一个connection
									 使用完成后,再将这个connection重新装入容器中,这个容器对象就是连接池(data source)
									 也叫数据源
								*作用:通过连接池获取连接对象
								*优点:提高效率  节省创建连接或释放连接--性能释放
									   可以重复利用connection对象
									 
	*自定义连接池
		1.创建一个MyDataSource类,在这个类中创建一个LinkedList
    
    
     
     
			*为啥用linkedList呢?因为它适合频繁的存取操作
			
**linkedlist和arraylist的区别		
	*ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表的数据结构
	*对于随机访问get和set操作,ArrayList优于LinkedList,因为LinkedList需要移动指针
	*对于增删操作add和remove,LinkedList优于ArrayList,因为ArrayList要移动数据
	
	*时间复杂度:ArrayList的时间要远远小于LinkedList的时间。
	*空间复杂度:ArrayList的空间消耗要远远大于LinkedList
				 ArrayList的空间浪费主要体现在list列表的结尾预留一定的容量空间
			
		2.在其构造方法中初始化List集合,并向其中装入5个Connection对象。
		3.创建一个public Connection getConnection();从List集合中获取一个连接对象返回.
		4.创建一个  public void readd(Connection) 这个方法是将使用完成后的Connection对象重新装入到List集合中.
		*我们上面自定义的连接池,其实并不是真正的连接池
		 因为连接池是一个标准,我们简单定义的那个,并没有任何标准而言。
		 
		 由API可知,所有连接池都必须实现javax.sql.DataSource接口
		 自定义的连接池就需要实现接口的方法
		 DataSource接口里面只有两个方法,但是实现那个类以后还有其他很多的方法
		 这是因为DataSource接口还有父接口,父接口还有方法,也需要去实现
		 
		 关于connection对象关不关这个问题,以前我们是必须要关的
		 但是现在有了连接池,我们希望的是不把那个connection关闭,而是放回池中
		 但是要怎样实现呢?即conn.close()不是销毁连接,而是将连接放回池中
		 本质:改变close()的行为(对方法功能进行增强)
			*继承(不靠谱,会导致代码依赖jar包)
			*装饰模式(不靠谱,继承connection接口要实现的方法太多,麻烦)
				*1)装饰类于被装饰类要实现同一个接口或继承同一个父类
				*2)在装饰类中持有一个被装饰类的引用
				*3)对方法进行增强
			*动态代理
				*Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);
				 对行为进行增强
			
			*结论:connection对象如果是从连接池中获取到的,那么它的close方法的行为已经改变了
			       不再是销毁,而是重新装会连接池
		 
		 
	*开源连接池
		*dbcp(了解)
			*没有自动回收空闲连接的功能
			*Apache的一个开源连接池,免费。说到Apache,就需要导包
			*使用:
				*导入两个jar包:commons-dbcp-1.4.jar
								commons-pool-1.5.6.jar
				*配置:
					*手动:手动编码
						BasicDataSource bds = new BasicDataSource();

						// 需要设置连接数据库最基本四个条件,因为只靠上面那一步,系统是不知道去连接哪个数据库的
						bds.setDriverClassName("com.mysql.jdbc.Driver");
						bds.setUrl("jdbc:mysql:///day18");
						bds.setUsername("root");
						bds.setPassword("abc");

						// 获取Connection对象
						Connection con = bds.getConnection();
					*自动:使用配置文件
						Properties props = new Properties();
						FileInputStream fis = new FileInputStream("配置文件路径");
						props.load(fis);
						DataSource ds = BasicDataSourceFactory.createDataSource(props);
		*c3p0(必会)
			*有自动回收空闲连接的功能
			*开源jdbc连接池
			*使用:
				*导包:c3p0-0.9.1.2.jar
				*手动:ComboPooledDataSource cpds = new ComboPooledDataSource();
					   cpds.setDriverClass("com.mysql.jdbc.Driver");
					   cpds.setJdbcUrl("jdbc:mysql:///day18");
					   cpds.setUser("root");
					   cpds.setPassword("abc");
				 自动:c3p0的配置文件可以是properties也可以是xml
					   c3p0的配置文件如果名称叫做 c3p0.properties or c3p0-config.xml 并且放置在classpath路径下(对于web应用就是classes目录)
					   那么c3p0会自动查找。
					   *注意:其实我们只需要将配置文件放置在src下就可以了,它其余工作全部自动完成
						ComboPooledDataSource cpds = new ComboPooledDataSource();
						这句代码会在指定的目录下查找指定名称的配置文件,并将其中内容加载
*/
    
    
   
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值