自学内容网 自学内容网

更加深入了解jdbc-03-数据库事务以及连接池

数据库事务

一组逻辑操作单元,使数据从一种状态变换到另一种状态。要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。

JDBC处理事务

  • 连接对象创建时,默认情况下是自动提交事务的,也就是sql执行成功后会自动向数据库提交不能回滚。
  • 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务,在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务,在出现异常时,调用 rollback(); 方法回滚事务

场景示例: A用户向B用户转账100元。

  1. 创建一个user表,并且设置user表中的值。
create table user(
uid int primary key AUTO_INCREMENT,
username varchar(20),
account varchar(20)
)

在这里插入图片描述

  1. 编写测试代码
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class AffairsTest {

    //使用事务以后的通用的增删改操作(version 2.0)
    public void update(Connection conn ,String sql, Object... args) {
        PreparedStatement ps = null;
        try {
            // 1.获取PreparedStatement的实例 (或:预编译sql语句)
            ps = conn.prepareStatement(sql);
            // 2.填充占位符
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            // 3.执行sql语句
            ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 4.关闭资源
            JDBCUtils.closeResource(null, ps);
        }
    }

    public void testJDBCTransaction() {
        Connection conn = null;
        try {
            // 1.获取数据库连接
            conn = JDBCUtils.getConnection();
            // 2.开启事务
            conn.setAutoCommit(false);
            // 3.进行数据库操作
            String sql1 = "update user set account = account - 100 where username = ?";
            update(conn, sql1, "A");
            //即同一个事务的多个操作必须在同一个连接下
            // 模拟网络异常
            //System.out.println(10 / 0);

            String sql2 = "update user set account = account  + 100 where username = ?";
            update(conn, sql2, "B");
            // 4.若没有异常,则提交事务
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            // 5.若有异常,则回滚事务
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                //6.恢复每次DML操作的自动提交功能
                conn.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //7.关闭连接
            JDBCUtils.closeResource(conn, null, null);
        }
    }

    public static void main(String[] args) {
        AffairsTest affairsTest = new AffairsTest();
        affairsTest.testJDBCTransaction();
    }
}

  1. 启动测试代码,查看结果
    在这里插入图片描述
    这里,通过将Connection对象中的setAutoCommit()方法设置为false来开启一个事务,然后A用户给B用户进行转账100元(也就是执行俩条更新语句),A用户的account字段减少100,B用户的account字段增加100,最后调用commit()方法,完成提交。数据库中的数据才进行了修改。最后在finnally语句中重新恢复自动提交并且关闭对应流。

注意:
我们通过System.out.println(10 / 0);语句来模拟网络中的异常,当出现异常的时候就会进入到catch代码块中,此时我们会调用Connection中的rollback()方法进行回滚,这时用户的这次转账操作并没有成功,数据还是保持和事务开始的时候一致。

事务的ACID属性

  1. 原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  2. 一致性(Consistency)
    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
  3. 隔离性(Isolation)
    事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  4. 持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

数据库连接池

为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
传统模式存在的问题

  • 数据库的连接资源并没有得到很好的重复利用
  • 对于每一次数据库连接,使用完后都得断开
  • 这种开发不能控制被创建的连接对象数

池化技术的优点:

  1. 资源重用(允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。)
  2. 更快的系统反应速度(直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销)
  3. 新的资源分配手段(通过最小连接数和最大连接数来控制数据库的连接占有数量)
  4. 统一的连接管理,避免数据库连接泄漏(可根据预先的占用超时设定,强制回收被占用连接)

常见的数据库连接池

  • DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快
  • C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。
  • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
  • BoneCP 是一个开源组织提供的数据库连接池,速度快
  • Druid 是阿里提供的数据库连接池

注意:
这些开源组织都实现了javax.sql.DataSource这个接口,DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。

数据库连接池的实现

C3P0数据库连接池
  1. 导入对应的jar包(c3p0-0.9.5.2.jar、c3p0-0.9.5.2-sources.jar、mchange-commons-java-0.2.12.jar)
    在这里插入图片描述

  2. 创建一个c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<named-config name="helloc3p0">
<!-- 获取连接的4个基本信息 -->
<property name="user">root</property>
<property name="password">123456</property>
<property name="jdbcUrl">jdbc:mysql:///test</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>

<!-- 涉及到数据库连接池的管理的相关属性的设置 -->
<!-- 若数据库中连接数不足时, 一次向数据库服务器申请多少个连接 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">5</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">10</property>
<!-- C3P0 数据库连接池可以维护的 Statement 的个数 -->
<property name="maxStatements">20</property>
<!-- 每个连接同时可以使用的 Statement 对象的个数 -->
<property name="maxStatementsPerConnection">5</property>

</named-config>
</c3p0-config>
  1. 编写一个java类来创建数据源
import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class DatabaseConnectionPool {
    //使用C3P0数据库连接池的配置文件方式,获取数据库的连接:推荐
    private static DataSource cpds = new ComboPooledDataSource("helloc3p0");
    public static Connection getConnection2() throws SQLException {
        Connection conn = cpds.getConnection();
        return conn;
    }

    public static void main(String[] args) throws SQLException {
        Connection connection2 = getConnection2();
        System.out.println(connection2);
    }
}
  1. 测试连接是否成功
    在这里插入图片描述
DBCP数据库连接池
  1. 导入jar包(commons-dbcp2-2.9.0.jar、commons-pool2-2.11.1.jar、commons-logging-1.2.jar)
    在这里插入图片描述

  2. 编辑一个dbcp.properties文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
username=root
password=123456

initialSize=10
  1. 编写java程序来创建数据源
public class DatabaseConnectionPool {
//使用dbcp数据库连接池的配置文件方式,获取数据库的连接:推荐
    private static DataSource source = null;
    static{
        try {
            Properties pros = new Properties();

            InputStream is = DatabaseConnectionPool.class.getClassLoader().getResourceAsStream("dbcp.properties");

            pros.load(is);
            //根据提供的BasicDataSourceFactory创建对应的DataSource对象
            source = BasicDataSourceFactory.createDataSource(pros);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    public static Connection getConnection4() throws Exception {

        Connection conn = source.getConnection();

        return conn;
    }
    public static void main(String[] args) throws Exception {
        Connection connection2 = getConnection4();
        System.out.println(connection2);
    }
}
  1. 测试连接是否成功
    在这里插入图片描述
Druid数据库连接池
  1. 导入对应的jar包(druid-1.1.10.jar)
    在这里插入图片描述
  2. 编写一个配置文件druid.properties
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
  1. 编写java程序来配置数据源

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Properties;

public class TestDruid {
    public static void main(String[] args) throws Exception {
        Properties pro = new Properties();
        pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties"));
        DataSource ds = DruidDataSourceFactory.createDataSource(pro);
        Connection conn = ds.getConnection();
        System.out.println(conn);
    }
}
  1. 测试是否连接成功
    在这里插入图片描述
    欢迎java热爱者了解文章,既然学习完了jdbc那么我们直接开始javaEE的学习,期待各位友友的关注和收藏,另外对编程感兴趣的友友们可以加以下群共同学习。群号:127871664

原文地址:https://blog.csdn.net/m0_72926194/article/details/140615507

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!