15158846557 在线咨询 在线咨询
15158846557 在线咨询
所在位置: 首页 > 营销资讯 > 网站运营 > Java Web开发实战—JDBC进阶

Java Web开发实战—JDBC进阶

时间:2023-05-24 19:00:02 | 来源:网站运营

时间:2023-05-24 19:00:02 来源:网站运营

Java Web开发实战—JDBC进阶:

数据库事务

事务的概念

事务,是指数据库中的一个操作序列,它由一条或多条SQL命令所组成,这些SQL命令不可分割,只有当事务中的所有SQL命令被成功执行后,整个事务引发的操作才会被更新到数据库,如果有一条执行失败,所有操作都将会被取消。

下面通过一个生活实例来讲解数据库的事务。现在很多商店提供扫码支付功能,假如李磊购物之后需向商家支付500元,其购物行为触发的SQL语句如下。

这两条SQL命令属于同一个操作序列,只有全部被成功执行时,整个事务才会被更新到数据库,否则,全部SQL命令都要被取消。这就避免了李磊账户少500元而商家账户金额不变的情况。

各大数据库厂商均提供了对事务的支持,接下来以MySQL为例讲解数据库中事务的管理。

MySQL数据库共有两种方式来管理事务。

1. 自动提交事务

在默认状态下,MySQL自动提交事务,即每执行一条SQL语句就提交一次事务。这可以通过MySQL的全局变量autocommit进行查看,SQL语句如下。

通过SQL语句查看当前数据库的事务状态,执行结果如下。

从以上执行结果可以看出,全局变量autocommit的值为ON,这时,数据库事务是默认提交的。

关闭数据库自动提交事务的功能,SQL语句如下。

SET AUTOCOMMIT = 0;#0是OFF,1是ON

以上SQL语句的执行结果如下。

mysql> SET AUTOCOMMIT = 0;

Query OK, 0 rows affected (0.00 sec)

再次通过SQL语句查看当前数据库的事务状态,执行结果如下。

从以上执行结果可以看出,全局变量autocommit的值为OFF,这时,数据库事务是需要手动提交的。

2. 手动提交事务

手动进行事务管理时,首先要开启事务(Start Transaction),再提交(Commit)或回滚(Rollback)事务。提交事务会将整个事务中的操作更新到数据库,回滚事务则会取消整个事务中已执行的所有操作。当手动提交事务时,上文实例中购物支付触发的SQL语句如下。

在实际的开发过程中,事务是并发控制的基本单位,将一组操作序列组合为一个要么全部成功要么全部失败的单元,可以简化错误,恢复并使应用程序更加可靠。

事务的ACID属性

ACID,是数据库事务正确执行的四个基本要素的缩写,它包含原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务的数据库,必需要具有这四种特性,否则在事务管理中无法保证数据的正确性。

1.原子性(Atomicity)

整个事务中的所有操作是不可分割的,要么完全执行,要么完全不执行,不能停滞在中间某个环节。事务的操作序列如果全部成功,就必须完全应用到数据库;如果有一项操作失败,就不能对数据库有任何影响。

2.一致性(Consistent)

事务完成时,数据必须是一致的,也就是说,和事务开始之前,数据存储中的数据处于一致状态,保证数据的无损。以转账为例,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还是5000,这就是事务的一致性。

3. 隔离性(Isolation)

隔离性是指事务与事务之间互相独立,彼此隔离。当多个用户并发访问数据库时,如操作同一张表,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰。对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

关于事务的隔离性,数据库提供了多种隔离级别,稍后会介绍。

4.持久性 (Durability)

持久性是指事务一旦提交,那么对数据库中的数据的改变就是永久性的,即使是数据库系统遇到故障也不会丢失事务处理的效果。比如:银行转帐过程中,转帐后帐户的数据要能被永远地保存下来。

数据库的隔离级别

对数据库而言,其明显的特征是资源可以被多个用户共享。当相同的数据库资源被多个用户(多个事务)同时访问时,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。

如果不考虑隔离性,数据库将会存在三种并发问题。

1.脏读

一个事务读到了另一个事务尚未提交的更改数据。例如,事务T1修改某一数据后,事务T2读取同一数据,然后事务T1由于某种原因撤销修改,这时T1已修改过的数据恢复原值,T2读到的数据就与数据库中的数据不一致,其读到的数据就为"脏"数据,对该数据的操作也无法承认。

