事务
一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
对事务的操作
- 开启事务
- 提交事务
- 回滚事务
使用Connection对象来管理事务
java.sql.Connection接口是一个数据库连接对象。它与特定数据库的连接(会话)。 执行SQL语句并在连接的上下文中返回结果。
-
开启事务
setAutoCommit(boolean autoCommit) // 调用该方法设置参数为false,即开启事务
-
提交事务
commit() // 当所有sql都执行完提交事务
-
回滚事务
rollback() // 在catch中回滚事务
Java代码举例
有如下一个mysql数据表,利用Java程序:把id = 1对应的余额减少500,id = 2对应的余额增加500
CREATE TABLE account (
id INT PRIMARY KEY AUTO_INCREMENT, -- id
NAME VARCHAR(10), -- 名字
balance DOUBLE -- 余额
);
INSERT INTO account (NAME, balance) VALUES (\'LeeHua\', 1000), (\'Tom\', 1000);
自定义一个注解,获取连接数据库的信息:
package my.view.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE) // 注解能作用于类上
@Retention(RetentionPolicy.RUNTIME) // 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
public @interface PropertiesAnnotation {
/* URL */
public abstract String url();
/* 用户 */
public abstract String user();
/* 密码 */
public abstract String password();
/* 驱动包 */
public abstract String driver();
}
定义一个工具类,用来注册驱动和获取数据库连接对象:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@PropertiesAnnotation(
url = "jdbc:mysql:///Study", user = "账号", password = "密码", driver = "com.mysql.jdbc.Driver"
)
public class JdbcUtils02 {
private static String url;
private static String user;
private static String password;
private static String driver;
/* 文件的读取,只需要读取一次即可拿到这些值。利用反射和注解、使用静态代码块 */
static{
// 读取资源文件,获取值。
try {
// 1. 解析注解
// 1.1 获取JdbcUtils02类的字节码文件对象
Class<JdbcUtils02> jdbcUtils02Class = JdbcUtils02.class;
// 2. 获取上边的注解对象
PropertiesAnnotation annotation = jdbcUtils02Class.getAnnotation(PropertiesAnnotation.class);
// 3. 调用注解中定义的抽象方法,获取返回值,赋值给静态成员变量
url = annotation.url();
user = annotation.user();
password = annotation.password();
driver = annotation.driver();
// 4. 注册驱动
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
* @return 连接对象
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
}
有了以上条件,对数据表进行操作:
-
获取数据库连接
Connection connection = JdbcUtils02.getConnection();
-
获取到数据库连接对象后,开启事务
connection.setAutoCommit(false);
-
开启事务实际上是创建一个日志文件,将定义的SQL语句的执行结果暂时放到日记中,如果没有错,提交事务,如果出错,那么就回滚事务。接下来定义动态SQL语句
String sql1 = "update account set balance = balance - ? where id = ?"; String sql2 = "update account set balance = balance + ? where id = ?";
-
定义好了SQL语句,接下来就要获取执行动态SQL语句的对象
PreparedStatement preparedStatement1 = connection.prepareStatement(sql1); PreparedStatement preparedStatement2 = connection.prepareStatement(sql2);
-
给动态SQL语句传入参数
// LeeHua 的账户余额减少500元 preparedStatement1.setDouble(1,500); preparedStatement1.setInt(2,1); // Tom 的账户余额增加500元 preparedStatement2.setDouble(1,500); preparedStatement2.setInt(2,2);
-
一切准备就绪,这时候就可以执行SQL语句了
preparedStatement1.executeUpdate(); preparedStatement2.executeUpdate();
-
执行SQL语句后,如果没有错误,那么就提交事务,这时候的表记录就会更改
connection.commit();
-
执行SQL语句后,如果有错误,那么就回滚事务,这个时候,会把日记中的记录删除,不会提交到表中,表的记录不会更改
connection.rollback();
-
无论执行SQL语句,是否存在错误,最后都需要释放资源,调用自定义releaseResources()方法,释放资源
releaseResources(preparedStatement2); releaseResources(preparedStatement1); releaseResources(connection);
-
releaseResources()方法的定义如下:
/** * 释放资源 * @param t 要被释放的资源 * @param <T> 要被释放的资源对象的类型 */ public static <T> void releaseResources (T t){ if(t != null){ try { // 利用反射,获取class对象 Class<?> aClass = t.getClass(); // 获取class对象中的方法对象 Method close = aClass.getMethod("close"); // 执行方法 close.invoke(t); } catch (Exception e) { e.printStackTrace(); } } }
对数据表进行操作的实现代码如下:
package my.view.jdbc;
import my.view.util.JdbcUtils02;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JdbcDemo09 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement1 = null;
PreparedStatement preparedStatement2 = null;
try {
// 1. 获取连接
connection = JdbcUtils02.getConnection();
// 开启事务
connection.setAutoCommit(false);
// 2. 定义sql
// 2.1 定义减少账户余额的SQL语句
String sql1 = "update account set balance = balance - ? where id = ?";
// 2.2 定义增加账户余额的SQL语句
String sql2 = "update account set balance = balance + ? where id = ?";
// 3.获取执行SQL语句的对象
preparedStatement1 = connection.prepareStatement(sql1);
preparedStatement2 = connection.prepareStatement(sql2);
// 4. 设置参数
// 4.1 LeeHua 的账户余额减少500元
preparedStatement1.setDouble(1,500);
preparedStatement1.setInt(2,1);
// 4.2 Tom 的账户余额增加500元
preparedStatement2.setDouble(1,500);
preparedStatement2.setInt(2,2);
// 5. 执行SQL语句
preparedStatement1.executeUpdate();
preparedStatement2.executeUpdate();
// 6. 提交事务
connection.commit();
} catch (Exception e) {
// 7. 事务回滚
try {
if(connection != null) {
connection.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
// 8. 释放资源
releaseResources(preparedStatement2);
releaseResources(preparedStatement1);
releaseResources(connection);
}
}
/**
* 释放资源
* @param t 要被释放的资源
* @param <T> 要被释放的资源对象的类型
*/
public static <T> void releaseResources (T t){
if(t != null){
try {
// 利用反射,获取class对象
Class<?> aClass = t.getClass();
// 获取class对象中的方法对象
Method close = aClass.getMethod("close");
// 执行方法
close.invoke(t);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
运行程序,查看表中的记录,发现LeeHua的账号余额减少了500,Tom的账户余额增加了500。