一、事務(wù)概述:
1.什么是事務(wù):
一件事情有n個(gè)組成單元 要不這n個(gè)組成單元同時(shí)成功 要不n個(gè)單元就同時(shí)失敗
就是將n個(gè)組成單元放到一個(gè)事務(wù)中
2.mysql的事務(wù):
默認(rèn)的事務(wù):一條sql語(yǔ)句就是一個(gè)事務(wù) 默認(rèn)就開啟事務(wù)并提交事務(wù)。
手動(dòng)事務(wù):
1)顯示的開啟事務(wù):
start transaction
2)事務(wù)提交:代表從開啟事務(wù)到事務(wù)提交 中間的所有的sql都認(rèn)為有效 真正的更新數(shù)據(jù)庫(kù)
commit
3)事務(wù)的回滾: 代表事務(wù)的回滾 從開啟事務(wù)到事務(wù)回滾 中間的所有的 sql操作都認(rèn)為無(wú)效數(shù)據(jù)庫(kù)沒(méi)有被更新
rollback
二、JDBC事務(wù)操作:
默認(rèn)自動(dòng)事務(wù):
執(zhí)行sql語(yǔ)句:executeUpdate() ---- 每執(zhí)行一次executeUpdate方法 代表事務(wù)自動(dòng)提交.
通過(guò)jdbc的API手動(dòng)事務(wù):
開啟事務(wù):conn.setAutoComnmit(false);
提交事務(wù):conn.commit();
回滾事務(wù):conn.rollback();
注意:控制事務(wù)的connnection必須是同一個(gè)空郊;
執(zhí)行sql的connection與開啟事務(wù)的connnection必須是同一個(gè)才能對(duì)事務(wù)進(jìn)行控制静汤。
三逆甜、DBUtils事務(wù)操作:
QueryRunner:
有參構(gòu)造:QueryRunner runner = new QueryRunner(DataSource dataSource);
有參構(gòu)造將數(shù)據(jù)源(連接池)作為參數(shù)傳入QueryRunner教藻,QueryRunner會(huì)從連 接池中獲得一個(gè)數(shù)據(jù)庫(kù)連接資源操作數(shù)據(jù)庫(kù),所以直接使用無(wú)Connection參數(shù) 的update方法即可操作數(shù)據(jù)庫(kù)谜叹。
無(wú)參構(gòu)造:QueryRunner runner = new QueryRunner();
無(wú)參的構(gòu)造沒(méi)有將數(shù)據(jù)源(連接池)作為參數(shù)傳入QueryRunner匾寝,那么我們?cè)谑? 用QueryRunner對(duì)象操作數(shù)據(jù)庫(kù)時(shí)要使用有Connection參數(shù)的方法。
四荷腊、使用ThreadLocal綁定連接資源:
注意:在同一個(gè)項(xiàng)目中旗吁,不管業(yè)務(wù)有多復(fù)雜,只要我們不手動(dòng)創(chuàng)建線程停局,那么各個(gè)方法互相調(diào)用都在同一個(gè)線程中很钓,這為我們線程綁定提供了前提條件。
五董栽、事務(wù)的特性和隔離級(jí)別:
1.事務(wù)的特性ACID:
1)原子性(Atomicity):原子性是指事務(wù)是一個(gè)不可分割的工作單位码倦,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生锭碳。
2)一致性(Consistency):一個(gè)事務(wù)中袁稽,事務(wù)前后數(shù)據(jù)的完整性必須保持一致。
3)隔離性(Isolation):多個(gè)事務(wù)擒抛,事務(wù)的隔離性是指多個(gè)用戶并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí)推汽, 一個(gè)用戶的 事務(wù)不能被其它用戶的事務(wù)所干擾,多個(gè)并發(fā)事務(wù)之間數(shù)據(jù)要相互隔離歧沪。
4)持久性(Durability):持久性是指一個(gè)事務(wù)一旦被提交歹撒,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變 就是永久性的,接下來(lái)即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響诊胞。
2.并發(fā)訪問(wèn)問(wèn)題----由隔離性引起:
如果不考慮隔離性暖夭,事務(wù)存在3中并發(fā)訪問(wèn)問(wèn)題。
1)臟讀:B事務(wù)讀取到了A事務(wù)尚未提交的數(shù)據(jù) ------ 要求B事務(wù)要讀取A事 務(wù)提交的數(shù)據(jù)撵孤;
2)不可重復(fù)讀:一個(gè)事務(wù)中兩次讀取的數(shù)據(jù)的內(nèi)容不一致 ----- 要求的是一個(gè)事 務(wù)中多次讀取時(shí)數(shù)據(jù)是一致的 --- unpdate迈着;
3)幻讀/虛讀:一個(gè)事務(wù)中兩次讀取的數(shù)據(jù)的數(shù)量不一致 ----- 要求在一個(gè)事務(wù)多 次讀取的數(shù)據(jù)的數(shù)量是一致的 --insert delete。
3.事務(wù)的隔離級(jí)別:
1)read uncommitted : 讀取尚未提交的數(shù)據(jù) :哪個(gè)問(wèn)題都不能解決
2)read committed:讀取已經(jīng)提交的數(shù)據(jù) :可以解決臟讀 ---- oracle默認(rèn)的
3)repeatable read:重讀讀刃奥搿:可以解決臟讀 和 不可重復(fù)讀 ---mysql默認(rèn)的
4)serializable:串行化:可以解決 臟讀 不可重復(fù)讀 和 虛讀---相當(dāng)于鎖表
注意:mysql數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別
查看mysql數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別:
select @@tx_isolation
設(shè)置mysql的隔離級(jí)別:
set session transaction isolation level
案例實(shí)現(xiàn):
MyDataSourceUtils.java
package com.zl.utils;
import java.sql.Connection;
import java.sql.SQLException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class MyDataSourceUtils {
//獲得Connection裕菠,從連接池中獲取
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
//創(chuàng)建ThreadLocal
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
//開啟事務(wù)
public static void startTransaction() throws SQLException{
Connection conn = getCurrentConnection();
conn.setAutoCommit(false);
}
//獲得當(dāng)前線程綁定的conn
public static Connection getCurrentConnection() throws SQLException{
//從ThreadLocal中尋找當(dāng)前線程是否有對(duì)應(yīng)Connection
Connection conn = tl.get();
if(conn==null){
//獲得一個(gè)新的Connection
conn=getConnection();
//將conn資源綁定到ThreadLocal上
tl.set(conn);
}
return conn;
}
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
//回滾事務(wù)
public static void rollback() throws SQLException {
getCurrentConnection().rollback();
}
//提交事務(wù)
public static void commit() throws SQLException {
Connection conn = getCurrentConnection();
conn.commit();
tl.remove();
conn.close();
}
}
TransferServlet.java
package com.zl.transfer.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.zl.transfer.service.TransferService;
public class TransferServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接收轉(zhuǎn)賬的參數(shù)
String out = request.getParameter("out");
String in = request.getParameter("in");
String moneyStr = request.getParameter("money");
double money = Double.parseDouble(moneyStr);
TransferService service = new TransferService();
boolean isTransferSuccess = service.transfer(out,in,money);
response.setContentType("text/html;charset=UTF-8");
if(isTransferSuccess){
response.getWriter().write("轉(zhuǎn)賬成功");
}else{
response.getWriter().write("轉(zhuǎn)賬失敗");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
TransferService.java
package com.zl.transfer.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.zl.transfer.service.TransferService;
public class TransferServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接收轉(zhuǎn)賬的參數(shù)
String out = request.getParameter("out");
String in = request.getParameter("in");
String moneyStr = request.getParameter("money");
double money = Double.parseDouble(moneyStr);
TransferService service = new TransferService();
boolean isTransferSuccess = service.transfer(out,in,money);
response.setContentType("text/html;charset=UTF-8");
if(isTransferSuccess){
response.getWriter().write("轉(zhuǎn)賬成功");
}else{
response.getWriter().write("轉(zhuǎn)賬失敗");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
TransferDao.java
package com.zl.transfer.dao;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import com.zl.utils.DataSourceUtils;
import com.zl.utils.MyDataSourceUtils;
public class TransferDao {
public void out(String out, double money) throws SQLException {
QueryRunner runner = new QueryRunner();
Connection conn = MyDataSourceUtils.getCurrentConnection();
String sql = "update account set money=money-? where name=?";
runner.update(conn, sql, money,out);
}
public void in(String in, double money) throws SQLException {
QueryRunner runner = new QueryRunner();
Connection conn = MyDataSourceUtils.getCurrentConnection();
String sql = "update account set money=money+? where name=?";
runner.update(conn, sql, money,in);
}
}