2.不可重复读

不可重复读是指一个事务读取数据后,另一个事务执行更新操作,使第一个事务无法再现前一次的读取结果。例如,事务T1读取B=100进行运算,事务T2读取同一数据B,对其进行修改后将B=200写回数据库。这时,T1为了对读取值校对重读B,而B已为200,导致此次读取值与第一次读取值不一致。

3.幻读

幻读是指一个事务读取数据后,另一个事务执行插入操作,使第一个事务无法再现前一次的读取结果。例如,事务T1两次统计所有账户的总金额,在这期间,事务T2插入了一条新记录,使得两次统计的总金额不一致。

为了解决并发造成的问题,数据库规范定义了四种隔离级别,用于限定事务之间的可见性,不同的事务隔离级别对应的解决数据并发问题的能力是不同的,具体如表2.1所示。

表2.1 数据库的隔离级别

隔离级别脏读不可重复读幻读
read uncommitted (读未提交)允许允许允许
read committed (读已提交)不允许允许允许
repeatable read (可重复读)不允许不允许允许
serializable (串行化)不允许不允许不允许
l read uncommitted (读未提交):一个事务读到另一个事务没有提交的数据。

l read committed (读已提交):一个事务读到另一个事务已经提交的数据。

l repeatable read (可重复读):在一个事务中读到的数据始终一致,无论其他事务是否提交。

l serializable(串行化):只能同时执行一个事务,相当于事务中的单线程。

在以上四种隔离级别中,安全性最高的是serializable (串行化),最低的是read uncommitted(读未提交),当然安全性能越高,执行效率就越低。像serializable(串行化)这样的级别,就是以锁表的方式,使得其他的事务只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。MySQL数据库默认的隔离级别为repeatable read (可重复读)。

JDBC事务处理

创建数据库和表

创建一个名称为chapter02的数据库,并在该数据库中创建名为account的表,向表中插入若干条数据,具体的SQL语句如下。

上述SQL语句运行完毕后,在命令行窗口检验数据库环境是否搭建成功,执行select语句,执行结果如下。

创建Java工程

在Eclipse中新建Java工程chapter02,在工程chapter02下新建目录lib,将MySQL数据库的驱动jar包mysql-connector-java-5.1.37-bin.jar复制到lib目录下,右击lib目录下的mysql-connector-java-5.1.37-bin.jar,在弹出的菜单中选择Build Path→Add to Build Path命令,完成jar包的导入。在工程chapter02的src目录下新建com.qfedu.chapter02包,在com.qfedu.chapter02包下新建TestPayment类,该类用于模拟支付过程,其中,lilei将支付给shop人民币100元,具体代码如书中例2-1所示。




数据库连接池

数据库连接池的必要性

本书第1章已经介绍过,编写JDBC程序一般会按照装载数据库驱动、建立数据库连接、执行SQL语句、断开数据库连接的步骤进行。

然而在实际开发过程中,建立连接是一个费时的活动。每一次请求都要建立一次数据库连接,每次向数据库建立连接的时候都要将 Connection 对象加载到内存中,若遇到访问量剧增的情况,势必会造成系统资源和时间的大量消耗,严重的甚至会造成服务器的崩溃。而且,对于每一次数据库连接,使用完后都得断开,数据库的连接资源不能得到很好的重复利用。如果程序出现异常而未能关闭连接,将会导致数据库系统中的内存泄漏,最终导致重启数据库。

从以上分析可以看出,传统的管理数据库连接的方式存在缺陷,为了解决这个问题,在实际开发中通常使用数据库连接池技术。

数据库连接池

数据库连接池,简单的说,就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去,具体如图2.1所示。

图2.1 数据库连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。数据库连接池对连接资源进行管理和调配,共有四个方面的优势。

1. 资源重用

由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,也增进了系统运行环境的平稳性,减少内存碎片以及数据库临时进程/线程的数量。

2. 更快的系统响应速度

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

3. 新的资源分配手段

对于多程序共享同一数据库的系统,可在应用层通过数据库连接池配置某一程序能够使用的最大数据库连接数,避免某一程序独占所有数据库资源。

4. 统一的连接管理,避免数据库连接泄漏

在较为完备的数据库连接池的实现中,可根据预先的连接占用超时设定,强制收回被占用的连接,从而避免了常规数据库在连接操作中可能出现的资源泄漏。

