jsp mysql事务锁_事务与连接池

事务与连接池

事务

事务介绍

l  事务的概念

事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功

l  数据库开启事务命令

start transaction  开启事务

Rollback  回滚事务

Commit   提交事务

Mysql中使用事务

1.创建表

create table account(

id int primary key auto_increment,

name varchar(20),

money double

);

insert into account values(null,'aaa',1000);

insert into account values(null,'bbb',1000);

insert into account values(null,'ccc',1000);

2、MySQL中事务默认自动提交的,每当执行一条SQL,就会提交一个事务 (一条SQL 就是一个事务)

Oracle 中事务默认 不自动提交,需要在执行SQL 语句后 通过commint 手动提交事务

3、mysql管理事务

方式一 :同时事务管理SQL 语句

start transaction  开启事务

rollback  回滚事务 (将数据恢复到事务开始时状态)

commit   提交事务 (对事务中进行操作,进行确认操作,事务在提交后,数据就不可恢复)

方式二:数据库中存在一个自动提交变量 ,通过 show variables like '%commit%'; ---- autocommint 值是 on,说明开启自动提交

关闭自动提交 set autocommit = off / set autocommit = 0

如果设置autocommit 为 off,意味着以后每条SQL 都会处于一个事务中,相当于每条SQL执行前 都执行 start transaction

补充:Oracle中 autocommit 默认就是 off

Jdbc使用事务

l  当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列语句:

l  JDBC控制事务语句

Connection.setAutoCommit(false); //  相当于start transaction

Connection.rollback();  rollback

Connection.commit();  commit

l  示例 演示银行转帐案例

l  在JDBC代码中使如下转帐操作在同一事务中执行。

update from account set money=money-100 where name=‘a’;

update from account set money=money+100 where name=‘b’;

l  设置事务回滚点

Savepoint sp = conn.setSavepoint();

Conn.rollback(sp);

Conn.commit();   //回滚后必须要提交

事务特性

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

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

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

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

事务隔离级别

l  多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

l  如果不考虑隔离性,可能会引发如下问题

1、脏读 :指一个事务读取另一个事务未提交的数据

A 转账 给B 100,未提交

B 查询账户多了100

A 回滚

B 查询账户那100不见了

2、不可重复读:在一个事务先后两次读取发生数据不一致情况,第二次读取到另一个事务已经提交数据 (强调数据更新 update)

A 查询账户 5000

B 向 A 账户转入 5000

A 查询账户 10000

3、虚读(幻读) : 在一个事务中,第二次读取发生数据记录数的不同 ,读取到另一个事务已经提交数据 (强调数据记录变化 insert )

A 第一次读取 存在5条记录

B 向 A 插入一条新的记录

A 第二次读取 存在6条记录

数据库内部定义了四种隔离级别,用于解决三种隔离问题

1 Serializable:可避免脏读、不可重复读、虚读情况的发生。(串行化)

2 Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)不可以避免虚读

3 Read committed:可避免脏读情况发生(读已提交)

4 Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

操作数据库内部隔离级别

set session transaction isolation level 设置事务隔离级别

select @@tx_isolation   查询当前事务隔离级别

实验一:演示脏读发生

在A窗口

将隔离级别设置 read uncommitted

A、B窗口同时开启事务

B窗口执行转账操作

update account set money = money - 500 where name='bbb';

update account set money = money + 500 where name ='aaa'; 未提交事务

A窗口查询 select *

from account; 查询到转账结果(脏读)

B 回滚 rollback

A窗口查询 金钱丢失

实验二:演示不可重复读

在A窗口设置

隔离级别 read committed; 重复实验一,发现无法脏读

B窗口转账后,提交事务

A窗口查询到 B提交的数据 (不可重复读)

实验三:演示阻止不可重复读发生

在A窗口

设置隔离级别 repeatable read 重复试验二,发现不会发生不可重复读

实验四:演示serializable 串行事务效果

A窗口设置隔离级别

serializable

A、B同时开启事务

B窗口插入一条数据

insert into account values(null,'ddd',1000);

在A窗口查询数据 select * from account;

发现A 窗口阻塞了,等待B事务执行结束

安全性:serializable > repeatable

read > read committed > read uncommitted

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

uncommitted

结论: 实际开发中,通常不会选择 serializable 和 read uncommitted , mysql默认隔离级别

