一、数据库的事务
1.1 事务概述
- 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态
- 事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态
- 为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态
- 举个栗子,当小明给小红转账时,因为种种原因没有转账成功,小明的钱减少了,小红却没有收到钱,此时就需要事务回滚,否则小明就得哭死…
1.2 事务的属性
事务的ACID(acid)属性
- 原子性(Atomicity)
- 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency)
- 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
- 隔离性(Isolation)
- 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(Durability)
- 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
1.3 JDBC事务处理
当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚
为了让多个 SQL 语句作为一个事务执行:
1. 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
2.在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
3.在出现异常时,调用 rollback(); 方法回滚事务
4.若此时 Connection 没有被关闭, 则需要恢复其自动提交状态
1.4 数据库事务使用的过程
使用数据库的事务,我们需要配合异常处理try
public void testJDBCTransaction() {
Connection conn = null;
try {
// 1.获取数据库连接
conn = JDBCUtils.getConnection();
// 2.开启事务
conn.setAutoCommit(false);
// 3.进行数据库操作
// 4.若没有异常,则提交事务
conn.commit();
} catch (Exception e) {
e.printStackTrace();
// 5.若有异常,则回滚事务
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}} finally {
JDBCUtils.close(null, null, conn); } }
1.5 使用数据库事务的好处
使用COMMIT 和 ROLLBACK语句,我们可以:
- 确保数据完整性。
- 数据改变被提交之前预览。
- 将逻辑上相关的操作分组
提交或回滚前的数据状态
- 改变前的数据状态是可以恢复的
- 执行 DML 操作的用户可以通过 SELECT 语句查询提交或回滚之前的修正
- 其他用户不能看到当前用户所做的改变,直到当前用户结束事务。
- DML语句所涉及到的行被锁定, 其他用户不能操作
提交后的数据状态
- 数据的改变已经被保存到数据库中。
- 改变前的数据已经丢失。
- 所有用户可以看到结果。
- 锁被释放, 其他用户可以操作涉及到的数据
说了这么多,还是使用代码来举例说明更加清晰(数据库连接的JDBCUtils类上一章写过了):
package com.company.jdbcDemo;
import com.company.jdbcDemo.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class AccountDemo {
public static void main(String[] args) throws SQLException {
//1.获取数据库连接对象
Connection connection = JDBCUtils.getConnection();
PreparedStatement ps = null;
try {
//2.开启事物--禁止自动提交
connection.setAutoCommit(false);
//-------------------------------------------------------------------
//3.做具体的操作---执行sql语句
//预编译
String sql = "update account set balance=? where name=?";
ps = connection.prepareStatement(sql);
//给占位符赋值
ps.setInt(1, 1000);
ps.setString(2, "aa");
//执行sql
ps.executeUpdate();
System.out.println(1 / 0);
//给占位符赋值
ps.setInt(1, 3000);
ps.setString(2, "cc");
//执行sql
ps.executeUpdate();
//-------------------------------------------------------------------
//4.事务提交
connection.commit();
}catch (Exception e){
e.printStackTrace();
//5.事务回滚
connection.rollback();
}finally {
//6.允许自动提交
connection.setAutoCommit(true);
//7.关闭资源----最后关闭资源
JDBCUtils.close(ps,connection);
}
}
}
二、数据库连接池
概述
前面我们的示例代码中,一直在调用我写的那个JDBCUtils类来完成数据库的连接,如果我们在别的工程中,就需要复制一下我的那个类,并且我写的那个也不是很严谨,我前面实现的数据库连接方式存在以下问题:
- 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码
- 数据库的连接资源并没有得到很好的重复利用.若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃
- 对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库
- 这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃
使用数据库连接池
- 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去
- 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
- 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中
2.1 数据库连接池技术的优点
- 资源重用
- 由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
- 更快的系统反应速度
- 数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间
- 新的资源分配手段
- 对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源
- 统一的连接管理,避免数据库连接泄露
- 在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露
三、DRUID(德鲁伊)
- 了解了数据库连接池以及优势,接下来我为大家带来一个目前常用的一个数据库连接池框架—>德鲁伊
- DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,据说是目前最好的连接池
3.1 德鲁伊的使用
阿里德鲁伊连接池技术首先分为两步:
1.加入jar包
例如:druid-1.1.10.jar
2.代码步骤
第一步:建立一个数据库连接池
第二步:设置连接池的参数
第三步:获取连接
使用德鲁伊连接数据库的方式一
//1、创建数据源(数据库连接池)对象
DruidDataSource ds =new DruidDataSource();
//2、设置参数
//(1)设置基本参数
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("root");
ds.setPassword("mysql123");
//3、获取连接
Connection conn = ds.getConnection();
//如果这里没有关闭,就相当于没有还
conn.close();
使用德鲁伊连接数据库的方式二
// 创建配置文件druid.properties
url=jdbc:mysql://localhost:3306/0319db ?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
代码如下:
Properties pro = new Properties();
pro.load(TestDruid2.class.getClassLoader().getResourceAsStream("druid.properties"));
DataSource ds=
DruidDataSourceFactory.createDataSource(pro);
Connection conn = ds.getConnection();
这里注意了,德鲁伊配置文件中的key,必须跟我下面一样,否则连接不成功哦
// druid.properties内容
url=jdbc:mysql://localhost:3306/demo
username=root
password=123321
driverClassName=com.mysql.jdbc.Driver
四、DBUtils工具类
- 既然数据库连接有了数据库连接池这么方便地操作,那么对数据的增删改查有没有相关的方法呢?当然有,它就是DBUtils
- 将常用的操作数据库的JDBC的类和方法集合在一起,就是DBUtils.
- 这个比较简单,我就介绍一下常用的API操作,具体的知识点,可以查看Java的API文档
我就直接上操作了,这里还是使用我前面实现的那个JDBCUtils类哈,偷个懒…
package com.company.jdbc2;
import com.company.jdbc.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;
import java.sql.SQLException;
import java.util.List;
public class DBUtilsDemo {
@Test
public void test() throws SQLException {
//1.创建操作对象
QueryRunner queryRunner = new QueryRunner();
//2.增,删,改是一个方法
String sql = "insert into student(sid,sname,sage) values(?,?,?)";
//返回值 :有几条数据受到影响
int i = queryRunner.update(JDBCUtils.getConnection(),
sql, 10, "kongkong", 18);
System.out.println("有" + i + "条数据受到影响");
}
@Test
public void test2() throws SQLException {
QueryRunner queryRunner = new QueryRunner();
String sql = "select sid a,sname,sage from student where sid=?";
//注意:类中的属性名一定要和字段名相同。如果不相同则需要在sql语句中使用别名
// Student student = queryRunner.query(JDBCUtils.getConnection(), sql,
// new BeanHandler<Student>(Student.class), 10);
sql = "select sid a,sname,sage from student";
List<Student> list = queryRunner.query(JDBCUtils.getConnection(), sql,
new BeanListHandler<Student>(Student.class));
for (Student s : list) {
System.out.println(s);
}
}
}
使用批处理
当我们需要对进行大批量的数据操作时,可以采用批处理技术,很简单,在url中添加批处理的参数
jdbc:mysql://localhost:3306/Demo?rewriteBatchedStatements=true
示例代码,(咳咳,依旧是我那个JDBCUtils实现连接的工具类…)
package com.company.jdbc3;
import com.company.jdbc.JDBCUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class BatchDemo {
@Test
public void test2() throws SQLException {
//1.获取数据库连接
Connection connection = JDBCUtils.getConnection();
//2.预编译
PreparedStatement ps = connection.prepareStatement(
"insert into student(sid,sname,sage) values(?,?,?)");
//3.给占位符赋值
for (int i = 1; i <= 100000 ; i++) {
ps.setInt(1,i);
ps.setString(2,"aaa"+i);
ps.setInt(3,i);
//添加到批处理中
ps.addBatch();
if (i % 1000 == 0){
//执行sql
ps.executeBatch();//执行批处理
//清空批处理
ps.clearBatch();
}
}
//4.关资源
JDBCUtils.close(ps,connection);
}
@Test
public void test() throws SQLException {
//1.获取数据库连接
Connection connection = JDBCUtils.getConnection();
//2.预编译
PreparedStatement ps = connection.prepareStatement(
"insert into student(sid,sname,sage) values(?,?,?)");
//3.给占位符赋值
for (int i = 1; i <= 100000 ; i++) {
ps.setInt(1,i);
ps.setString(2,"aaa"+i);
ps.setInt(3,i);
//执行sql
ps.executeUpdate();
}
//4.关资源
JDBCUtils.close(ps,connection);
}
}
到此这篇关于JDBC的扩展知识点总结的文章就介绍到这了,更多相关JDBC知识点内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!