【JavaEE学习笔记】JDBC_02_抽取jdbc工具类,事物,调用存储过程

本文介绍如何通过Java JDBC工具类简化数据库操作,并探讨事务处理及存储过程调用等高级主题。

JDBC_02

A.抽取jdbc工具类

1.概述

在我Java链接数据库中,需要一系列的步骤

但在开发中,不可能每次都要写这么长的步骤

所以,我们可以将这些步骤抽取到一个工具类中

这样在链接数据库时,直接调用即可,减少代码的复用性

2.工具类

工具类一旦写好,尽量避免改动

但我们每次链接的数据库不同,帐号密码也不同

因此我们需要建立一个配置文件,存放这些参数

只需要通过集合properties.load()方法

或者ResourceBundle工具(代码中解释)从文本中读取配置信息即可

当我们需要更改数据库配置信息,只需要更改配置文件即可

在src文件下创建一个配置文件jdbc.properties

url=jdbc:mysql://localhost:3306/mydb_01
user=root
password=root
driverClass=com.mysql.jdbc.Driver
工具类

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;

// 工具类
public class JDBCUtil {

	private static String url;
	private static String user;
	private static String password;
	private static String driverClass;

	// 静态代码块,随着类的加载而执行
	// 读取配置信息并加载驱动,只要调用类,就加载
	static {
		try {
			// 方式1 properties
			// 创建集合
			// Properties prop = new Properties();
			// 读取配置信息
			// prop.load(new FileInputStream("src//jdbc.properties"));
			// url = prop.getProperty("url");
			// user = prop.getProperty("user");
			// password = prop.getProperty("password");
			// 加载驱动
			// Class.forName(prop.getProperty("driverClass"));

			// 方式2 ResourceBundle工具
			// 如果一个文件在src目录下 而且键值对数据是以"="连接 并且文件后缀名是.properties
			// 1)获取对象
			ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); // 不带后缀名
			url = bundle.getString("url");
			user = bundle.getString("user");
			password = bundle.getString("password");
			driverClass = bundle.getString("driverClass");

			// 加载驱动
			Class.forName(driverClass);

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	// 不让外界创建对象,私有构造
	private JDBCUtil() {
		super();
	}

	// 获取连接
	public static Connection getConnection() throws SQLException {
		return DriverManager.getConnection(url, user, password);
	}

	// 释放资源
	public static void close(Connection conn, Statement statement, ResultSet resultSet) throws SQLException {
		// 非空判断
		if (resultSet != null) {
			resultSet.close();
		}

		if (statement != null) {
			statement.close();
		}

		if (conn != null) {
			conn.close();
		}
	}

	// 如果没有返回结果集,方法重载
	public static void close(Connection conn, Statement statement) throws SQLException {
		// 非空判断
		if (statement != null) {
			statement.close();
		}

		if (conn != null) {
			conn.close();
		}
	}

}
测试类

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

import org.xxxx.jdbc.utils.JDBCUtil;

// 测试类
public class JDBCDemo01 {
	public static void main(String[] args) throws SQLException {
		// 拷jar包
		// 获取链接对象
		Connection conn = JDBCUtil.getConnection();
		
		// 定义sql语句
		String sql = "select * from user where username=?";
		
		// 获取预编译对象
		PreparedStatement statement = conn.prepareStatement(sql);
		
		// 给占位符赋值
		statement.setString(1, "张三");
		
		// 执行语句
		ResultSet resultSet = statement.executeQuery();
		
		// 处理结果
		while (resultSet.next()) {
			int id = resultSet.getInt("id");
			String username = resultSet.getString("username");
			System.out.println(id + "---" + username);
		}
		
		// 释放资源
		JDBCUtil.close(conn, statement, resultSet);
	}
}

B.事物

1.模拟银行转账

现在数据库中创建一个表

CREATE TABLE bank(
	username VARCHAR(50),
	money INT
);

INSERT INTO bank VALUES('张三', 5000);
INSERT INTO bank VALUES('李四', 100);

SELECT * FROM bank;
现在需求是张三给李四转账1000

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

import org.xxxx.jdbc.utils.JDBCUtil;

public class Demo01 {
	public static void main(String[] args) {
		// 拷jar包
		
		Connection conn = null;
		PreparedStatement statement1 = null;
		PreparedStatement statement2 = null;
		try {
			// 建立连接
			conn = JDBCUtil.getConnection();
			
			// 定义sql语句
			String sql1 = "update bank set money=money-1000 where username=?";	// 账户转出
			String sql2 = "update bank set money=money+1000 where username=?";	// 账户转入
			
			// 获取预编译对象
			statement1 = conn.prepareStatement(sql1);
			statement2 = conn.prepareStatement(sql2);
			
			// 给?赋值
			statement1.setString(1, "张三");
			statement2.setString(1, "李四");
			
			// 执行语句
			statement1.executeUpdate();
			statement2.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			// 释放资源
			try {
				JDBCUtil.close(null, statement2);
				JDBCUtil.close(conn, statement1);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}
查看结果

现在制造一个异常来模拟:当张三将钱转出去,服务器发生异常,强制终止

给执行语句中间加一个异常

			// 执行语句
			statement1.executeUpdate();
			// 加一个异常
			System.out.println(1 / 0);
			statement2.executeUpdate();

会发现张三账户扣了钱,但李四没到帐

这种情况在银行是不允许的,因此引入事物概念

2.概述

a.概念

事务指一组最小逻辑操作单元,里面由多个操作组成

组成事务的每一部分必须要同时提交成功

如果有一个操作失败,整个操作就回滚

b.特性

1)原子性:是一个最小逻辑操作单元

指事务是一个不可分割的工作单位

事务中的操作要么都发生,要么都不发生

2)一致性:事务过程中,数据处于一致状态

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

3)隔离性:事务与事务之间是隔离的

多个用户并发访问数据库时,数据库为每一个用户开启的事务

不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离

4)持久性:事务一旦提交成功,对数据的更改会反映到数据库中

