1. 程式人生 > >javaweb之事務與連線池

javaweb之事務與連線池

事務

什麼是事務?
轉賬:

  1. 給張三賬戶減1000元
  2. 給李四賬戶加1000元

當給張三賬戶減1000元后,丟擲了異常!這會怎麼樣呢?我相信從此之後,張三再也不敢轉賬了。

使用事務就可以處理這一問題:把多個對資料庫的操作繫結成一個事務,要麼都成功,要麼都失敗!

==============

事物的特性:ACID

  • 原子性:事務中所有操作是不可再分割的原子單位。事務中所有操作要麼全部執行成功,要麼全部執行失敗。
  • 一致性:事務執行後,資料庫狀態與其它業務規則保持一致。如轉賬業務,無論事務執行成功與否,參與轉賬的兩個賬號餘額之和應該是不變的。
  • 隔離性:隔離性是指在併發操作中,不同事務之間應該隔離開來,使每個併發中的事務不會相互干擾。
  • 永續性:一旦事務提交成功,事務中所有的資料操作都必須被持久化到資料庫中,即使提交事務後,資料庫馬上崩潰,在資料庫重啟時,也必須能保證通過某種機制恢復資料。

==============

MySQL操作事務

  1. 開始事務:start transaction
  2. 結束事務:commit或rollback

==============

JDBC事務

  1. 開始事務:con.setAutoCommit(false);
  2. 結束事務;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配置檔案

  1. 通過預設配置初始化連線池
    ComboPooledDataSource ds = new ComboPooledDataSource();
    Connection con = ds.getConnection();
XXX
  1. 通過命名配置初始化連線池
    ComboPooledDataSource ds = new ComboPooledDataSource(“oracle-config”);
    Connection con = ds.getConnection();
XXX

==================

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<Map<String,Object>> list = qr.query(sql, new MapListHandler() );

// 把結果集轉換成一列的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);