/*
*事物
*概念:就是一件事情,组成这件事情可能有多个单元,要求这些单元要么全都成功,要么全都不成功
在开发中有事物的存在,可以保证数据的完整性
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();
这句代码会在指定的目录下查找指定名称的配置文件,并将其中内容加载
*/
笔记--事物和连接池
最新推荐文章于 2022-07-26 20:43:13 发布