笔记--事物和连接池

来源:互联网 发布:骷髅陷阱升级数据 编辑:程序博客网 时间:2024/05/29 04:05
/**事物*概念:就是一件事情,组成这件事情可能有多个单元,要求这些单元要么全都成功,要么全都不成功   在开发中有事物的存在,可以保证数据的完整性   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 readoracle中默认事务隔离级别: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.jarcommons-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();这句代码会在指定的目录下查找指定名称的配置文件,并将其中内容加载*/
原创粉丝点击