事务一旦被提交,它对数据库中数据的改变就是永久性的

接下来即使数据库发生故障也不应该对其有任何影响

3.事务回滚

针对上述银行账户问题,就要使用回滚来解决

Connection SetAutoCommit(boolean b):开启事物,默认为true,b=false则开启事物

Connection rollback():回滚事物到初始状态(当程序运行异常,张三李四账户余额不变)

Connection commit():提交事物

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

import org.xxxx.jdbc.utils.JDBCUtil;

public class Demo01 {
	public static void main(String[] args) {
		// 拷jar包
		
		Connection conn = null;
		PreparedStatement statement1 = null;
		PreparedStatement statement2 = null;
		try {
			// 建立连接
			conn = JDBCUtil.getConnection();
			
			// 开启事物
			conn.setAutoCommit(false);
			
			// 定义sql语句
			String sql1 = "update bank set money=money-1000 where username=?";	// 账户转出
			String sql2 = "update bank set money=money+1000 where username=?";	// 账户转入
			
			// 获取预编译对象
			statement1 = conn.prepareStatement(sql1);
			statement2 = conn.prepareStatement(sql2);
			
			// 给?赋值
			statement1.setString(1, "张三");
			statement2.setString(1, "李四");
			
			// 执行语句
			statement1.executeUpdate();
			// 加一个异常
			System.out.println(1 / 0);
			statement2.executeUpdate();
			
		} catch (Exception e) {
			// 一旦发生异常,回滚到最初状态
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		} finally {
			// 提交事物
			try {
				conn.commit();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			
			// 释放资源
			try {
				JDBCUtil.close(null, statement2);
				JDBCUtil.close(conn, statement1);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}
当程序发生异常,数据回滚到最初状态

4.事物回滚点

当程序中执行两次转账,第一次成功,第二次失败,回滚到初始状态

但我想第一转账成功保留,只会滚到第二次转账前的状态,怎么办?

需要设置一个回滚点

Connection setSavepoint():在两次之间设置一个回滚点

Connection rollback(Connection setSavepoint()):回滚到回滚点上

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

import org.xxxx.jdbc.utils.JDBCUtil;

public class Demo01 {
	public static void main(String[] args) {
		// 拷jar包

		Connection conn = null;
		PreparedStatement statement1 = null;
		PreparedStatement statement2 = null;
		Savepoint savepoint = null;
		try {
			// 建立连接
			conn = JDBCUtil.getConnection();

			// 开启事物
			conn.setAutoCommit(false);

			// 定义sql语句
			String sql1 = "update bank set money=money-1000 where username=?"; // 账户转出
			String sql2 = "update bank set money=money+1000 where username=?"; // 账户转入

			// 第一次转账
			// 获取预编译对象
			statement1 = conn.prepareStatement(sql1);
			statement2 = conn.prepareStatement(sql2);

			// 给?赋值
			statement1.setString(1, "张三");
			statement2.setString(1, "李四");

			// 执行语句
			statement1.executeUpdate();
			statement2.executeUpdate();

			// 设置回滚点
			savepoint = conn.setSavepoint();
			
			// 第二次转账
			// 执行语句
			statement1.executeUpdate();
			// 加一个异常
			System.out.println(1 / 0);
			statement2.executeUpdate();

		} catch (Exception e) {
			// 一旦发生异常,回滚到最初状态
			try {
				conn.rollback(savepoint);	// 回滚到指定回滚点
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		} finally {
			// 提交事物
			try {
				conn.commit();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}

			// 释放资源
			try {
				JDBCUtil.close(null, statement2);
				JDBCUtil.close(conn, statement1);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

第一次转账成功,第二次发生异常

在两次之间设置了一个回滚点,使得正确执行的部分得以保留

C.调用存储过程

1.概述

有时为了提高效率,需要执行存储过程语句

2.调用存储过程

Connection prepareCall(sql):创建存储过程对象

建立一个存储过程

DELIMITER $
CREATE PROCEDURE pro_bank()
BEGIN
	SELECT * FROM bank;
END $
调用存储过程
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.xxxx.jdbc.utils.JDBCUtil;

public class Demo01 {
	public static void main(String[] args) {
		// 拷jar包

		Connection conn = null;
		CallableStatement prepareCall = null;
		ResultSet resultSet = null;
		try {
			// 创建连接
			conn = JDBCUtil.getConnection();

			// 定义sql语句
			String sql = "call pro_bank";

			// 创建存储过程对象
			prepareCall = conn.prepareCall(sql);

			// 执行sql语句
			resultSet = prepareCall.executeQuery();
			
			// 查看结果
			while (resultSet.next()) {
				String username = resultSet.getString("username");
				int money = resultSet.getInt("money");
				System.out.println(username + "---" + money);
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			// 释放资源
			try {
				JDBCUtil.close(conn, prepareCall, resultSet);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值