您的位置:首页 > 数据库

JDBC应用程序连接数据库--事务处理

2016-06-02 22:01 435 查看

JDBC应用程序连接数据库–事务处理

JDBC应用程序连接数据库事务处理
事务概念

问题引出

解决方案
用到的API

API使用方法

总结

事务概念

事务是指一组最小逻辑操作单元,里面有多个操作组成,组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就需要回滚,事务要保证ACID特性。这在数据库中已经涉及到,不在编程中再次强调。我们的任务是使用Java应用程序去模拟操作数据库中的事务处理,本文将详述该问题。

问题引出

首先解决这个问题的所有的API均在java.sql.Connection中,要注意对这个核心API的学习。

情景创建,模拟转账过程,张三给李四转账1000元,那么要求张三的钱要减1000,而李四的钱要加1000,其SQL语句为:

CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
accountName VARCHAR(20),
money DOUBLE
);

UPDATE account SET money=money-1000 WHERE accountName="zs";

UPDATE account SET money=money+1000 WHERE accountName="ls";


实现该过程的Java程序如下:

package com.jpzhutech.transaction;

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

import org.junit.Test;
import org.junit.runners.ParentRunner;

public class transaction {

@SuppressWarnings("resource")
@Test
public void test(){
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName=?";
String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName=?";
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Class.forName("com.mysql.jdbc.Driver");

try {
String url = "jdbc:mysql://192.168.101.44/amon";
String user = "root";
String password = "560128";

connection = DriverManager.getConnection(url, user, password);
//System.out.println(connection);
// 执行sql_zs

//实际上当不写时相当于下面的这行代码自动执行
//connection.setAutoCommit(true);

preparedStatement = connection.prepareStatement(sql_zs);
preparedStatement.setString(1, "zs");
int count_zs = preparedStatement.executeUpdate();
System.out.println("影响了"+count_zs+"行");

// 执行sql_ls
preparedStatement = connection.prepareStatement(sql_ls);
preparedStatement.setString(1, "ls");
int count_ls = preparedStatement.executeUpdate();
System.out.println("影响了"+count_ls+"行");

} catch (SQLException e) {
throw new RuntimeException(e);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
try {
preparedStatement.close();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}


分析这段代码我们知道,如果在我的代码中有一个部分写错,比如ls的SQL语句写错没有正确的执行,那么会发生的情况是张三的钱少了1000元,但是李四的钱并没有增加,看下面的代码:

package com.jpzhutech.transaction;

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

import org.junit.Test;
import org.junit.runners.ParentRunner;

public class transaction {

@SuppressWarnings("resource")
@Test
public void test(){
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName=?";
//明显我们可以看出李四的这段更新代码存在很大的问题,并不会被执行,但是张三的代码会被执行,结果只能是张三的钱继续少1000元,而李四的钱并没有增加
String sql_ls = "UPDATE1 account SET money=money+1000 WHERE accountName=?";
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Class.forName("com.mysql.jdbc.Driver");

try {
String url = "jdbc:mysql://192.168.101.44/amon";
String user = "root";
String password = "560128";

connection = DriverManager.getConnection(url, user, password);
//System.out.println(connection);
// 执行sql_zs

//实际上当不写时相当于下面的这行代码自动执行
//connection.setAutoCommit(true);

preparedStatement = connection.prepareStatement(sql_zs);
preparedStatement.setString(1, "zs");
int count_zs = preparedStatement.executeUpdate();
System.out.println("影响了"+count_zs+"行");

// 执行sql_ls
preparedStatement = connection.prepareStatement(sql_ls);
preparedStatement.setString(1, "ls");
int count_ls = preparedStatement.executeUpdate();
System.out.println("影响了"+count_ls+"行");

} catch (SQLException e) {
throw new RuntimeException(e);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
try {
preparedStatement.close();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}


从上面的两段代码的对比我们可以知道,在我们写SQL语句或者在其它的情况下可能会发生某个应该同时执行的语句并没有做到我们想要的效果,那么此时就会发生错误,程序员应该避免这种情况的发生,怎么避免呢?在数据库中使用事务的ACID特性能够解决这种问题,下面将详细的说明该怎么解决。

解决方案

使用事务的ACID特性能够避免此种情况的发生,但是用Java应用程序该怎么做到呢?是否Java提供了这种机制呢?答案是肯定的,Java确实提供了这种机制,在Connection接口中有几个方法能够实现该种功能。

[b]用到的API[/b]

Connection中存在下面几个与事务相关的方法,分别是:

void commit()

void rollback()

void rollback(Savepoint savepoint)

void setAutoCommit(boolean autoCommit)

Savepoint setSavepoint(String name)

[b]API使用方法[/b]

package com.jpzhutech.transaction;

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

import org.junit.Test;
import org.junit.runners.ParentRunner;

public class transaction {

@Test
public void test1(){
String sql_zs = "UPDATE account SET money=money-1000 WHERE accountName=?";
String sql_ls = "UPDATE account SET money=money+1000 WHERE accountName=?";
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Class.forName("com.mysql.jdbc.Driver");

try {
String url = "jdbc:mysql://192.168.101.44/amon";
String user = "root";
String password = "560128";

connection = DriverManager.getConnection(url, user, password);
//System.out.println(connection);
// 执行sql_zs

//实际上当不写时相当于下面的这行代码自动执行
//connection.setAutoCommit(true);

//一 设置事务手动提交,也就是将自动提交关闭
connection.setAutoCommit(false);

preparedStatement = connection.prepareStatement(sql_zs);
preparedStatement.setString(1, "zs");
int count_zs = preparedStatement.executeUpdate();
System.out.println("影响了"+count_zs+"行");

// 执行sql_ls
preparedStatement = connection.prepareStatement(sql_ls);
preparedStatement.setString(1, "ls");
int count_ls = preparedStatement.executeUpdate();
System.out.println("影响了"+count_ls+"行");

} catch (SQLException e) {
try {
//二 出现异常我希望回滚
connection.rollback();
} catch (SQLException e1) {
throw new RuntimeException(e);
}
throw new RuntimeException(e);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {

//三 如果所有的操作执行成功
try {
connection.commit();
} catch (SQLException e1) {

}
try {
preparedStatement.close();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

@SuppressWarnings("resource")
@Test
public void test2(){
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=?";

Connection connection = null;
PreparedStatement preparedStatement = null;
Savepoint savepoint = null;
try {
Class.forName("com.mysql.jdbc.Driver");

try {
String url = "jdbc:mysql://192.168.101.44/amon";
String user = "root";
String password = "560128";

connection = DriverManager.getConnection(url, user, password);
//System.out.println(connection);
// 执行sql_zs

//实际上当不写时相当于下面的这行代码自动执行
//connection.setAutoCommit(true);

//一 设置事务手动提交,也就是将自动提交关闭
connection.setAutoCommit(false);

//第一次操作(此次操作是正确的)
preparedStatement = connection.prepareStatement(sql_zs1);
preparedStatement.setString(1, "zs");
preparedStatement.executeUpdate();

// 执行sql_ls
preparedStatement = connection.prepareStatement(sql_ls1);
preparedStatement.setString(1, "ls");
preparedStatement.executeUpdate();

//设置回滚位置
savepoint = connection.setSavepoint("transaction");

//第二次操作(此次操作是错误的)
preparedStatement = connection.prepareStatement(sql_zs2);
preparedStatement.setString(1, "zs");
preparedStatement.executeUpdate();

// 执行sql_ls
preparedStatement = connection.prepareStatement(sql_ls2);
preparedStatement.setString(1, "ls");
preparedStatement.executeUpdate();

} catch (SQLException e) {
try {
//二 出现异常我希望回滚
connection.rollback(savepoint);
} catch (SQLException e1) {
throw new RuntimeException(e);
}
throw new RuntimeException(e);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {

//三 如果所有的操作执行成功
try {
connection.commit();
} catch (SQLException e1) {

}
try {
preparedStatement.close();
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}


总结

究竟什么时候会用到事务编程,依具体情况而定,请读者自己衡量,为了不失完整性我将此部分也全部进行了讲解,后续部分会主键增加关于JDBC优化的知识。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: