JDBC 控制事务(MySQL为例)
事务
一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
对事务的操作
开启事务
提交事务
回滚事务
使用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。
相关推荐
Andrea0 2020-09-18
gaozhennan 2020-08-03
mcvsyy 2020-08-02
msmysql 2020-06-21
bluetears 2020-06-17
dongtiandeyu 2020-08-18
大黑牛 2020-08-15
ASoc 2020-11-14
Cherishyuu 2020-08-19
CoderYYN 2020-08-16
Dullonjiang 2020-08-11
zbcaicai 2020-07-29
AscaryBird 2020-07-27
liulin0 2020-07-26
ldcwang 2020-07-26
helloxusir 2020-07-25
娜娜 2020-07-20
pengpengflyjhp 2020-07-19
点滴技术生活 2020-07-19