数据库事务

事务的概念

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

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

  1. 自动提交事务

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

  1. show variables like '%commit%';

当全局变量autocommit的值为ON时,数据库事务时默认提交的。

#关闭数据库自动提交功能
#0是off,1是on
set autocommit = 0;
  1. 手动提交事务

手动进行事务管理步骤:

  1. 开启事务(strart transaction)
  2. 提交(commit)或回滚(rollback)事务

提交事务会将整个事务中的操作更新导数据库

回滚事务则会取消整个事务已执行的所有操作

例:

#开启事务
start transaction;
update student set age = age-1 where sid = 1;
update student set sname= 'erha' where sid = 2;
commit;
#或者
rollback;

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

事务的ACID属性

ACID是数据库事务正确执行的四个基本要素的缩写,一个支持事务的数据库必须具有这四种特性才能保证数据的正确性

  1. 原子性(atomicity):整个事务中的所有操作是不可分割的,要么完全执行,要么完全不执行
  2. 一致性(consistency):事务完成时数据必须是一致的,即与事务开始之前数据存储中的数据处于一致状态
  3. 隔离性(isolation):事务与事务之间相互独立,彼此隔离,操作互不干扰
  4. 持久性(durability):事务一旦提交,那么对数据库中的数据的改变时永久的

数据库的隔离级别

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

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

  1. 脏读

一个事务读取到了另一个事务尚未提交的更改数据

  1. 不可重复读

一个事务读取数据后,另一个事务执行更新操作,使第一个事务无法再现前一次的读取结果

  1. 幻读

一个事务读取数据后,另一个事务执行插入插入操作,使第一个事务无法再现前一次的读取结果

为了解决问题,数据库规范定义了四种隔离级别,用于限定事务之间的可见性

隔离级别 脏读 不可重复读 幻读
read uncommitted(读未提交) 允许 允许 允许
read committed(读已提交) 不允许 允许 允许
repeatable read(可重复读) 不允许 不允许 允许
serializable(串行化) 不允许 不允许 不允许
  • read uncommitted(读未提交):一个事务读到另一个事务没有提交的数据。
  • read committed(读已提交):一个事务读到另一个事务已经提交的数据。
  • repeatable read(可重复读):一个事务中读到的数据始终一致,无论其他事务是否提交
  • serializable(串行化):只能同时执行一个事务,相当于单线程

安全性最高的是serializable(串行化),最低的是read uncommitted(读未提交),越安全执行效率就越低。serializable(串行化)是以锁表的方式,使其他事务只能在锁外等待,选用级别根据实际情况而定。MYSQL默认级别为repeatable read(可重复读)。

JDBC事务处理

再JDBC的数据操作中,Connection对象为事务管理提供了三种方法

  • setAutoCommit(boolean autocommit):设置是否自动提交事务
  • commit():提交事务
  • rollback():回滚

手动进行事务管理,需要调用setAutoCommit(false)来禁止自动提交。


数据库连接池

数据库连接池的必要性

实际开发中,建立连接是一个费时的活动,会造成系统资源和时间的大量消耗,严重的甚至会造成服务器的崩溃。如果程序出现异常而未能关闭将会导致内存的泄漏,最终导致重启数据库。因此开发中通常使用数据库连接池技术。

数据库连接池

为数据连接建立一个缓冲池。预先再缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从连接池中取出一个,用完再放回去

连接池负责分配、管理和释放资源,允许应用程序重复使用一个现有的数据库连接。有四大优势:

  1. 资源重用
  2. 更快的系统响应速度
  3. 新的资源分配手段
  4. 统一的连接管理,避免数据库连接泄漏

工作原理

  1. 连接池的建立;java中提供了很多容器类,可以方便构建连接池
  2. 连接池的管理
  3. 连接池的关闭
  4. 连接池的配置

自定义数据库连接池

用于理解连接池原理


C3P0数据库连接池

C3P0数据库连接池介绍

使用较多的开源数据库连接池之一,性能高效,支持JDBC定义的规范,拓展性好,可以和Hibernate、spring等开源框架整合使用。

它是通过核心类ComboPooledDataSource实现DataSource接口

C3P0数据库连接池使用

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

要在lib目录中导入C3P0的jar

c3p0-0.9.2-pre.jar

mchange-commons-java-0.2.3.jar

package com.itheima.jdbc.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class TestC3P0_1 {
    public static void main(String[] args) throws Exception {
        //核心类
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        //四项基本设置
        //设置连接数据库的驱动
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        //设置连接数据库的路径
        dataSource.setJdbcUrl("jdbc:mysql://localhost/user");
        //设置数据库的用户名
        dataSource.setUser("root");
        //设置数据库的密码
        dataSource.setPassword("root");
        //其他设置
        //初始化连接数为10
        dataSource.setInitialPoolSize(10);
        //设置最大连接数为20
        dataSource.setMaxPoolSize(20);
        //设置最小连接数为3
        dataSource.setMinPoolSize(3);
        //设置每次创建的连接数
        dataSource.setAcquireIncrement(3);
        //设置连接的最大空闲时间
        dataSource.setMaxAdministrativeTaskTime(300);
        //获得连接
        dataSource.getConnection();


    }
}
  1. 通过读取配置文件创建对象(暂时放着先学xml)

先将数据库的配置信息写入c3p0-config.xml文件中,然后通过CombolPooledDataSource类的构造方法读取配置文件并创造该类的对象。


DBCP数据库连接池

DBCP数据库连接池介绍

可以与应用服务器整合使用,也可以由应用程序独立使用。Tomcat服务器即内置了该数据库连接池。

通过BasicDataSource核心类实现DataSourse接口,还提供了BasicDataSourceFactory类用于创建BasicDataSource对象,通过调用createDataSourse()方法读取配置文件信息并返回一个连接池对象给调用者。

DBCP数据库连接池使用

1.直接创建并设置属性

commons-dbcp-1.4.jar

commons-pool-1.6.jar

package com.itheima.jdbc.test;

import org.apache.commons.dbcp.BasicDataSource;

import java.sql.SQLException;

public class TestDBCP_1 {
    public static void main(String[] args) throws SQLException {
        BasicDataSource dataSource = new BasicDataSource();
        //基本四项设置
        //设置连接数据库的驱动
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        //设置连接数据库的路径
        dataSource.setUrl("jdbc:mysql://localhost/user");
        //设置数据库的用户名
        dataSource.setUsername("root");
        //设置数据库的密码
        dataSource.setPassword("root");
        //其他设置
        //初始化连接数为10
        dataSource.setInitialSize(10);
        //设置最大连接数为20
        dataSource.setMaxIdle(20);
        //设置最小连接数为3
        dataSource.setMinIdle(3);
        // 设置可以同时分配的最大活动连接数
        dataSource.setMaxActive(15);
        // 获得连接
        System.out.println(dataSource.getConnection());

    }

}
  1. 通过读取配置文件创建对象

先将数据库连接池的配置信息写入dbcpconfig.properties文件中,然后通过basicDataSourceFactory类读取配置信息并创建BasicDataSoure对象,最后获取。

package com.itheima.jdbc.test;

import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.util.Properties;

public class TestDBCP_2 {
    public static void main(String[] args) throws Exception {
            //用类加载器的方式加载文件
            InputStream is = TestDBCP_2.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties = new Properties();
            properties.load(is);
            //使用BasicDataSourceFactory调用静态方法,获得dataSource
        DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
        System.out.println(dataSource.getConnection());

        }
    }