JDBC 前言 在JDBC-01當中,我們簡單地學習了有關JDBC的一些基本操作,現在我們再一次進行深入的學習。 正文 事務 首先,我們來學習的是JDBC中事務的運用,那麼讓我們再次瞭解一下事務的概念。 事務的概念 事務指的是邏輯上的一組操作,組成這組操作各個邏輯單元要麼全部成功,要麼全部失敗。 關 ...
JDBC
前言
在JDBC-01當中,我們簡單地學習了有關JDBC的一些基本操作,現在我們再一次進行深入的學習。
正文
事務
首先,我們來學習的是JDBC中事務的運用,那麼讓我們再次瞭解一下事務的概念。
事務的概念
事務指的是邏輯上的一組操作,組成這組操作各個邏輯單元要麼全部成功,要麼全部失敗。
關於事務的API
commit()
rollback()
異常案例:
package com.charles.jdbc.high; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; /** * 有關事務的案例 * 轉賬案例 * @author Charles */ public class Demo01 { @Test public void demo(){ Connection conn = null; PreparedStatement pste = null; try { // 載入驅動 + 獲取連接 conn = org.charl.Demo.getConnection(); // 編寫SQL String sql = "update account set money = money + ? where name = ?"; // 預編譯 pste = conn.prepareStatement(sql); // 轉賬過程 pste.setInt(1,-1000); pste.setString(2,"aaa"); pste.executeUpdate(); pste.setInt(1,1000); pste.setString(2,"bbb"); pste.executeUpdate(); // 因為這個錯誤,導致異常的發生 int i = 1 / 0; } catch (SQLException e){ e.printStackTrace(); } finally { // 資源釋放 org.charl.Demo.release(pste,conn); } } }
修改後的案例
package com.charles.jdbc.high; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; /** * 有關事務的案例 * 轉賬案例 * @author Charles */ public class Demo01 { @Test public void demo(){ Connection conn = null; PreparedStatement pste = null; try { // 載入驅動 + 獲取連接 conn = org.charl.Demo.getConnection(); // 開啟事務 conn.setAutoCommit(false); // 編寫SQL String sql = "update account set money = money + ? where name = ?"; // 預編譯 pste = conn.prepareStatement(sql); // 轉賬過程 pste.setInt(1,-1000); pste.setString(2,"aaa"); pste.executeUpdate(); pste.setInt(1,1000); pste.setString(2,"bbb"); pste.executeUpdate(); // 因為這個錯誤,導致異常的發生 // int i = 1 / 0; conn.commit(); } catch (SQLException e){ // 回滾事務 try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); } finally { // 資源釋放 org.charl.Demo.release(pste,conn); } } }
這樣,數據就會進行回滾,保證了數據的安全性。
連接池
概念:連接池是創建和管理一個連接的緩衝池的技術,這些連接準備好被任何需要它們的線程使用。
好處(作用):
- 減少連接創建時間
- 簡化的編程模式
- 受控的資源使用
- 提高連接的效率
連接池原理:
自定義連接池:
我們可以利用DataSource介面來實現一個自定義連接池
具體步驟:
-
-
重寫一個getConnection方法
-
初始化多個連接在記憶體中
-
/** * 自定義連接池 * @author Charles */ public class MyDataSource implements DataSource { // 在初始化的時候提供一些連接 private List<Connection> connectionList = new ArrayList<Connection>(); public MyDataSource(){ // 初始化連接 for (int i = 1; i <= 4; i++){ // 向集合中存入連接 connectionList.add(org.charl.Demo.getConnection()); } } // 獲得連接的方法 @Override public Connection getConnection(String username, String password) throws SQLException { Connection conn = connectionList.remove(0); return conn; } // 歸還連接的方法 public void addBack(Connection conn){ connectionList.add(conn); }
代碼案例:
package jdbc.datasources; import org.junit.Test; import javax.management.relation.Relation; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 利用連接池的案例 */ public class Demo01 { @Test public void demo(){ Connection conn = null; PreparedStatement preparedStatement = null; ResultSet rs = null; MyDataSource md = null; try{ // 利用自定義的連接池註冊驅動 + 獲得連接 md = new MyDataSource(); conn = md.getConnection(); // 編寫SQL語句 String sql = "select * from account"; preparedStatement = conn.prepareStatement(sql); rs = preparedStatement.executeQuery(); // 遍歷結果 while (rs.next()){ System.out.println(rs.getInt("id") + " " + rs.getString("name") + " " + rs.getString("money")); } }catch (Exception e){ e.printStackTrace(); }finally { // 釋放資源 if (preparedStatement != null){ try{ preparedStatement.close(); } catch (SQLException e){ e.printStackTrace(); } preparedStatement = null; } if (rs != null){ try { rs.close(); }catch (SQLException e){ e.printStackTrace(); } rs = null; } // 歸還連接 md.addBack(conn); } } }
Druid 是阿裡旗下的開源連接池產品,使用非常簡單,可以與Spring 框架進行快速整合。
Maven導包
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency>
基本代碼實現
package com.charles.datasource.demo1; import com.alibaba.druid.pool.DruidDataSource; import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DruidDemo1 { @Test public void demo01(){ Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 使用連接池 DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("com.mysql.jdbc.Driver"); druidDataSource.setUrl("jdbc:mysql:///web_test3"); druidDataSource.setUsername("root"); druidDataSource.setPassword("1234"); // 獲得連接 connection = druidDataSource.getConnection(); // 編寫SQL String sql = "select * from user"; // 預編譯sql preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ System.out.println(resultSet.getInt("id") + " " + resultSet.getString("username") + " " + resultSet.getString("password")); } } catch (SQLException e) { e.printStackTrace(); } finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
當然也可以將這些資料庫信息向外面引入,即創建一個db.properties
代碼實現:
單元測試類
@Test public void Demo02(){ Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 使用連接池 Properties properties = new Properties(); properties.load(new FileInputStream("src/main/resources/db.properties")); DataSource dataSource = DruidDataSourceFactory.createDataSource(properties); // 獲得連接 connection = dataSource.getConnection(); // 編寫SQL String sql = "select * from user"; // 預編譯sql preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); while (resultSet.next()){ System.out.println(resultSet.getInt("id") + " " + resultSet.getString("username") + " " + resultSet.getString("password")); } } catch (SQLException | FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } }
db.properties
# 連接設置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///web_test3
username=root
password=1234
# 初始化連接
initialSize=10
# 最大連接數量
maxActive=50
# 最大空閑連接
maxIdle=20
# 最小空閑連接
minIdle=5
# 超時等待時間(以毫秒為單位)
maxWait=60000
C3P0連接池
C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規範和JDBC2的標準擴展。
使用方法與Druid相類似
Maven導包
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency>
基本代碼實現
package com.charles.datasource.demo1; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class C3p0 { @Test public void demo01() { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 使用連接池 ComboPooledDataSource DataSource = new ComboPooledDataSource(); DataSource.setDriverClass("com.mysql.jdbc.Driver"); DataSource.setJdbcUrl("jdbc:mysql:///web_test3"); DataSource.setUser("root"); DataSource.setPassword("1234"); // 獲得連接 connection = DataSource.getConnection(); // 編寫SQL String sql = "select * from user"; // 預編譯sql preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " " + resultSet.getString("username") + " " + resultSet.getString("password")); } } catch (SQLException e) { e.printStackTrace(); } catch (PropertyVetoException e) { e.printStackTrace(); } finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
當然,C3P0連接池也可以通過外部配置文件引用
具體代碼實現:
c3p0-config.xml配置文件
<?xml version="1.0" encoding="utf-8"?> <c3p0-config> <default-config> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///web_test3</property> <property name="user">root</property> <property name="password">1234</property> <property name="initialPoolSize">5</property> <property name="maxPoolSize">20</property> <property name="minPoolSize">5</property> </default-config> </c3p0-config>
單元測試類
@Test public void Demo02(){ Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; try { // 使用連接池 ComboPooledDataSource DataSource = new ComboPooledDataSource(); DataSource.setDriverClass("com.mysql.jdbc.Driver"); DataSource.setJdbcUrl("jdbc:mysql:///web_test3"); DataSource.setUser("root"); DataSource.setPassword("1234"); // 獲得連接 connection = DataSource.getConnection(); // 編寫SQL String sql = "select * from user"; // 預編譯sql preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { System.out.println(resultSet.getInt("id") + " " + resultSet.getString("username") + " " + resultSet.getString("password")); } } catch (SQLException e) { e.printStackTrace(); } catch (PropertyVetoException e) { e.printStackTrace(); } finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } }
這裡註意,只要將配置文件放在預設路徑下,它就會自動查找,不需要手動導入。
Commons DbUtils一個對JDBC進行簡單封裝的工具類庫,它能夠簡化JDBC應用程式的開發,同時也不會影響程式的性能。
兩個字:方便 -----> 一個字:懶
ArrayHandler 和 ArrayListHandler:
小結
以上便是JDBC的內容了,多練,練到煩了就會了。當然為了更偷懶,我們後期將會學到Mybatis,這個框架比JDBC更方便。
加油!
時間:2020-04-06 01:41:37