repeatable read ,oracle默认隔离级别 read committed

JDBC程序中能否指定事务的隔离级别 ?

Connection接口中定义事务隔离级别四个常量:

static int TRANSACTION_READ_COMMITTED

指示不可以发生脏读的常量;不可重复读和虚读可以发生。

static int TRANSACTION_READ_UNCOMMITTED

指示可以发生脏读 (dirty read)、不可重复读和虚读 (phantom read) 的常量。

static int TRANSACTION_REPEATABLE_READ

指示不可以发生脏读和不可重复读的常量;虚读可以发生。

static int TRANSACTION_SERIALIZABLE

指示不可以发生脏读、不可重复读和虚读的常量。

通过 void setTransactionIsolation(int

level) 设置数据库隔离级别

事务的丢失更新问题(lost

update )

l  两个或多个事务更新同一行,但这些事务彼此之间都不知道其它事务进行的修改,因此第二个更改覆盖了第一个修改

l  丢失更新问题的解决

悲观锁( Pessimistic Locking )

select * from table lock

in share mode(读锁、共享锁)

select * from table for

update(写锁、排它锁)

乐观锁( Optimistic Locking )

通过时间戳字段

解决方案详解:

方案一:悲观锁 (假设丢失更新一定会发生 ) ----- 利用数据库内部锁机制,管理事务

方案二:乐观锁 (假设丢失更新不会发生)------- 采用程序中添加版本字段解决丢失更新问题

一、悲观锁

mysql数据库内部提供两种常用 锁机制:共享锁(读锁)和排它锁(写锁)

允许一张数据表中数据记录,添加多个共享锁,添加共享锁记录,对于其他事务可读不可写的

一张数据表中数据记录,只能添加一个排它锁,在添加排它锁的数据 不能再添加其他共享锁和排它锁的 ,

对于其他事物可读不可写的

*** 所有数据记录修改操作,自动为数据添加排它锁

添加共享锁方式:select * from account lock

in share mode ;

添加排它锁方式:select * from account for

update;

* 锁必须在事务中添加 ,如果事务结束了 锁就释放了

解决丢失更新:事务在修改记录过程中,锁定记录,别的事务无法并发修改

二、乐观锁

采用记录的版本字段,来判断记录是否修改过 --------------

timestamp

timestamp 可以自动更新

create table product (

id int,

name varchar(20),

updatetime timestamp

);

insert into product values(1,'冰箱',null);

update product set name='洗衣机' where id = 1;

* timestamp 在插入和修改时 都会自动更新为当前时间

解决丢失更新:在数据表添加版本字段,每次修改过记录后,版本字段都会更新,如果读取是版本字段,与修改时版本字段不一致,说明别人进行修改过数据

(重改)

事务案例:银行转账(在 service 层处理事务  ThreadLocal)

数据库连接池

连接池介绍

一次性创建多个连接,将多个连接缓存在内存中 ,形成数据库连接池(内存数据库连接集合),如果应用程序需要操作数据库,只需要从连接池中获取一个连接,使用后,并不需要关闭连接,只需要将连接放回到连接池中。

* 好处:节省创建连接与释放连接

性能消耗 ---- 连接池中连接起到复用的作用 ,提供程序性能

自定义连接池

l  编写连接池需实现javax.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:

Connection getConnection()

Connection getConnection(String username,

String password)

l  实现DataSource接口,并实现连接池功能的步骤:

在DataSource构造函数中批量创建与数据库的连接,并把创建的连接保存到一个集合对象中

实现getConnection方法,让getConnection方法每次调用时,从集合对象中取一个Connection返回给用户。

当用户使用完Connection,调用Connection.close()方法时,Connection对象应保证将自己返回到连接池的集合对象中,而不要把conn还给数据库。

自定义连接池程序问题:

1、尽量不要使用具体对象类型的引用

MyDataSource dataSource = new MyDataSource();应该写为 DataSource dataSource = new

MyDataSource();

2、使用自定义方法 close将连接放回连接池,需要用户在使用时需要记忆额外API

解决:让用户定义连接池时 DataSource dataSource =

new MyDataSource();在用户使用连接后,应该调用conn.close();完成将连接放回到连接池

对close方法进行方法增强

练习:增强close方法,不真正关闭连接,而是将连接放回到连接池

增强java中一个方法存在三种方式:1、继承方法覆盖 2、包装 3、动态代理