数据库连接池

工作原理

连接池技术的核心思想是连接的复用,通过建立一个数据库连接池以及一套连接使用、分配、管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。另外,由于对JDBC中的原始连接进行了封装,从而方便了数据库应用对于连接的使用,提高了开发效率。

对连接池的工作原理可以从连接池的建立、连接池的管理、连接池的关闭、连接池的配置四个方面去理解。

1. 连接池的建立

一般在系统初始化时,连接池会根据系统配置建立,并在池中建立若干个连接对象,以便使用时能从连接池中获取。为避免连接随意建立和关闭造成的系统开销,连接池中的连接不能随意创建和关闭。Java中提供了很多容器类,可以方便地构建连接池。

2. 连接池的管理

连接池管理策略是连接池机制的核心,会对系统性能产生很大影响。当线程请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给线程使用。如果没有空闲连接,则查看当前所开的连接数是否已经达到最杭州接数,如果没有达到就重新创建一个连接;如果达到,就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常。

当线程释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过了就从连接池中删除该连接,否则就保留为其他线程服务。该策略保证了数据库连接的有效复用,避免了频繁建立释放连接所带来的系统资源开销。

3. 连接池的关闭

当应用程序退出时,关闭连接池中所有的链接,释放连接池相关资源,该过程正好与创建相反。

4. 连接池的配置

数据库连接池中采用minConn和maxConn来限制连接的数量。minConn是当应用启动时连接池所创建的连接数,如果过大启动将变慢,但是启动后响应更快;如果过小启动将加快,但是最初使用时会因为连接不足而延缓执行速度,可以通过反复试验来确定饱和点。maxConn是连接池中的最杭州接数,设定连接池最杭州接数来防止系统无尽的与数据库连接。

自定义数据库连接池

为了方便大家理解,本书将演示如何自定义一个简单的连接池。自定义连接池,基本思想就是新建一个List集合充当连接池,然后在连接池中添加几个连接,当使用连接的时候,从连接池中获取,当不使用时,将连接归还到连接池中。

(1)在src目录下的com.qfedu.chapter02包下新建MyConnectionPool类,该类用于模拟连接池的基本功能,具体代码如例2.2所示。

(2)在src目录下的com.qfedu.chapter02包下新建TestMyConnectionPool类,该类用于测试连接池的效果,具体代码如例2.3所示。

实际开发中已有很多性能优良的第三方连接池可供使用,开发者一般只需理解连接池的原理,如果没有特殊要求就无须自行定义连接池。

为了方便地利用数据库连接池进行开发,Java语言为数据库连接池提供了公共的接口DataSource,第三方数据库连接池一般都要实现该接口,从而使Java程序能够在不同的数据库连接池之间切换。

目前常用的数据库连接池 C3P0和DBCP,它们都是实现DataSource接口的。接下来,本书将对这两种数据库连接池作重点讲解。

C3P0数据库连接池

C3P0数据库连接池介绍

在目前的开发中,C3P0是使用较多的开源数据库连接池之一,它性能高效,支持JDBC定义的规范,扩展性好,可以和Hibernate、Spring等开源框架整合使用,很受开发者欢迎。

C3P0数据库连接池通过核心类ComboPooledDataSource实现DataSource接口,该类支撑着整个连接池的主要功能。它提供了充足的方法来实现对数据库连接池的配置和操作,具体如表2.2所示。

表2.2 ComboPooledDataSource类的方法




方法名称功能描述
void setDriverClass(String driverClass)设置连接数据库的驱动
void setJdbcUrl(String jdbcUrl)设置连接数据库的路径
void setUser(String user)设置数据库用户名
void setPassword(String password)设置数据库密码
void setMaxPoolSize(int maxPoolSize)设置数据库连接池最杭州接数
void setMinPoolSize(int minPoolSize)设置数据库连接池最小连接数
void setInitialPoolSize(int initialPoolSize)设置数据库连接池初始化的连接数
void setMaxIdleTime(int maxIdleTime)设置连接的最大空闲时间
Connection getConnection()获得连接



C3P0数据库连接池使用

使用C3P0数据库,主要是对其核心类ComboPooledDataSource方法的调用,在此之前,首先要获得一个可用的ComboPooledDataSource对象。通常情况下,获得该对象的方法有两种。

