JavaWeb14-事务,连接池(Java正在的全栈开发)

时间:2022-05-04
本文章向大家介绍JavaWeb14-事务,连接池(Java正在的全栈开发),主要内容包括事务&连接池、一.事务、二.连接池、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

事务&连接池

一.事务

1. 事务介绍

什么是事务

事务,一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元。这些单元要么全都成功,要么全都不成功。

事务在开发中的作用

下面我们来举例说明什么是事务,如下所示:

现实生活中的银行转账业务,张三要给李四转账1000元,而在程序员眼中两条SQL语句就可以搞定,如下:

l 给张三的账户减去1000元;

l 给李四的账户加上1000元;

如果在转账的业务中,成功的将张三的账户减去1000元,而在给李四的账户加1000元的时候,程序出现了问题,李四的账户没有加上1000元,而张三的账户却减掉了1000元,在现实生活中,这种情况肯定是不允许存在的。当我们将这个转账业务放在一个事务中,就不会出现以上情况了。

事务中有多个操作,这些操作要么全部成功,要么全部失败,也就是说给张三的账户减去1000元如果成功了,那么给李四的账户加上1000元的操作也必须是成功的,否则给张三减去1000元,以及给李四加上1000元都必须是失败的

2. mysql中的事务

mysql中默认事务处理

在mysql登录的情况下执行以下命令

show variables like '%commit%'; ---- autocommint 值是 on,说明开启自动提交

mysql数据库默认是开启事务的,一条sql一个事务.

oracle它默认情况下autocommit是off,就需要手动提交事务.

关闭mysql的自动事务处理命令

set autocommit = off;( set autocommit = 0)

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

mysql中手动事务处理

start transaction:-- 开启事务 一旦手动开启了事务,事务自动提交失效.

commit;-- 提交事务

rollback;-- 事务回滚

3. jdbc中的事务

jdbc中事务处理api

java.sql.Connection接口中提供了关于事务操作的API

setAutoCommit(boolean flag); 参数为false相当于start transaction

commit(); 事务提交

rollback(); 事务回滚

回滚(Rollback)指的是程序或数据处理错误,将程序或数据恢复到上一次正确状态的行为

回滚点介绍

Savepoint setSavepoint(String name)

在当前事务中创建一个具有给定名称的保存点,并返回表示它的新 Savepoint 对象。

例如:

Savepoint sp = conn.setSavepoint();

Conn.rollback(sp);

4. 事务的特性

事务的四大特性是面试官经常问的问题,简称ACID(Atomicity Consistency Isolation Durability),分别是:

原子性:原子性对应的英文是Atomicity,即表示事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败;

一致性:一致性对应的英文是Consistency,事务执行后,数据库状态与其它业务规则保持一致。例如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的;

隔离性:隔离性对应的英文是Isolation,是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰;

持久性:持久性对应的英文是Durability,指的是一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。

不同的事务,其一致性的表现形式是不同的,事务的其他三大特性其实都是为了事务的一致性服务的。

5. 事务的隔离级别与问题

不考虑隔离性产生的问题介绍

脏读 一个事务读取到了另一个事务未提交数据.

不可重复读 一个事务内,两次读取到的数据不一致.(update)

虚读(幻读) 两次读取的数据不一致(insert)

事务的4种隔离级别介绍

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

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

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

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

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

mysql数据库默认的事务隔离级别-----repeatable read级别.

oracle数据默认的事务隔离级别 ----read committed

设置事务隔离级别

nmysql中设置

数据库默认有事务的隔离级别,mysql 中查看与修改事务的隔离级别

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

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

njdbc中设置事务隔离级别

在java.sql.Connection接口中提供

setTransactionIsolation(int level) ;

参数可以取 Connection 常量之一:

Connection.TRANSACTION_READ_UNCOMMITTED、

Connection.TRANSACTION_READ_COMMITTED、

Connection.TRANSACTION_REPEATABLE_READ

Connection.TRANSACTION_SERIALIZABLE。

(注意,不能使用 Connection.TRANSACTION_NONE,因为它指定了不受支持的事务。)

6. 演示设置隔离级别来解决问题

脏读分析与解决

脏读:一个事务读取到另一个事务未提交数据.

1.打开两个窗口 A B

2.在A 窗口中设置事务的隔离级别为Read uncommitted

3.在A窗口中设置事务的隔离级别为Read uncommitted

4.在A窗口中开启事务,将aaa---bbb 转500

5.在B窗口查看account表中数据,,发现可以查看 到A窗口中的未提交的事务 操作结果。就出现了脏读。

解决脏读 设置事务的隔离级别为 read committed

不可重复读分析与解决

在上面的例子基础上操作,当我们将事务的隔离级别设置 为read committed时,B窗口在查询时,就没有查询到A窗口未提交的数据,解决了脏读。

1.当A窗口将数据提交

2.这时B窗口之前查询过一次,再次查询,这时就发现,我们在一个事务中两次查询结果不一致,就出现了不可重复读。

