Fork me on GitHub

JDBC编程之事务处理

-------------------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_passwordstu_info两张表不一致,本来小黄的stu_id005

* 其中 insertStuPassword() 中已经插入了小黄的stu_id stu_pwd

* insertStuInfo(),却因为输错了已经存在的 stu_id004 而无法插入

* (即 一个插进去了,一个没有插进去,导致数据不一致)

*/

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_passwordstu_info两张表不一致,本来小黄的stu_id005

* 其中 insertStuPassword() 中已经插入了小黄的stu_id stu_pwd

* insertStuInfo(),却因为输错了已经存在的 stu_id004 而无法插入

* (即 一个插进去了,一个没有插进去,此时,可使事务回滚,使之都不插入,保持一致性)

*/

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】

posted on 2017-04-11 21:08  siwuxie095  阅读(421)  评论(0编辑  收藏  举报

导航