一.什么叫数据库事务?
- 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
- 一组逻辑操作单元:一个或多个DML操作。
二.事务处理的原则:
保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存 下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。
数据一旦提交,就不可回滚(以下都是为了解决再一次事物处理中数据过早提交的问题)
三.哪些操作会导致数据的自动提交
- DDL操作一旦执行,都会自动提交
- 解决方法: 不适用
- DML默认情况下,一旦执行,就会自动提交。
- 解决方法: set autocommit = false
- 默认在关闭连接时,会自动的提交数据
- 解决方法:等所有事物都执行完毕在关闭连接
四.案例分析
- 分析1:将增删改的通用方法中,获取连接和关闭连接的操作删除,在外部获取连接和关闭连接,并把获取到的连接作为参数传入通用方法中;
作用:这样避免了每执行一次sql语句就关闭一次连接,即避免过早的提交数据,造成无法回滚数据的情况。(对应第三种解决方法)
//增删改操作的通用方法2(考虑事物)
public static int update(Connection conn,String sql,Object ...args){//sql中占位符的个数与可变形参的长度相同!
//Connection conn = null;
PreparedStatement ps = null;
try {
//conn = getConnection();
//1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);//小心参数声明错误!!
}
//3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally{
//4.资源的关闭
closeConnection(null, ps);
}
return 0;
}
- 分析2:在外部获取连接并调用通用方法,使用 conn.setAutoCommit(false)来避免执行DML操作后提交数据。
作用:保证最后执行完成所有SQL语句后,再提交所有数据。若中途遇到异常(案例中模拟了网络异常),则可以做到数据回滚,避免了错误的产生。(对应第二种解决方法)
说明:再使用 set autocommit = false 之后一定要再改回去,因为若该连接为数据库连接池中获取的连接可能被重复使用。
@Test
public void testUpdateWithTx() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
System.out.println(conn.getAutoCommit());//true
//1.取消数据的自动提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn,sql1, "AA");
//模拟网络异常
System.out.println(10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2, "BB");
System.out.println("转账成功");
//2.提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
try {
conn.setAutoCommit(true);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
JDBCUtils.closeConnection(conn, null);
}
}
五.事务的ACID属性
- 原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
- 隔离性(Isolation) 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的 数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其 他操作和数据库故障不应该对其有任何影响。
5.1 数据库的并发问题
- 对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种 并发问题:
- 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的 内容就是临时且无效的。
- 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字 段, 值就不同了。
- 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如 果 T1 再次读取同一个表, 就会多出几行。
- 数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。
- 一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰 程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
5.2 四种隔离级别
- Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED 。
- Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。
5.3 在MySql中设置隔离级别
- 每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别。
- 查看当前的隔离级别:
SELECT @@tx_isolation;
- 设置当前 mySQL 连接的隔离级别:
set transaction isolation level read committed
- 设置数据库系统的全局的隔离级别:
set global transaction isolation level read committed;
六.数据库连接池
6.1 数据库连接池技术的优点
- 资源重用 由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一 方面也增加了系统运行环境的平稳性。
- 更快的系统反应速度 数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均 已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销, 从而减少了系统的响应时间
- 新的资源分配手段 对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库 连接数的限制,避免某一应用独占所有的数据库资源
- 统一的连接管理,避免数据库连接泄漏 在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据 库连接操作中可能出现的资源泄露
6.2 多种开源的数据库连接池
- JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器 (Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
- DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因 自身存在BUG,Hibernate3已不再提供支持。
- C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。hibernate官方推荐使用
- Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一 点
- BoneCP 是一个开源组织提供的数据库连接池,速度快
- Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是 速度不确定是否有BoneCP快
- DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
- DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速
数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个 数据源即可。
注意:当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
6.3 Druid(德鲁伊)数据库连接池
Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了 日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的 连接池之一。
- 实现代码:
mport java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.junit.Test;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class DruidTest {
public Connection getConnection() throws Exception{
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");//读取配置文件
pros.load(is);
//创建连接池
DataSource source = DruidDataSourceFactory.createDataSource(pros);
//获取连接
Connection conn = source.getConnection();
return conn;
}
}
- 配置文件
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall
- 详细配置参数