JavaWeb之事務&資料庫連線池
1. 事務
Transaction 其實指的就是一組操作,裡面包含許多單一的邏輯,如果有一個邏輯沒有執行成功,那麼
個事務就是執行失敗,所有的資料都會回滾到未執行前的狀態。
事務是為解決資料安全操作提出的,事務控制實際上就是控制資料的安全訪問,比如銀行轉賬。
2. 事務的使用
命令列方式
# 開啟事務
start transaction;
# 提交事務
commit;
# 回滾事務
rollback;
程式碼方式
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Demo01 {
@Test
public void transactionDemo(){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = ConnectionUtil.getConnection();
//關閉事務自動提交
conn.setAutoCommit(false);
String sql = "select * from account";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name") + "==" + rs.getInt("money"));
}
//所有操作執行完成後手動的提交一下
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
try {
//回滾事務
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
ConnectionUtil.release(rs, pstmt, conn);
}
}
}
3. 事務特性(ACID)
原子性 事務中包含的邏輯不可再分
一致性 事務執行前後,資料的完整性保持一直
隔離性 事務在執行期間,不能受到其他事務的影響
永續性 事務執行成功,應該持久化到磁碟上
4. 安全問題
讀
髒讀
在A視窗中設定隔離級別為讀未提交,在A、B兩個視窗中開啟事務,在B視窗中修改資料。在A中可以查詢到B視窗中還未提交的資料。一個事務中讀取到另一個事務還未提交的資料,就是髒讀。讀到的是資料庫記憶體中的資料,並非磁碟上真正的資料。
不可以重複讀
在A視窗中設定隔離級別為讀已提交,在A、B兩個視窗中開啟事務,在B視窗中修改資料。在A中就不可以查詢到B視窗還未提交的資料了,這樣就解決了髒讀的問題,但是這樣會引發一個新的問題,那就是隻能讀取到已經提交的資料。這樣的話,前後讀取到的結果是不一致的,發生了不可重複讀,所謂不可重複度,就是不能執行多次讀取,否則會出現查詢結果不一致。
將隔離級別設定為重複讀,就可以解決整個問題了。
幻讀
一個事務讀取到另一個事務已插入的資料,導致查詢結果不一致。
將隔離級別設定為可序列化,就可以解決這些問題了,到底可序列化是怎麼解決這個問題的呢?
在A視窗中設定隔離級別為可序列化,在A、B兩個視窗中開啟事務,在B視窗中修改資料,只有B視窗執行commit,A視窗才可以查詢資料。這個級別一般比較少用,因為它的效率比較低。
寫
丟失更新
兩個不同的事務在某一時刻對同一資料執行修改操作 ,導致第一次運算元據丟失
樂觀鎖
l樂觀鎖認為事務不一定會產生丟失更新,讓事務進行併發修改,不對事務進行鎖定。發現併發修改某行資料時,樂觀鎖丟擲異常。讓使用者解決。可以透過給資料表新增自增的version欄位進行資料修改時,資料庫會檢測version欄位和事務中的version欄位是否一致。若不一致,丟擲異常,交給程式猿自己處理。
悲觀鎖
悲觀鎖認為一定會發生丟失更新,所以悲觀鎖要求一個事務執行提交之後,其他事務才能查詢修改資料。
5. 隔離級別
Read Uncommitted 讀未提交 ,引發髒讀問題
Read Committed 讀已提交,解決髒讀,引發不可重複讀問題(Oracle預設隔離級別)
Repeatable Read 重複讀,解決不可重複讀,未解決幻讀(MySQL預設隔離級別)
Serializable,可序列化 解決所有問題
隔離級別分類
按效能從高到低可劃分為:讀未提交>讀已提交>重複讀>可序列化
按攔截程式從高到低可劃分為:可序列化>重複讀>讀已提交>讀未提交
6. 資料庫連線池
資料庫在使用的時候再去建立連線,這是一件非常耗時的操作,為了改善使用者體驗,我們可以在程式開始的時候,在記憶體中開闢一塊空間,稱為資料庫連線池,一開始往池子裡放多個連線物件,如果有使用者需要使用資料庫連線,就從池子裡取物件,當操作完成後將連線歸還,這樣就可以做到連線複用
自定義資料庫連線池
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class MyDataSource implements DataSource {
List<Connection> dataSoucePool = new ArrayList<Connection>();
public MyDataSource() {
for (int i = 0; i < 10;i++){
//將10個連線放到連線池中
Connection conn = ConnectionUtil.getConnection();
dataSoucePool.add(conn);
}
}
@Override
public Connection getConnection() throws SQLException {
if (dataSoucePool.size() == 0){
//如果連線池已經沒有空閒的連線了,擴容
for (int i = 0; i < 5;i++){
Connection conn = ConnectionUtil.getConnection();
dataSoucePool.add(conn);
}
}
//每次都移出連線池第一個連線物件
Connection conn = dataSoucePool.remove(0);
Connection connection = new ConnectionWrap(conn,dataSoucePool);
return connection;
}
public Connection backConnectuon(Connection conn){
dataSourcePool.add(conn);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
解決自定義連線池出現的問題
自定義連線池出現了什麼問題呢?
自定義連線池多增加了一個backConnection方法來歸還連線,違背了面向介面程式設計的規範。我們可以使用裝飾者模式來包裝Connection類,以符合面向介面的規範。
import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
public class ConnectionWrap implements java.sql.Connection {
Connection connection = null;
List<Connection> list = null;
public ConnectionWrap(Connection connection, List<Connection> list) {
this.connection = connection;
this.list = list;
}
@Override
public Statement createStatement() throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return null;
}
@Override
public String nativeSQL(String sql) throws SQLException {
return null;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
}
@Override
public boolean getAutoCommit() throws SQLException {
return false;
}
@Override
public void commit() throws SQLException {
}
@Override
public void rollback() throws SQLException {
}
@Override
public void close() throws SQLException {
System.out.println(list.size());
list.add(connection);
System.out.println(list.size());
}
@Override
public boolean isClosed() throws SQLException {
return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
}
@Override
public boolean isReadOnly() throws SQLException {
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
}
@Override
public String getCatalog() throws SQLException {
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
}
@Override
public void setHoldability(int holdability) throws SQLException {
}
@Override
public int getHoldability() throws SQLException {
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return false;
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
@Override
public String getClientInfo(String name) throws SQLException {
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
return null;
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
}
@Override
public String getSchema() throws SQLException {
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
}
@Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
7. 常用開源資料庫連線池有哪些?
DBCP
# 不使用配置檔案方式
public void demo(){
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3306/bank");
basicDataSource.setUsername("root");
basicDataSource.setPassword("123456");
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
connection = basicDataSource.getConnection();
String sql = "select * from account";
pstmt = connection.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name") + rs.getInt("money"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
ConnectionUtil.release(rs, pstmt, connection);
}
}
# 使用配置檔案方式
public void demo() {
BasicDataSourceFactory factory = new BasicDataSourceFactory();
Properties properties = new Properties();
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
InputStream inputStream = new FileInputStream("src\dbcpconfig.properties");
properties.load(inputStream);
DataSource dataSource = factory.createDataSource(properties);
connection = dataSource.getConnection();
String sql = "select * from account";
pstmt = connection.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name") + rs.getInt("money"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ConnectionUtil.release(rs, pstmt, connection);
}
}
C3P0
# 不使用配置檔案方式
public void demo(){
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
} catch (PropertyVetoException e) {
e.printStackTrace();
}
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/bank");
dataSource.setUser("root");
dataSource.setPassword("123456");
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
connection = dataSource.getConnection();
String sql = "select * from account";
pstmt = connection.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name") + rs.getInt("money"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
ConnectionUtil.release(rs, pstmt, connection);
}
}
# 使用配置檔案方式
public void demo() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
connection = dataSource.getConnection();
String sql = "select * from account";
pstmt = connection.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name") + rs.getInt("money"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ConnectionUtil.release(rs, pstmt, connection);
}
}
8. DbUtils
增刪改
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);
查詢
直接new介面的匿名實現類
public void demo(){
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
Account account = null;
try {
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;
}
}, 3);
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println(account.toString());
}
直接使用框架已經寫好的實現類
# 查詢單個物件
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
Account account = queryRunner.query("select * from account where id = ?",
new BeanHandler<Account>(Account.class), 8);
# 查詢多個物件
QueryRunner queryRunner = new QueryRunner(new ComboPooledDataSource());
List<Account> list = queryRunner.query("select * from account ",
new BeanListHandler<Account>(Account.class));
ResultSetHandler 常用的實現類
BeanHandler:查詢到的單個資料封裝成一個物件
BeanListHandler:查詢到的多個資料封裝 成一個List<物件>
ArrayHandler:查詢到的單個資料封裝成一個陣列
ArrayListHandler,:查詢到的多個資料封裝成一個集合 ,集合裡面的元素是陣列
MapListHandler:查詢到的多個資料封裝成一個集合 ,集合裡面的元素是map
©著作權歸作者所有:來自51CTO部落格作者灰白世界的原創作品,如需轉載,請註明出處,否則將追究法律責任
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3486/viewspace-2819425/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Javaweb-資料庫連線池JavaWeb資料庫
- 資料庫連線池-Druid資料庫連線池原始碼解析資料庫UI原始碼
- 《四 資料庫連線池原始碼》手寫資料庫連線池資料庫原始碼
- Flask資料庫連線池Flask資料庫
- python資料庫連線池Python資料庫
- 資料庫連線池原理資料庫
- JavaWEB開發13——事務與連線池JavaWeb
- 【MySQL】自定義資料庫連線池和開源資料庫連線池的使用MySql資料庫
- 資料庫連線池實現資料庫
- 聊聊資料庫連線池 Druid資料庫UI
- 手寫資料庫連線池資料庫
- Python資料庫連線池DButilsPython資料庫
- Java Druid資料庫連線池+SpringJDBCJavaUI資料庫SpringJDBC
- mysql資料庫連線池配置教程MySql資料庫
- MySql資料庫連線池專題MySql資料庫
- Druid資料庫連線池使用體驗UI資料庫
- Springboot 整合阿里資料庫連線池 druidSpring Boot阿里資料庫UI
- druid資料庫連線池的配置類UI資料庫
- Spring Boot整合Druid資料庫連線池Spring BootUI資料庫
- 淺談JDBC和資料庫連線池JDBC資料庫
- 帶你進入資料庫連線池資料庫
- 資料庫連線池技術詳解資料庫
- javaweb專案(1)連線資料庫,登入註冊JavaWeb資料庫
- golang兩種資料庫連線池實現Golang資料庫
- django中的資料庫連線池實現Django資料庫
- python資料庫連線池的正確用法Python資料庫
- 資料庫連線池_druid基本使用&工具類資料庫UI
- 資料庫連線池到底應該設多大?資料庫
- springboot專案整合druid資料庫連線池Spring BootUI資料庫
- 資料庫連線池的實現及原理資料庫
- 自定義帶監控的資料庫連線池資料庫
- Druid資料庫連線池就這麼簡單UI資料庫
- 第77節:Java中的事務和資料庫連線池和DBUtilesJava資料庫
- 第 67 期 Go database/sql 資料庫連線池分析GoDatabaseSQL資料庫
- 資料庫連線池設計和實現(Java版本)資料庫Java
- Java技術分享:什麼是資料庫連線池?Java資料庫
- Go實戰準備工作---建立資料庫連線池Go資料庫
- 從原始碼分析DBCP資料庫連線池的原理原始碼資料庫