javaweb之事務與連線池
事務
什麼是事務?
轉賬:
- 給張三賬戶減1000元
- 給李四賬戶加1000元
當給張三賬戶減1000元后,丟擲了異常!這會怎麼樣呢?我相信從此之後,張三再也不敢轉賬了。
使用事務就可以處理這一問題:把多個對資料庫的操作繫結成一個事務,要麼都成功,要麼都失敗!
==============
事物的特性:ACID
- 原子性:事務中所有操作是不可再分割的原子單位。事務中所有操作要麼全部執行成功,要麼全部執行失敗。
- 一致性:事務執行後,資料庫狀態與其它業務規則保持一致。如轉賬業務,無論事務執行成功與否,參與轉賬的兩個賬號餘額之和應該是不變的。
- 隔離性:隔離性是指在併發操作中,不同事務之間應該隔離開來,使每個併發中的事務不會相互干擾。
- 永續性:一旦事務提交成功,事務中所有的資料操作都必須被持久化到資料庫中,即使提交事務後,資料庫馬上崩潰,在資料庫重啟時,也必須能保證通過某種機制恢復資料。
==============
MySQL操作事務
- 開始事務:start transaction
- 結束事務:commit或rollback
==============
JDBC事務
- 開始事務:con.setAutoCommit(false);
- 結束事務;con.commit()或con.rollback();
==============
儲存點
儲存點的是可以回滾到事務中的某個位置,而不是回滾整個事務。
回滾到儲存點不會結束事務。
設定儲存點:Savepoint sp = con.setSavepoint();
回滾到儲存點:con.rollback(sp);
==============
事務隔離級別
- 髒讀:讀到未提交
- 不可重複讀:兩次讀取不一致,讀取到另一事務修改的記錄
- 幻讀:兩次讀取不一致,讀取到另一事務插入的記錄
四大隔離級別
- SERIALIZABLE(序列化):對同一資料的訪問是序列的,即非併發的,所以不會出現任何併發問題。易出現死鎖,效率太低!不可用!
- REPEATABLE READ(可重複讀):防止了髒讀、不可重複讀,但沒有防止幻讀
- READ COMMITTED(讀已提交):防止了髒讀,但沒有防止不可重複讀,以及幻讀
- READ UNCOMMITTED(讀未提交):可能出現所有併發問題,效率最高,但不可用!
MySQL預設事務隔離級別為:REPEATABLE READ
Oracle預設事務隔離級別為:READ COMMITTED
MySQL設定事務隔離級別
- 檢視:select @@tx_isolation
- 設定:set transaction isolation level 四選一
JDBC設定事務隔離級別
con.setTransactionIsolation(四選一)
===============
資料庫連線池
作用:使用池來管理連線的生命週期,節省資源,提高效能。
java提供的連線池介面:javax.sql.DataSource,連線池廠商的連線池類需要實現這一介面。
DBCP
jar:commons-pool.jar、commons-dbcp.jar
BasicDataSource ds = new BasicDataSource();
ds.setUsername(“root”);
ds.setPassword(“123”);
ds.setUrl(“jdbc:mysql://localhost:3306/mydb1”);
ds.setDriverClassName(“com.mysql.jdbc.Driver”);
ds.setMaxActive(20);
ds.setMaxIdle(10);
ds.setInitialSize(10) ;
ds.setMinIdle(2) ;
ds.setMaxWait(1000) ;
Connection con = ds.getConnection();
C3P0
jar:c3p0-0.9.2-pre1.jar、c3p0-oracle-thin-extras-0.9.2-pre1.jar、mchange-commons-0.2.jar
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setJdbcUrl(“jdbc:mysql://localhost:3306/mydb1”);
ds.setUser(“root”);
ds.setPassword(“123”);
ds.setDriverClass(“com.mysql.jdbc.Driver”);
ds.setAcquireIncrement(5) ;
ds.setInitialPoolSize(20) ;
ds.setMinPoolSize(2) ;
ds.setMaxPoolSize(50) ;
Connection con = ds.getConnection();
C3P0配置檔案
- 通過預設配置初始化連線池
ComboPooledDataSource ds = new ComboPooledDataSource();
Connection con = ds.getConnection();
- 通過命名配置初始化連線池
ComboPooledDataSource ds = new ComboPooledDataSource(“oracle-config”);
Connection con = ds.getConnection();
==================
Tomcat配置連線池
在server.xml中,或在conf/catalina/localhost/下建立xml檔案
獲取Tomcat資源
Context cxt = new InitialContext();
DataSource ds = (DataSource)cxt.lookup(“java:/comp/env/myc3p0”);
Connection con = ds.getConnection();
==================
修改JdbcUtils
public class JdbcUtils {
private static DataSource dataSource = new ComboPooledDataSource();
public static DataSource getDataSource() {
return dataSource;
}
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
==================
DBUtils
jar:commons-dbutils.jar
核心類:QueryRunner、ResultSetHandler
QueryRunner方法:
- update():DDL、DML
- query():DQL
- batch():批處理
增、刪、改
public void fun1() throws SQLException {
QueryRunner qr = new QueryRunner();
String sql = “insert into user values(?,?,?)”;
qr.update(JdbcUtils.getConnection(), sql, “u1”, “zhangSan”, “123”);
}
查
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = “select * from tab_student”;
// 把結果集轉換成Bean
Student stu = qr.query(sql, new BeanHandler(Student.class));
// 把結果集轉換成Bean的List
List list = qr.query(sql, new BeanListHandler(Student.class));
// 把結果集轉換成Map
Map<String,Object> map = qr.query(sql, new MapHandler());
// 把結果集轉換成List
// 把結果集轉換成一列的List
List list = qr.query(sql, new ColumnListHandler(“name”)) ;
// 把結果轉換成單行單列的值
Number number = (Number)qr.query(sql, new ScalarHandler());
================
批處理
DataSource ds = JdbcUtils.getDataSource();
QueryRunner qr = new QueryRunner(ds);
String sql = "insert into tab_student values(?,?,?,?)";
Object[][] params = new Object[10][]; //表示 要插入10行記錄
for(int i = 0; i < params.length; i++) {
params[i] = new Object[]{"S_300" + i, "name" + i, 30 + i, i%2==0?"男":"女"};
}
qr.batch (sql, params);