事务(理解)与数据库连接池
一、什么是事务
转账的功能,冠希给美美转 1000 元钱。使用事务先给冠希扣除掉 1000 元再给美美加上 1000 元事务结束了
二、事物的特性
- 原子性(Atomicity):事务中的操作是一个不可分割的整体,要么全部成功,要么全部失败。例如,在银行转账中,扣款和入账必须同时成功,否则就回滚到初始状态。
- 一致性(Consistency):事务执行前后,数据库都必须处于合法的状态。比如,转账前后,双方账户的总金额应该保持不变。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不能被其他事务干扰,每个事务都感觉像是在独立执行。
- 持久性(Durability):一旦事务提交成功,它对数据库的修改就会永久保存,即使系统发生故障也不会丢失。
三、客户端进行事务操作
第一种方式—开启事务
开启事务
执行转账操作,aaa付给bbb100
UPDATE account SET money = money - 100 where name = "aaa";
UPDATE account SET money = money + 100 where name = "bbb";
执行回滚操作
可以看到数据回滚成功
回滚或者提交都代表事务已经结束,所以再次执行相同的操作,回滚不会生效!
第二种方式—设置不默认提交
多次执行转账操作
多次执行回滚操作发现都可以生效
由此可知设置自动提交为off,执行回滚或者提交,事务不会结束
四、使用JDBC进行事务操作
jdbc进行事务操作只有一种方式——设置不默认提交
调用connection的setAutoCommit方法,设置autoCommit为false
import java.sql.*;
public class TransactionExample {public static void main(String[] args) {try (Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring_db", "root", "2762089616..")) {// 关闭自动提交connection.setAutoCommit(false);try (Statement statement = connection.createStatement()) {// 执行多个SQL语句statement.executeUpdate("UPDATE account SET money = money - 100 where name = 'aaa'");statement.executeUpdate("UPDATE account SET money = money + 100 where name = 'bbb'");// 提交事务connection.commit();} catch (SQLException e) {// 发生异常时回滚事务connection.rollback();e.printStackTrace();}} catch (SQLException e) {e.printStackTrace();}}
}
五、JDBC操作数据库
JDBC操作数据库的步骤
jdbc操作数据库需要经历以下步骤:
1、加载数据库驱动
在使用 JDBC 操作数据库之前,首先需要加载对应的数据库驱动。不同的数据库厂商提供了各自的驱动程序,例如:
- MySQL:com.mysql.cj.jdbc.Driver
- Oracle:oracle.jdbc.driver.OracleDriver
- SQL Server:com.microsoft.sqlserver.jdbc.SQLServerDriver
加载驱动的方式有两种:
1.1 使用Class.forName()方法(反射方式)
try {Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {e.printStackTrace();
}
1.2 直接创建
try {// 直接实例化MySQL驱动(MySQL 8.x及以上版本)new com.mysql.cj.jdbc.Driver();// 或者直接实例化Oracle驱动// new oracle.jdbc.driver.OracleDriver();System.out.println("驱动加载成功!");
} catch (Exception e) {System.err.println("驱动加载失败:" + e.getMessage());
}
2、建立数据库连接
加载驱动后,需要建立与数据库的连接。通过DriverManager.getConnection()方法可以获取一个Connection对象,该对象代表与数据库的连接。连接数据库时需要提供数据库的 URL、用户名和密码等信息。
Connection connection = DriverManager.getConnection(url, username, password));
3、创建Statement对象
建立连接后,需要创建Statement对象(或其子类PreparedStatement、CallableStatement)来执行 SQL 语句。
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {preparedStatement.setString(1, "John");preparedStatement.setInt(2, 30);}
4、执行sql语句,返回结果集
根据不同的需求,使用Statement对象执行相应的 SQL 语句
ResultSet resultSet = statement.executeQuery(sql)) ;
5、遍历结果集然后输出
while(resultSet.next()){Account account =new Account(resultSet.getInt("id"), resultSet.getString("name"), resultSet.getString("money"));System.out.println(account);
}
6、关闭资源
在完成数据库操作后,需要关闭相关资源,包括ResultSet、Statement和Connection对象,关闭资源时遵守先开后闭规则
if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}
}
if(stmt!=null) {try {tmt.close();} catch (SQLException e) {e.printStackTrace();}
}
if(conn!=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}
}
在执行不同的sql语句时我们发现代码有共同部分重复编写,为了简化代码,提高耦合度,我们不妨把原有代码的共同部分进行提取,写入工具类中
JDBCUtil 1.0版本
该工具类将共有的加载驱动、创建Connection连接和关闭资源进行封装
package com.qcby.util;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;public class JdbcUtil {public static void createDriver() throws ClassNotFoundException {Class.forName("com.mysql.jdbc.Driver");}public static Connection getConnection() throws ClassNotFoundException, SQLException {createDriver();Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring_db", "root", "2762089616..");return connection;}public static void close(Connection conn, Statement stmt, ResultSet rs){if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if(stmt!=null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn!=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}public static void close(Connection conn, Statement stmt){close(conn, stmt, null);}public static void close(Connection conn) {close(conn, null, null);}
}
JDBCUtil 2.0版本
2.0版本中,数据库连接信息(URL、USER、PASSWORD)是代码中固定的。2.0版本将数据库连接信息(URL、USER、PASSWORD)通过静态代码块从jdbc.properties
文件中加载得到
package com.qcby.util;import com.alibaba.druid.pool.DruidDataSource;import java.io.IOException;
import java.sql.*;
import java.util.Properties;public class JdbcUtil {private static String URL = null;private static String USER = null;private static String PASSWORD = null;static {Properties properties = new Properties();try {properties.load(JdbcUtil.class.getResourceAsStream("/jdbc.properties"));URL = properties.getProperty("url");USER = properties.getProperty("username");PASSWORD = properties.getProperty("password");} catch (IOException e) {e.printStackTrace();}}public static void createDriver() throws ClassNotFoundException {Class.forName("com.mysql.jdbc.Driver");}public static Connection getConnection() throws ClassNotFoundException, SQLException {createDriver();Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);return connection;}public static void close(Connection conn, Statement stmt, ResultSet rs){if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if(stmt!=null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if(conn!=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}public static void close(Connection conn, Statement stmt){close(conn, stmt, null);}public static void close(Connection conn) {close(conn, null, null);}
}
JDBCUtil 3.0版本
2.0版本中,数据库连接信息(URL、USER、PASSWORD)是通过静态代码块从jdbc.properties
文件中加载的。3.0版本在此基础上引入了Druid连接池,以提高数据库连接的效率和性能。
package com.qcby.util;import com.alibaba.druid.pool.DruidDataSource;import java.sql.Connection;
import java.sql.SQLException;public class DruidUtil {private static String URL = null;private static String USER = null;private static String PASSWORD = null;private static DruidDataSource druidDataSource = null;private static Connection connection = null;private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();static {druidDataSource = new DruidDataSource();druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");druidDataSource.setUrl("jdbc:mysql://localhost:3306/spring_db");druidDataSource.setUsername("root");druidDataSource.setPassword("2762089616..");druidDataSource.setInitialSize(1);druidDataSource.setMaxActive(10);druidDataSource.setMaxWait(3000);}public static DruidDataSource getDataSource() {return druidDataSource;}public static Connection getConnection() throws SQLException {if(connection == null || connection.isClosed()){connection = getDataSource().getConnection();}threadLocal.set(connection);return connection;}public static void closeConnection() throws SQLException {if(connection!= null){threadLocal.remove();connection.close();}}}