1. 直接创建ComboPooledDataSource对象并设置属性

这种实现方式使用ComboPooledDataSource类直接创建对象,然后调用方法为其设置属性,最后获取数据库的连接,接下来将通过具体实例对这一过程进行讲解。

(1)将C3P0连接池的jar包c3p0-0.9.2-pre5.jar和mchange-commons-java-0.2.3.jar复制到工程chapter02的lib目录下,右击lib目录下的上述jar包,在弹出的菜单中选择Build Path→Add to Build Path,完成jar包的导入。

(2)在src目录下的com.qfedu.chapter02包下新建TestC3P0_1类,具体代码如例2.4所示。

2. 通过读取配置文件创建ComboPooledDataSource对象

使用这种方式,首先将数据库连接池的配置信息写入了c3p0-config.xml文件中,然后通过ComboPooledDataSource类的构造方法读取配置信息并创建该类对象,最后调用该类对象的方法获取数据库连接,接下来将通过具体实例对这一过程进行讲解。

(1)在src目录下创建一个c3p0-config.xml的配置文件,具体代码如例2.5所示。

在c3p0-config.xml文件中,<default-config>……</default-config>标签中的内容为默认配置,如果在创建ComboPooledDataSource对象时没有指定配置信息,就默认采用该配置。<named-config name="qfedu">……</named-config>标签中的内容为命名配置,如果想要采用该配置,就必须在需要在创建ComboPooledDataSource对象时传入<named-config>标签中name属性的值。

(2)在src目录下的com.qfedu.chapter02包下新建TestC3P0_2类,该类用于测试C3P0数据库连接池的功能。

DBCP数据库连接池

DBCP数据库连接池介绍

DBCP是数据库连接池(DataBase connection pool)的简称,是由Apache组织开发的开源数据库连接池。该连接池既可以与应用服务器整合使用,也可由应用程序独立使用。Tomcat服务器即内置了该数据库连接池。

DBCP数据库连接池通过BasicDataSource核心类实现DataSource接口,与C3P0连接池的CombopooledDataSource类相同,BasicDataSource类也提供了一套API来完成对连接池对象的配置和操作。BasicDataSource类提供的方法如表2.3所示

表2.3 BasicDataSource类的方法




方法名称功能描述
void setDriverClassName(String driverClassName)设置连接数据库的驱动
void setUrl(String url)设置连接数据库的路径
void setUsername(String username)设置数据库用户名
void setPassword(String password)设置数据库密码
void setMaxActive(int maxActive)设置数据库连接池最杭州接数
void setMinIdle(int minIdle)设置数据库连接池最小连接数
void setInitialSize(int initialSize)设置数据库连接池初始化的连接数
Connection getConnection()获得连接



除了BasicDataSource类,DBCP数据库连接池还提供了BasicDataSourceFactory类用于创建BasicDataSource对象,它通过调用createDataSource()方法读取配置文件信息并返回一个连接池对象给调用者,这些与C3P0是不同的。

DBCP数据库连接池的使用

当使用DBCP数据库连接池时,首先要获取BasicDataSource对象。获取BasicDataSourc对象有两种方法,具体如下。

1.直接创建BasicDataSource并设置属性

这种实现方式采用硬编码方式,直接创建ComboPooledDataSource对象后调用方法为其设置属性值,最后获取数据库的连接。

2. 通过读取配置文件创建ComboPooledDataSource对象

使用这种方式,首先将数据库连接池的配置信息写入dbcpconfig.properties文件中,然后通过BasicDataSourceFactory类读取配置信息并创建BasicDataSource对象,最后调用该对象的方法获取数据库连接。




小结:Java Web开发实战—JDBC进阶

本章主要介绍了JDBC事务处理以及数据库连接池的相关知识,包括事务的概念、属性,JDBC事务处理,数据库连接池的概述以及工作原理等,最后通过案例对C3P0和DBCP两种数据库连接池技术进行了详细讲解。通过对本章内容的学习,大家应该掌握JDBC处理事务以及通过数据库连接池获取连接的开发流程。



关键词:实战

74
73
25
news

版权所有© 亿企邦 1997-2025 保留一切法律许可权利。

为了最佳展示效果,本站不支持IE9及以下版本的浏览器,建议您使用谷歌Chrome浏览器。 点击下载Chrome浏览器
关闭