1、继承方法覆盖,对原有类方法进行加强

注意:必须控制对象构造,才能使用继承的方式对方法进行加强

2、包装(装饰者)

编写包装类 1)必须要与被装饰对象,有相同父类或者实现相同接口  2)必须将被装饰对象传递给装饰者(构造函数)

包装是一种通用方法加强措施,不管是否控制目标对象创建,都可以完成方法加强

3、动态代理

原理:利用类加载器,在内存中根据目标类加载器和接口创建一个代理对象,通过代理对象完成对原有对象方法进行加强

注意:被代理对象,必须实现接口

应用场景:也是只有存在目标对象,就可以通过动态代理进行加强

DBCP连接池

DBCP 是Apache 的commons 项目的一个子项目

去官网下载 dbcp 和 pool 的jar包

,DBCP 依赖 POOL的jar包

复制两个jar 到WEB-INF/lib下

DBCP连接池

核心类 BasicDataSource

* 任何数据库连接池,需要数据库连接,必须通过JDBC四个基本参数构造

1、手动设置参数

// 使用连接池

BasicDataSource

basicDataSource = new BasicDataSource();

//

设置JDBC四个基本参数

basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");

basicDataSource.setUrl("jdbc:mysql:///day14");

basicDataSource.setUsername("root");

basicDataSource.setPassword("abc");

2、通过配置文件

//

根据属性参数 获得连接池

InputStream

in = DBCPTest.class.getResourceAsStream("/dbcp.properties");

Properties

properties = new Properties();

//

装载输入流

properties.load(in);

DataSource

dataSource = BasicDataSourceFactory.createDataSource(properties)

C3P0连接池

主流开源连接池,在Hibernate和Spring 都提供对C3P0连接池支持

去下载c3p0 开发包 http://sourceforge.net

将c3p0的jar 复制WEB-INF/lib下

1、手动

//

核心连接池类

ComboPooledDataSource

comboPooledDataSource = new ComboPooledDataSource();

//

设置四个JDBC基本连接属性

comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");

comboPooledDataSource.setJdbcUrl("jdbc:mysql:///day14");

comboPooledDataSource.setUser("root");

comboPooledDataSource.setPassword("abc");

2、在src下新建c3p0-config.xml

ComboPooledDataSource comboPooledDataSource

= new ComboPooledDataSource(); 会自定加载配置文件

常用基本连接池属性

acquireIncrement   如果连接池中连接都被使用了,一次性增长3个新的连接

initialPoolSize     连接池中初始化连接数量 默认:3

maxPoolSize      最大连接池中连接数量

默认:15连接

maxIdleTime      如果连接长时间没有时间,将被回收 默认:0 连接永不过期

minPoolSize      连接池中最小连接数量 默认:3

Tomcat内置连接池

tomcat服务器内置连接池

(使用Apache DBCP)

配置tomcat 内置连接池,通过JNDI方式 去访问tomcat的内置连接池

JNDI Java命名和目录接口,是JavaEE一项技术 ,允许将一个Java对象绑定到一个JNDI容器(tomcat)中 ,并且为对象指定一个名称

通过javax.naming

包 Context 对JNDI 容器中绑定的对象进行查找,通过指定名称找到绑定Java对象

操作步骤

1、配置使用tomcat 内置连接池 配置 元素

context元素有三种常见配置位置

1) tomcat/conf/context.xml 所有虚拟主机,所有工程都可以访问该连接池

2)

tomcat/conf/Catalina/localhost/context.xml 当前虚拟主机(localhost)下所有工程都可以使用该连接池

3) 当前工程/META-INF/context.xml 只有当前工程可以访问该连接池

type="javax.sql.DataSource" username="root"

password="abc"

driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day14"

maxActive="8" maxIdle="4"/>

* 必须先将mysql驱动jar包 复制tomcat/lib下

* 在tomcat启动服务器时,创建连接池对象,绑定 jdbc/EmployeeDB 指定名称上

2、通过运行在JNDI容器内部的程序(Servlet/JSP)去访问tomcat内置连接池

Context context = new InitialContext();

Context envCtx =

(Context)context.lookup("java:comp/env"); 固定路径

DataSource datasource = (DataSource)

envCtx.lookup("jdbc/EmployeeDB"); 通过绑定名称,查找指定java对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值