-------------------siwuxie095
JDBC 编程之事务处理
数据库是一个多用户使用的共享资源,当多个用户并发的存取数据时,数据库
中就会可能发生多个用户同时存取同一数据的情况
若对并发操作不加控制,就可能会产生和读取不正确的数据,破坏数据的一致性
而事务正是并发控制的基本单位。所谓事务,是一个操作序列,这些操作要么都
执行,要么都不执行,是一个不可分割的工作单位
事务也是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据的一致性
在编程中,可以把数据库事务看做是一组 SQL 语句,这组 SQL 语句是一个逻辑工作
单元,它们是不可分割的,其执行结果应该作为一个整体,永久性的修改数据库内容,
或 作为一个整体,取消对数据库的修改
事务的四个基本特征
(1)原子性
事务中包含的操作,看做是一个逻辑单元,这个逻辑单元中的操作
要么全部成功,要么全部失败
这也意味着事务中的所有元素,作为一个整体 提交 或 回滚
「事务的所有元素是不可分割的,是一个完整的操作」
(2)一致性
事务开始之前,和事务结束以后,数据库都处于一致性状态,数据库
的完整性约束,没有被破坏
(3)隔离性
对数据库进行修改的多个事务,是彼此隔离的
即 事务必须是独立的,不应该以任何形式影响其他事务
(4)持久性
事务完成之后,对于系统的影响是永久的,该修改真实的修改了
数据库,即使系统出现故障,也会一直保留
与事务相关的 SQL 语句:
开始事务:BEGIN TRANSACTION
提交事务:COMMIT TRANSACTION
回滚事务:ROLLBACK TRANSACTION
程序示例:
首先下载 MySQL 的 JDBC 驱动,下载链接:
https://dev.mysql.com/downloads/connector/j/
mysql-connector-java-5.1.41.zip 解压后一览:
工程名:JDBCTest
包名:com.siwuxie095.jdbc
类名:TransactionTest.java、TransactionTestX.java
打开资源管理器,在工程 JDBCTest 文件夹下,创建一个文件夹:lib,
在其中放入:mysql-connector-java-5.1.41-bin.jar
工程结构目录如下:
选择 mysql-connector-java-5.1.41-bin.jar,右键->Build Path->Add to Build Path
此时,工程结构目录一览:
TransactionTest.java:
package com.siwuxie095.jdbc;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement;
//实际上将数据库信息硬编码到java代码中,不可取 public class TransactionTest {
public static Connection getConnection() {
Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver"); conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/sims_db","root","8888");
} catch (Exception e) { e.printStackTrace(); System.err.println("加载数据库失败..."); } return conn; }
public static void insertStuPassword() {
Connection conn=getConnection();
try {
String sql="insert into stu_password(stu_id,stu_pwd)"+ "values('005','005')";
Statement st=conn.createStatement(); int count=st.executeUpdate(sql); System.out.println("向stu_password表中插入了 "+count+" 条记录");
} catch (Exception e) { e.printStackTrace(); } }
public static void insertStuInfo() {
Connection conn=getConnection();
try { /** * stu_id是主键,出现异常: * 即stu_password和stu_info两张表不一致,本来小黄的stu_id是005 * 其中 insertStuPassword() 中已经插入了小黄的stu_id 和 stu_pwd * 而 insertStuInfo(),却因为输错了已经存在的 stu_id:004 而无法插入 * (即 一个插进去了,一个没有插进去,导致数据不一致) */ String sql="insert into stu_info(stuid,stu_name,stu_sex,stu_scademic,stu_major)"+ "values('004','小黄','男','工程学院','土木工程')";
Statement st=conn.createStatement(); int count=st.executeUpdate(sql); System.out.println("向stu_info表中插入了 "+count+" 条记录");
} catch (Exception e) { e.printStackTrace(); } }
public static void main(String[] args) { insertStuPassword(); insertStuInfo(); }
} |
TransactionTestX.java:
package com.siwuxie095.jdbc;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement;
//实际上将数据库信息硬编码到java代码中,不可取 public class TransactionTestX {
public static Connection getConnection() {
Connection conn=null;
try {
Class.forName("com.mysql.jdbc.Driver"); conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/sims_db","root","8888");
} catch (Exception e) { e.printStackTrace(); System.err.println("加载数据库失败..."); } return conn; }
//将SQLException抛给上层调用方法进行处理 public static void insertStuPassword(Connection conn) throws SQLException {
String sql = "insert into stu_password(stu_id,stu_pwd)" + "values('005','005')";
Statement st = conn.createStatement(); int count = st.executeUpdate(sql); System.out.println("向stu_password表中插入了 " + count + " 条记录");
}
public static void insertStuInfo(Connection conn) throws SQLException {
/** * stu_id是主键,出现异常: * 即stu_password和stu_info两张表不一致,本来小黄的stu_id是005 * 其中 insertStuPassword() 中已经插入了小黄的stu_id 和 stu_pwd * 而 insertStuInfo(),却因为输错了已经存在的 stu_id:004 而无法插入 * (即 一个插进去了,一个没有插进去,此时,可使事务回滚,使之都不插入,保持一致性) */ String sql = "insert into stu_info(stu_id,stu_name,stu_sex,stu_academic,stu_major)" + "values('004','小黄','男','工程学院','土木工程')";
Statement st = conn.createStatement(); int count = st.executeUpdate(sql); System.out.println("向stu_info表中插入了 " + count + " 条记录");
}
//在main方法中进行事务回滚 public static void main(String[] args) {
Connection conn=null;
try {
conn=getConnection(); conn.setAutoCommit(false);//禁止事务自动提交
insertStuPassword(conn); insertStuInfo(conn);
conn.commit();//提交事务
} catch (SQLException e) {
System.out.println("=======捕获到 SQL 异常======="); e.printStackTrace();
try {
conn.rollback();//回滚事务 System.out.println("=======事务回滚成功======");
} catch (Exception e2) { e2.printStackTrace(); } }finally { try { if (conn!=null) { conn.close(); } } catch (Exception e3) { e3.printStackTrace(); } } }
} |
对比 TransactionTest.java 和 TransactionTestX.java:
「事务处理使数据库的一致性没有被破坏」
注意:高版本的 JDBC 驱动需要指明是否进行 SSL 连接
即 加上:?characterEncoding=utf8&useSSL=false
或:
即 加上:?useUnicode=true&characterEncoding=utf-8&useSSL=false
总结 JDBC 编程流程:
(1)加载驱动:加载 JDBC 驱动程序
(2)打开连接:打开一个数据库连接
(3)执行查询:创建一个会话对象,执行增删改查等操作
(4)处理结果:处理查询的结果
(5)清理环境:关闭会话,关闭连接等操作,完成资源的清理工作
关于 数据库的准备,详见本人博客的分类:来一杯Java,
里面的 JDBC编程之数据准备
本人博客(任选其一)链接:
https://www.baidu.com/s?ie=UTF-8&wd=siwuxie095
【made by siwuxie095】