什么是事务:
数据库事务是指作为单个逻辑工作单元执行的一系列操作(SQL语句)。这些操作要么全部执行,要么全部不执行。
为什么需要事务
经典的银行转账行为,A账户转给B账户10元,数据库操作需要两步,第一步A账户减10元,第二步B账户加10元,如果没有事务并且在两步中间发生异常,就会导致A的账户少了10元,但B的账户没有变化,如果不能保证这两步操作统一,银行的转账业务也没法进行展开了。
事务管理是每个数据库(oracle、mysql、db等)都必须实现的。
事务特性(4种):
- 原子性 (atomicity):强调事务的不可分割.
- 一致性 (consistency):事务的执行的前后数据的完整性保持一致.
- 隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰
- 持久性(durability) :事务一旦结束,数据就持久到数据库
事务运行模式(3种)
- 自动提交事务:默认事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。
- 显式事务:以BEGIN TRANSACTION显式开始,以COMMIT或ROLLBACK显式结束。
- 隐性事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。
下面做一个事务的示例:
JDBC用了Druid连接池
1、maven引包
<!--mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
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<Map<String, Object>> excuteQuery(String sql, Object... params) {
// 创建ResultSetMetaData对象
ResultSetMetaData rsmd = null;
Connection conn = null;
PreparedStatement pst = null;
ResultSet rst = null;
// 创建List
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
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]);
}
}
// 执行
rst = pst.executeQuery();
rsmd = rst.getMetaData();
// 获得结果集列数
int columnCount = rsmd.getColumnCount();
// 将ResultSet的结果保存到List中
while (rst.next()) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 1; i <= columnCount; i++) {
map.put(rsmd.getColumnLabel(i), rst.getObject(i));
}
list.add(map);// 每一个map代表一条记录,把所有记录存在list中
}
} catch (SQLException e1) {
System.out.println(e1.getMessage());
} finally {
// 关闭所有资源
closeAll(conn, pst, rst);
}
return list;
}
/**
* 关闭所有资源
*/
private void closeAll(Connection conn, PreparedStatement pst, ResultSet rst) {
// 关闭结果集对象
if (rst != null) {
try {
rst.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
// 关闭PreparedStatement对象
if (pst != null) {
try {
pst.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
// 关闭Connection 对象
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}
/**
* 关闭所有资源
*/
public void close(AutoCloseable autoCloseable) {
if (autoCloseable != null) {
try {
autoCloseable.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
/**
* 通过反射机制查询单条记录
*
* @param sql
* @param params
* @param cls
* @return
* @throws Exception
*/
public <T> T findSimpleRefResult(String sql, Class<T> cls, Object... params) {
T resultObject = null;
int index = 1;
Connection conn = null;
PreparedStatement pst = null;
ResultSet rst = null;
try {
conn = DBPoolConnection.getInstance().getConnection();
pst = conn.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
pst.setObject(index++, params[i]);
}
}
rst = pst.executeQuery();
ResultSetMetaData metaData = rst.getMetaData();
int cols_len = metaData.getColumnCount();
while (rst.next()) {
// 通过反射机制创建一个实例
resultObject = cls.newInstance();
for (int i = 0; i < cols_len; i++) {
String cols_name = metaData.getColumnName(i + 1);
Object cols_value = rst.getObject(cols_name);
if (cols_value == null) {
cols_value = "";
}
try {
Field field = cls.getDeclaredField(cols_name.toLowerCase());
field.setAccessible(true); // 打开javabean的访问权限
field.set(resultObject, cols_value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn, pst, rst);
}
return resultObject;
}
/**
* 通过反射机制查询多条记录
*
* @param sql
* @param params
* @param cls
* @return
* @throws Exception
*/
public <T> List<T> findMoreRefResult(String sql, Class<T> cls, Object... params) {
List<T> list = new ArrayList<T>();
int index = 1;
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
connection = DBPoolConnection.getInstance().getConnection();
pstmt = connection.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(index++, params[i]);
}
}
resultSet = pstmt.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int cols_len = metaData.getColumnCount();
while (resultSet.next()) {
// 通过反射机制创建一个实例
T resultObject = cls.newInstance();
for (int i = 0; i < cols_len; i++) {
String cols_name = metaData.getColumnName(i + 1);
Object cols_value = resultSet.getObject(cols_name);
if (cols_value == null) {
cols_value = "";
}
try {
Field field = cls.getDeclaredField(cols_name.toLowerCase());
field.setAccessible(true); // 打开javabean的访问权限
field.set(resultObject, cols_value);
} catch (Exception e) {
e.printStackTrace();
}
}
list.add(resultObject);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(connection, pstmt, resultSet);
}
return list;
}
/**
* 通过反射机制查询多条记录
*
* @param sql
* @param params
* @param cls
* @return
* @throws Exception
*/
public List<String> findMoreRefResult(String sql, Object... params) {
List<String> list = new ArrayList<String>();
int index = 1;
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
connection = DBPoolConnection.getInstance().getConnection();
pstmt = connection.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(index++, params[i]);
}
}
resultSet = pstmt.executeQuery();
while (resultSet.next()) {
// 通过反射机制创建一个实例
String cols_value = resultSet.getString(1);
list.add(cols_value);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(connection, pstmt, resultSet);
}
return list;
}
/**
* SQL 查询将查询结果:一行一列
*
* @param sql SQL语句
* @param params 参数数组,若没有参数则为null
* @return 结果集
*/
public Object executeQuerySingle(String sql, Object... params) {
Connection conn = null;
PreparedStatement pst = null;
ResultSet rst = null;
Object object = 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]);
}
}
// 执行
rst = pst.executeQuery();
if (rst.next()) {
object = rst.getObject(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(conn, pst, rst);
}
return object;
}
}
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);
}
}
}
运行前:
运行后: