JDBC加深之事务 ---- Java进阶篇

目录

 

事务的基本概念

事务ACID特性

案例

需求

数据库设计

Java代码

工具类

事务操作类 

测试类

实现方式二(工具不同)

db.properties

工具类

事务操作类


事务的基本概念

事物是指一组最小的逻辑操作单元,里面由多个操作组成。组成事务的所有操作必须都成功;如果有存在一个操作失败,则整个操作都失败(也叫事物回滚)

 

事务ACID特性

  • 原子性(Atomicity

原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 

  • 一致性(Consistency

事务必须使数据库从一个一致性状态变换到另外一个一致性状态

  • 隔离性(Isolation

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

  • 持久性(Durability

持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

 

案例

需求

需求: 张三给李四转账
设计: 账户表
技术:
 Connection
 void setAutoCommit(boolean autoCommit) ;  设置事务是否自动提交
								          如果设置为false,表示手动提交事务。

void commit();						      手动提交事务
void rollback() ;		 回滚(出现异常时候,所有已经执行成功的代码需要回退到事务开始前的状态。)
Savepoint setSavepoint(String name) 

 

数据库设计

/*建库*/
create database demo18
use demo18

/*账户表*/
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
accountName VARCHAR(20) NOT NULL,
money  DOUBLE
);

/*添加测试数据*/
INSERT INTO account(accountName,money) VALUES('张三','10000');
INSERT INTO account(accountName,money) VALUES('李四','10000');
select * from account /*查看测试数据是否添加成功*/


/*转账*/
UPDATE account SET money=money-1000 WHERE accountName='张三';
UPDATE account SET money=money+1000 WHERE accountName='李四';


Java代码

工具类

package cn.itcase.account;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/*工具类
 *Connection          连接对象
 *Statement           执行命令对象:把sql语句发送到数据库执行
 *ResultSet           (在线式)结果集接口,必须要保持与数据库的连接
 *PreparedStatement   预编译对象
 */
public class JdbcUtil1 {
	// static 修饰的成员属性 是共享数据 类加载时加载 类文件消失时消失
	private static String url = "jdbc:mysql://localhost:3306/demo18"; // 连接数据库
	// 等价上句代码 private staitc String url ="jdbc:mysql:///demo17";
	private static String user = "root"; // 数据库的登录用户名
	private static String password = "root"; // 数据库的登录密码

	/**
	 * 获取连接的方法封装
	 */
	public static Connection getConnection() {

		try {
			// 加载驱动
			Class.forName("com.mysql.jdbc.Driver");
			return DriverManager.getConnection(url, user, password);
		} catch (Exception e) {
			throw new RuntimeException();
		}

	}

	/**
	 * 释放资源的方法封装
	 * 
	 * 
	 */
	public static void closeAll(Connection conn, PreparedStatement pstmt, ResultSet rs) {
		try {
			if (rs != null) {
				rs.close();
				rs = null;
			}
			if (pstmt != null) {
				pstmt.close();
				pstmt = null;
			}
			if (conn != null && conn.isClosed()) {
				conn.close();
				conn = null;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}
}

事务操作类 

package cn.itcase.account;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

public class AccountDao {

	// 全局参数
	private Connection con;
	private PreparedStatement pstmt;

	// 1. 转账,没有使用事务
	public void trans1() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JdbcUtil1.getConnection(); // 默认开启的隐士事务
			con.setAutoCommit(true);

			/*** 第一次执行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次执行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JdbcUtil1.closeAll(con, pstmt, null);
		}

	}

	// 2. 转账,使用事务
	public void trans2() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JdbcUtil1.getConnection(); // 默认开启的隐士事务
			// 一、设置事务为手动提交
			con.setAutoCommit(false);

			/*** 第一次执行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次执行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			try {
				// 二、 出现异常,需要回滚事务
				con.rollback();
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 三、所有的操作执行成功, 提交事务
				con.commit();
				JdbcUtil1.closeAll(con, pstmt, null);
			} catch (SQLException e) {
			}
		}

	}

	// 3. 转账,使用事务, 回滚到指定的代码段
	public void trans() {
		// 定义个标记
		Savepoint sp = null;
		
		// 第一次转账
		String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
		
		// 第二次转账
		String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='张三';";
		String sql_ls2 = "UPDATE account SET money=money+500 WHERE accountName='李四';";

		try {
			con = JdbcUtil1.getConnection(); // 默认开启的隐士事务
			con.setAutoCommit(false);       // 设置事务手动提交

			/*** 第一次转账 ***/
			pstmt = con.prepareStatement(sql_zs1);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls1);
			pstmt.executeUpdate();
			
			// 回滚到这个位置?
			sp = con.setSavepoint(); 
			
			
			/*** 第二次转账 ***/
			pstmt = con.prepareStatement(sql_zs2);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls2);
			pstmt.executeUpdate();
			

		} catch (Exception e) {
			try {
				// 回滚 (回滚到指定的代码段)
				con.rollback(sp);
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 提交
				con.commit();
			} catch (SQLException e) {
			}
			JdbcUtil1.closeAll(con, pstmt, null);
		}

	}

}

测试类

package cn.itcase.account;

import org.junit.Before;
import org.junit.Test;

/**
 * 测试类
 * @author Administrator
 *
 */

public class Accounttest {
	/**
	 * 初始化对象
	 */
	AccountDao ad = null;
	@Before
	public void init(){
		 ad = new AccountDao();
	}
	@Test
	public void trantest(){
		ad = new AccountDao();
		ad.trans2();
	}

}

实现方式二(工具不同)

db.properties

url=jdbc:mysql://localhost:3306/demo18
user=root
password=root
driverClass=com.mysql.jdbc.Driver

工具类

package cn.itcase.account;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
 * 工具类
 * @author Administrator
 *Connection     连接对象
 *Statement      执行命令对象:把sql语句发送到数据库执行
 *ResultSet      (在线式)结果集接口,必须要保持与数据库的连接
 *PreparedStatement   预编译对象
 *
 */
public class JDBCUtil {
	private static String url = null;
	private static String user = null;
	private static String password = null;
	private static String driverClass = null;

	/**
	 * 加载配置 静态代码块中(只加载一次) 随类的创建而加载
	 */
	static {
		try {
			// 读取db.properties文件
			Properties props = new Properties();
			InputStream in = JDBCUtil.class
					.getResourceAsStream("/db.properties");// 类加载器加载文件

			// 加载文件
			props.load(in);
			// 读取信息
			url = props.getProperty("url");
			user = props.getProperty("user");
			password = props.getProperty("password");
			driverClass = props.getProperty("driverClass");

			// 1.加载驱动程序
			Class.forName(driverClass);
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("驱程程序注册出错");
		}
	}

	/**
	 * 抽取获取连接对象的方法
	 */
	public static Connection getConnection() {
		// 2.创建连接
		try {
			Connection conn = DriverManager.getConnection(url, user, password);
			return conn;
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

	/**
	 * 释放资源的方法 3.释放资源 后打开先关闭
	 * 
	 * 方法重载
	 */
	public static void close(Connection conn, Statement stmt) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
	}


	public static void close(Connection conn, Statement stmt, ResultSet rs) {
		if (rs != null)
			try {
				rs.close();
			} catch (SQLException e1) {
				e1.printStackTrace();
				throw new RuntimeException(e1);
			}
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}

	}

	public static void close(Connection conn, PreparedStatement pstmt) {
		if (pstmt != null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
	}
}

/*
 * 
 * 构造代码:类中直接用{}定义,每一次创建对象时执行。静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别
 * 
 * 静态代码块 :用staitc声明,jvm加载类时执行,仅执行一次静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的
 * 
 * 执行顺序优先级:静态块,main(),构造块,构造方法。
 */

事务操作类

package cn.itcase.account;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

public class AccountDao {

	// 全局参数
	private Connection con;
	private PreparedStatement pstmt;

	// 1. 转账,没有使用事务
	public void trans1() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JDBCUtil.getConnection(); // 默认开启的隐士事务
			con.setAutoCommit(true);

			/*** 第一次执行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次执行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(con, pstmt);
		}

	}

	// 2. 转账,使用事务
	public void trans2() {

		String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName='李四';";

		try {
			con = JDBCUtil.getConnection(); // 默认开启的隐士事务
			// 一、设置事务为手动提交
			con.setAutoCommit(false);

			/*** 第一次执行SQL ***/
			pstmt = con.prepareStatement(sql_zs);
			pstmt.executeUpdate();

			/*** 第二次执行SQL ***/
			pstmt = con.prepareStatement(sql_ls);
			pstmt.executeUpdate();

		} catch (Exception e) {
			try {
				// 二、 出现异常,需要回滚事务
				con.rollback();
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 三、所有的操作执行成功, 提交事务
				con.commit();
				JDBCUtil.close(con, pstmt);
			} catch (SQLException e) {
			}
		}

	}

	// 3. 转账,使用事务, 回滚到指定的代码段
	public void trans() {
		// 定义个标记
		Savepoint sp = null;
		
		// 第一次转账
		String sql_zs1 = "UPDATE account SET money=money-1000 WHERE accountName='张三';";
		String sql_ls1 = "UPDATE account SET money=money+1000 WHERE accountName='李四';";
		
		// 第二次转账
		String sql_zs2 = "UPDATE account SET money=money-500 WHERE accountName='张三';";
		String sql_ls2 = "UPDATE account SET money=money+500 WHERE accountName='李四';";

		try {
			con = JDBCUtil.getConnection(); // 默认开启的隐士事务
			con.setAutoCommit(false);       // 设置事务手动提交

			/*** 第一次转账 ***/
			pstmt = con.prepareStatement(sql_zs1);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls1);
			pstmt.executeUpdate();
			
			// 回滚到这个位置?
			sp = con.setSavepoint(); 
			
			
			/*** 第二次转账 ***/
			pstmt = con.prepareStatement(sql_zs2);
			pstmt.executeUpdate();
			pstmt = con.prepareStatement(sql_ls2);
			pstmt.executeUpdate();
			

		} catch (Exception e) {
			try {
				// 回滚 (回滚到指定的代码段)
				con.rollback(sp);
			} catch (SQLException e1) {
			}
			e.printStackTrace();
		} finally {
			try {
				// 提交
				con.commit();
			} catch (SQLException e) {
			}
			JDBCUtil.close(con, pstmt);
		}

	}

}

 

 

你可能感兴趣的:(JDBC加深之事务 ---- Java进阶篇)