解决不可重复读 设置事务的隔离级别为 Repeatable read

虚读分析

它主要强调的是多次查询的结果的条不一样,而不可重复读强调的是结果不一样。

串行化演示

设置隔离级别为 Serializable

注意

对于隔离级别四种

安全性 Serializable>Repeatable read>read committed >read uncommitted;

性能 Serializable<Repeatable read<read committed <read uncommitted;

在开发中,一般使用 read committed repeatable read两种。

7. 案例-转账汇款

功能描述

实现帐户之间转账操作,在创建一个account.jsp页面,在页面上有三个文本框,分别是转款人,收款人及金额,提交到服务器端,服务器端调用jdbc完成汇款操作。

我们采用web三层架构

涉及到的页面与类

Web层 account.jsp AccountServlet

Service层 AccoountService

Dao层 AccountDao

案例分析

注意:

在service层中使用事务

代码实现

8. 案例-使用ThreadLocal解决转账汇款问题

ThreadLocal介绍与原理分析

public class ThreadLocal<T>extends Object

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

它的底层是使用了一个Map集合

Map<Thread,Object>

它的key就是当前的线程对象.

set(Object obj) 它就相当于 map.put(Thread.currentThread(),obj);

get()它就相当于 map.get(Thread.currentThread()));

二.连接池

1. 连接池介绍

什么是连接池

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

以下是使用连接池与没有使用连接池的一个比较图例

连接池的优点

节省创建连接与释放连接性能消耗

连接池中连接起到复用的作用,提高程序性能

连接池的原理

连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

连接池的规定

javax.sql包下有一个 DataSource

所有的支持java的连接池都应该实现javax.sql.DataSource接口,在这个接口中提供了一个方法 getConnection()它就是获取一个连接对象的。

如果连接对象Connection是通过连接池获取的,当通过Connection对象调用close()方法时,不在是销毁连接对象,而是将连接对象放回到连接池。

2. dbcp连接池

dbcp连接池介绍

DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:

commons-dbcp.jar:连接池的实现

commons-pool.jar:连接池实现的依赖库

Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用.

dbcp连接池配置(代码)

// 1.创建连接池对象

BasicDataSource ds = new BasicDataSource();

// 2.设置相关属性

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

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

ds.setUsername("root");

ds.setPassword("1234");

dbcp连接池配置(配置文件)

//1.加载配置信息

Properties props = new Properties();

props.load(new FileInputStream(properties配置文件路径);

//properties文件内容

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql:///day14

username=root

password=1234

// 2.通过BasicDataSourceFactory获取一个连接池对象

DataSource ds = BasicDataSourceFactory.createDataSource(props);

3. c3p0连接池

c3p0连接池介绍

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。

c3p0与dbcp区别

dbcp没有自动回收空闲连接的功能

c3p0有自动回收空闲连接功能

在使用c3p0连接池时将c3p0的jar 复制WEB-INF/lib下,我们使用的版本

c3p0-0.9.1.2.jar

c3p0连接池配置(代码)

//1.创建连接池对象
ComboPooledDataSource ds = new ComboPooledDataSource();
// 2.手动配置参数
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql:///day14");
ds.setUser("root");
ds.setPassword("1234");

c3p0连接池配置(配置文件)

只要在src下创建c3p0.properties 或者 c3p0-config.xml名称的配置文件,c3p0会自动查找.

ComboPooledDataSource ds = new ComboPooledDataSource();

自动查找配置文件

在src/c3p0-config.xml

<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///day14</property>
<property name="user">root</property>
<property name="password">1234</property>
</default-config>
</c3p0-config>

或者在src/c3p0.properties

c3p0.jdbcUrl=jdbc:mysql://localhost:3306/day07
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.user=root
c3p0.password=1234

4. tomcat内置连接池

tomcat内置连接池介绍

我们在web开发中,可以将链接池交给服务器管理,如果需要时,就可以直接从服务器中获取连接对象。在tomcat中提供了一个context.xml文件,我们可以将连接池配置在这个文件中,详细信息大家可以查看tomcat帮助文档

context.xml文件可以放置在三个位置:

1.tomcat/conf下 这个配置是被服务器下所有应用所使用的.

2.tomcat/Catalina/xxx目录下,这个配置被某一个虚拟主机使用

3.在web工程的META-INF目录下创建一个context.xml文件,它是被当前工程所使用

JNDI介绍

JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。目录服务是命名服务的一种自然扩展

配置tomcat内置连接池

<Context>
<Resource name="jdbc/abc" auth="Container"
type="javax.sql.DataSource" username="root" password="1234"
driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql:///day14"
maxActive="8" maxIdle="4"/>
</Context>

获取连接的方法:

Context context = new InitialContext(); // 初始化上下文对象
Context envCtx = (Context) context.lookup("java:comp/env"); // 固定写法
DataSource ds = (DataSource) envCtx.lookup("jdbc/abc");// 通过绑定名称,查找指定java对象