什么是事务:
数据库事务是指作为单个逻辑工作单元执行的一系列操作(SQL语句)。这些操作要么全部执行,要么全部不执行。
为什么需要事务
经典的银行转账行为,A账户转给B账户10元,数据库操作需要两步,第一步A账户减10元,第二步B账户加10元,如果没有事务并且在两步中间发生异常,就会导致A的账户少了10元,但B的账户没有变化,如果不能保证这两步操作统一,银行的转账业务也没法进行展开了。
事务管理是每个数据库(oracle、mysql、db等)都必须实现的。
事务特性(4种):
事务运行模式(3种)
下面做一个事务的示例:
JDBC用了Druid连接池
1、maven引包
mysql
mysql-connector-java
5.1.47
com.alibaba
druid
1.1.10
2、数据库设计
CREATE TABLE `account` (
`userid` varchar(64) NOT NULL,
`username` varchar(64) NOT NULL,
`accountbalance` decimal(10,2) NOT NULL DEFAULT '0.00',
`createtime` datetime DEFAULT NULL,
`updatetime` datetime DEFAULT NULL,
PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `account` VALUES ('1001', '张三', '1000.00', '2018-11-09 09:39:52', '2018-11-09 09:39:55');
INSERT INTO `account` VALUES ('1002', '李四', '1000.00', '2018-11-09 09:40:12', '2018-11-09 09:40:14');
3、建立jdbc.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/zhd?useUnicode=true&characterEncoding=UTF-8&InnoDB=true&useSSL=false
username=root
password=123456
4、建立连接池工具类DBPoolConnection
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.DruidPooledConnection;
public class DBPoolConnection {
static Logger log = LoggerFactory.getLogger(DBPoolConnection.class);
private static DBPoolConnection dbPoolConnection = null;
private static DruidDataSource druidDataSource = null;
static {
Properties properties = loadPropertiesFile("jdbc.properties");
try {
druidDataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); // DruidDataSrouce工厂模式
} catch (Exception e) {
log.error("获取配置失败");
}
}
/**
* @param string 配置文件名
* @return Properties对象
*/
private static Properties loadPropertiesFile(String fullFile) {
if (null == fullFile || fullFile.equals("")) {
throw new IllegalArgumentException("Properties file path can not be null" + fullFile);
}
InputStream inputStream = null;
Properties p = null;
try {
ClassLoader cl = DBPoolConnection.class.getClassLoader();
inputStream = cl.getResourceAsStream(fullFile);
p = new Properties();
p.load(inputStream);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != inputStream) {
inputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return p;
}
/**
* 数据库连接池单例
*
* @return
*/
public static synchronized DBPoolConnection getInstance() {
if (null == dbPoolConnection) {
dbPoolConnection = new DBPoolConnection();
}
return dbPoolConnection;
}
/**
* 返回druid数据库连接
*
* @return
* @throws SQLException
*/
public DruidPooledConnection getConnection() throws SQLException {
return druidDataSource.getConnection();
}
}
5、建立JDBCUtil工具类
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 对jdbc的完整封装
*
*/
public class JDBCUtil {
public static Logger log = LoggerFactory.getLogger(JDBCUtil.class);
/**
* insert update delete SQL语句的执行的统一方法
*
* @param sql SQL语句
* @param params 参数数组,若没有参数则为null
* @return 受影响的行数
*/
public int executeUpdate(String sql, Object... params) {
// 受影响的行数
int affectedLine = 0;
// 创建ResultSetMetaData对象
Connection conn = null;
PreparedStatement pst = null;
try {
// 获得连接
conn = DBPoolConnection.getInstance().getConnection();
// 调用SQL
pst = conn.prepareStatement(sql);
// 参数赋值
if (params != null) {
for (int i = 0; i < params.length; i++) {
pst.setObject(i + 1, params[i]);
}
}
/*
* 在此 PreparedStatement 对象中执行 SQL 语句, 该语句必须是一个 SQL 数据操作语言(Data
* Manipulation Language,DML)语句,比如 INSERT、UPDATE 或 DELETE
* 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。
*/
// 执行
affectedLine = pst.executeUpdate();
} catch (SQLException e) {
System.out.println(e.getMessage());
} finally {
// 释放资源
closeAll(conn, pst, null);
}
return affectedLine;
}
/**
* insert update delete SQL语句的执行的统一方法
*
* @param sql SQL语句
* @param params 参数数组,若没有参数则为null
* @return 受影响的行数
*/
public int executeUpdate(Connection conn, String sql, Object... params) {
// 受影响的行数
int affectedLine = 0;
// 创建ResultSetMetaData对象
PreparedStatement pst = null;
try {
// 获得连接
// 调用SQL
pst = conn.prepareStatement(sql);
// 参数赋值
if (params != null) {
for (int i = 0; i < params.length; i++) {
pst.setObject(i + 1, params[i]);
}
}
/*
* 在此 PreparedStatement 对象中执行 SQL 语句, 该语句必须是一个 SQL 数据操作语言(Data
* Manipulation Language,DML)语句,比如 INSERT、UPDATE 或 DELETE
* 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。
*/
// 执行
affectedLine = pst.executeUpdate();
} catch (SQLException e) {
System.out.println(e.getMessage());
} finally {
// 释放资源
close(pst);
}
return affectedLine;
}
/**
* 获取结果集,并将结果放在List中
*
* @param sql SQL语句 params 参数,没有则为null
* @return List 结果集
*/
public List
5、进行测试:
正常情况下:
public class JDBCTest {
public static void main(String[] args) {
JDBCUtil jdbcUtil = new JDBCUtil();
Connection conn = null;
try {
conn = DBPoolConnection.getInstance().getConnection();
conn.setAutoCommit(false);
String sql_1 = "update account set accountbalance = accountbalance + ? where userid = ?";
String sql_2 = "update account set accountbalance = accountbalance - ? where userid = ?";
int line_1 = jdbcUtil.executeUpdate(conn, sql_1, 10, "1001");
int line_2 = jdbcUtil.executeUpdate(conn, sql_2, 10, "1002");
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
jdbcUtil.close(conn);
}
}
}
异常情况下:
public class JDBCTest {
public static void main(String[] args) {
JDBCUtil jdbcUtil = new JDBCUtil();
Connection conn = null;
try {
conn = DBPoolConnection.getInstance().getConnection();
conn.setAutoCommit(false);
String sql_1 = "update account set accountbalance = accountbalance + ? where userid = ?";
String sql_2 = "update account set accountbalance = accountbalance - ? where userid = ?";
int line_1 = jdbcUtil.executeUpdate(conn, sql_1, 10, "1001");
if (line_1 == 1) {
throw new RuntimeException();
}
int line_2 = jdbcUtil.executeUpdate(conn, sql_2, 10, "1002");
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
jdbcUtil.close(conn);
}
}
}