1. 事務(wù)
Transaction 其實(shí)指的一組操作,里面包含許多個(gè)單一的邏輯。只要有一個(gè)邏輯沒有執(zhí)行成功,那么都算失敗。 所有的數(shù)據(jù)都回歸到最初的狀態(tài)(回滾)
- 為什么要有事務(wù)?
為了確保邏輯的成功。 例子: 銀行的轉(zhuǎn)賬。
1.1 使用命令行方式演示事務(wù)。
- 開啟事務(wù)
start transaction;
- 提交或者回滾事務(wù)
commit; 提交事務(wù), 數(shù)據(jù)將會寫到磁盤上的數(shù)據(jù)庫
rollback ; 數(shù)據(jù)回滾,回到最初的狀態(tài)。
-
關(guān)閉自動(dòng)提交功能。
img01.png -
演示事務(wù)
img02.png
1.2 使用代碼方式演示事務(wù)
代碼里面的事務(wù),主要是針對連接來的。
- 通過conn.setAutoCommit(false )來關(guān)閉自動(dòng)提交的設(shè)置。
- 提交事務(wù) conn.commit();
- 回滾事務(wù) conn.rollback();
@Test
public void testTransaction(){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtil.getConn();
//連接,事務(wù)默認(rèn)就是自動(dòng)提交的。 關(guān)閉自動(dòng)提交。
conn.setAutoCommit(false);
String sql = "update account set money = money - ? where id = ?";
ps = conn.prepareStatement(sql);
//扣錢, 扣ID為1 的100塊錢
ps.setInt(1, 100);
ps.setInt(2, 1);
ps.executeUpdate();
int a = 10 /0 ;
//加錢, 給ID為2 加100塊錢
ps.setInt(1, -100);
ps.setInt(2, 2);
ps.executeUpdate();
//成功: 提交事務(wù)。
conn.commit();
} catch (SQLException e) {
try {
//事變: 回滾事務(wù)
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps, rs);
}
}
1.3 事務(wù)的特性
- 原子性
指的是 事務(wù)中包含的邏輯,不可分割。
- 一致性
指的是 事務(wù)執(zhí)行前后。數(shù)據(jù)完整性
- 隔離性
指的是 事務(wù)在執(zhí)行期間不應(yīng)該受到其他事務(wù)的影響
- 持久性
指的是 事務(wù)執(zhí)行成功,那么數(shù)據(jù)應(yīng)該持久保存到磁盤上。
1.4 事務(wù)的安全隱患
不考慮隔離級別設(shè)置,那么會出現(xiàn)以下問題。
讀
- 臟讀:一個(gè)事務(wù)讀到另外一個(gè)事務(wù)還未提交的數(shù)據(jù)。
- 不可重復(fù)讀:一個(gè)事務(wù)讀到了另外一個(gè)事務(wù)提交的數(shù)據(jù) ,造成了前后兩次查詢結(jié)果不一致。
- 幻讀:一個(gè)事務(wù)讀到了另一個(gè)事務(wù)insert的數(shù)據(jù) ,造成前后查詢結(jié)果不一致 。
寫
- 丟失更新
1.5 可串行化
如果有一個(gè)連接的隔離級別設(shè)置為了串行化 ,那么誰先打開了事務(wù), 誰就有了先執(zhí)行的權(quán)利, 誰后打開事務(wù),誰就只能得著,等前面的那個(gè)事務(wù),提交或者回滾后,才能執(zhí)行。 但是這種隔離級別一般比較少用。 容易造成性能上的問題。 效率比較低。
1.6 總結(jié)
分類
- 按效率劃分,從高到低
讀未提交 > 讀已提交 > 可重復(fù)讀 > 可串行化
- 按攔截程度 ,從高到底
可串行化 > 可重復(fù)讀 > 讀已提交 > 讀未提交
問題
讀未提交
引發(fā)問題: 臟讀
讀已提交
解決: 臟讀 , 引發(fā): 不可重復(fù)讀
可重復(fù)讀
解決: 臟讀 、 不可重復(fù)讀 , 未解決: 幻讀
可串行化
解決: 臟讀、 不可重復(fù)讀 、 幻讀。
mySql 默認(rèn)的隔離級別是 可重復(fù)讀
Oracle 默認(rèn)的隔離級別是 讀已提交
2. 數(shù)據(jù)庫連接池
2.1 DBCP
- 導(dǎo)入jar文件
不使用配置文件方式:
public void testDBCP01(){
Connection conn = null;
PreparedStatement ps = null;
try {
//1. 構(gòu)建數(shù)據(jù)源對象
BasicDataSource dataSource = new BasicDataSource();
//連的是什么類型的數(shù)據(jù)庫, 訪問的是哪個(gè)數(shù)據(jù)庫 , 用戶名, 密碼。。
//jdbc:mysql://localhost/bank 主協(xié)議:子協(xié)議 ://本地/數(shù)據(jù)庫
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/bank");
dataSource.setUsername("root");
dataSource.setPassword("root");
//2. 得到連接對象
conn = dataSource.getConnection();
String sql = "insert into account values(null , ? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "admin");
ps.setInt(2, 1000);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps);
}
}
使用配置文件方式:
Connection conn = null;
PreparedStatement ps = null;
try {
BasicDataSourceFactory factory = new BasicDataSourceFactory();
Properties properties = new Properties();
InputStream is = new FileInputStream("src//dbcpconfig.properties");
properties.load(is);
DataSource dataSource = factory.createDataSource(properties);
//2. 得到連接對象
conn = dataSource.getConnection();
String sql = "insert into account values(null , ? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "liangchaowei");
ps.setInt(2, 100);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps);
}
2.2 C3P0
1 .拷貝jar文件 到 lib目錄
不使用配置文件方式
Connection conn = null;
PreparedStatement ps = null;
try {
//1. 創(chuàng)建datasource
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//2. 設(shè)置連接數(shù)據(jù)的信息
dataSource.setDriverClass("com.mysql.jdbc.Driver");
//忘記了---> 去以前的代碼 ---> jdbc的文檔
dataSource.setJdbcUrl("jdbc:mysql://localhost/bank");
dataSource.setUser("root");
dataSource.setPassword("root");
//2. 得到連接對象
conn = dataSource.getConnection();
String sql = "insert into account values(null , ? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "admi234n");
ps.setInt(2, 103200);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtil.release(conn, ps);
}
使用配置文件方式
//默認(rèn)會找 xml 中的 default-config 分支。
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 得到連接對象
conn = dataSource.getConnection();
String sql = "insert into account values(null , ? , ?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "admi234n");
ps.setInt(2, 103200);
3. DBUtils
3.1 增刪改
//dbutils 只是幫我們簡化了CRUD 的代碼, 但是連接的創(chuàng)建以及獲取工作。 不在他的考慮范圍
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
//增加
//queryRunner.update("insert into account values (null , ? , ? )", "aa" ,1000);
//刪除
//queryRunner.update("delete from account where id = ?", 5);
//更新
//queryRunner.update("update account set money = ? where id = ?", 10000000 , 6);
3.2 查詢
- 直接new接口的匿名實(shí)現(xiàn)類
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
Account account = queryRunner.query("select * from account where id = ?", new ResultSetHandler<Account>(){
@Override
public Account handle(ResultSet rs) throws SQLException {
Account account = new Account();
while(rs.next()){
String name = rs.getString("name");
int money = rs.getInt("money");
account.setName(name);
account.setMoney(money);
}
return account;
}
}, 6);
System.out.println(account.toString());
- 直接使用框架已經(jīng)寫好的實(shí)現(xiàn)類。
* 查詢單個(gè)對象
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
//查詢單個(gè)對象
Account account = queryRunner.query("select * from account where id = ?",
new BeanHandler<Account>(Account.class), 8);
* 查詢多個(gè)對象
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
List<Account> list = queryRunner.query("select * from account ",
new BeanListHandler<Account>(Account.class));
3.3 ResultSetHandler 常用的實(shí)現(xiàn)類
以下兩個(gè)是使用頻率最高的
BeanHandler, 查詢到的單個(gè)數(shù)據(jù)封裝成一個(gè)對象
BeanListHandler, 查詢到的多個(gè)數(shù)據(jù)封裝 成一個(gè)List<對象>
ArrayHandler, 查詢到的單個(gè)數(shù)據(jù)封裝成一個(gè)數(shù)組
ArrayListHandler, 查詢到的多個(gè)數(shù)據(jù)封裝成一個(gè)集合 ,集合里面的元素是數(shù)組。
MapHandler, 查詢到的單個(gè)數(shù)據(jù)封裝成一個(gè)map
MapListHandler,查詢到的多個(gè)數(shù)據(jù)封裝成一個(gè)集合 ,集合里面的元素是map。
ColumnListHandler
KeyedHandler
ScalarHandler

