后端学习 - JDBC
生活随笔
收集整理的這篇文章主要介紹了
后端学习 - JDBC
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 一 JDBC概述
- 1 Java中的數據存儲技術
- 2 什么是JDBC
- 3 JDBC程序的編寫步驟
- 二 Java連接數據庫的方式
- 三 使用 PreparedStatement 實現 CRUD 操作
- 1 數據庫的調用的三個接口
- 2 增Create/刪Delete/改Update 操作
- 3 查Retrieval操作
- 4 批量插入操作
- 四 數據庫事務
- 1 事務
- 2 事務的 ACID 性質
- 3 三種導致數據自動提交的操作
- 4 MySQL 的四種隔離級別
- 5 根據 ACID 改進實現
- 五 DAO(Data Access Object) 邏輯架構
- 六 數據庫連接池
- 1 作用
- 2 C3P0 數據庫連接池
- 3 DBCP 數據庫連接池
- 4 Druid 數據庫連接池(實用) *
- 七 Apache-DBUtils 實現 CRUD 操作 *
- 1 實現
- 2 自定義 Handler
- 3 資源的關閉
一 JDBC概述
1 Java中的數據存儲技術
- JDBC(Java Database Connectivity)直接訪問數據庫;
- JDO(Java Data Object)技術;
- 第三方工具,如Hibernate, Mybatis 等。
后兩種本質上是更好地封裝了JDBC。
2 什么是JDBC
獨立于特定數據庫管理系統、通用的SQL數據庫存取和操作的公共接口(一組API),定義了用來訪問數據庫的標準Java類庫,使用這些類庫可以以一種標準的方法、方便地訪問數據庫資源。JDBC為訪問不同的數據庫提供了一種統一的途徑,為開發者屏蔽了一些細節問題。
3 JDBC程序的編寫步驟
二 Java連接數據庫的方式
- 一個數據庫連接就是一個 socket 連接。socket = (IP : port)
- 這種方式實現了數據與代碼的分離,實現了解耦;如果需要修改配置文件信息,可以避免程序重新打包。
三 使用 PreparedStatement 實現 CRUD 操作
1 數據庫的調用的三個接口
- Statement(已淘汰):用于執行靜態 SQL 語句并返回它所生成結果的對象;
- PreparedStatement:SQL 語句被預編譯并存儲在此對象中(所以,一個 PreparedStatement 的實例表示一條預編譯過的 SQL 語句),可以使用此對象多次高效地執行該語句,是Statement的子接口;
- CallableStatement:用于執行 SQL 存儲過程,是Statement的子接口。
2 增Create/刪Delete/改Update 操作
根據有無返回值,將增刪改歸為一類方法,查歸為一類方法。
/* 定義時 */public void commonOps(String sql, Object... args) {Connection conn = null;PreparedStatement ps = null;try {// 1.連接數據庫,獲得conn,并根據sql預加載psconn = JDBCUtils.getMyConnection(); // 自己封裝的方法,實現同二部分ps = conn.prepareStatement(sql); // PreparedStatement的特性:預編譯sql語句// 2.填充占位符for (int i = 0; i < args.length; i++) {ps.setObject(i + 1, args[i]); // sql索引從1開始}// 3.執行ps.execute();} catch (Exception e) {e.printStackTrace();} finally {// 4.關閉資源JDBCUtils.closeMyResources(conn, ps);}}/* 使用時 */public void deleteTest() {String sql = "delete from customers where name = ?";String delete_name = "張三";commonOps(sql, delete_name);}3 查Retrieval操作
- ResultSet 的 next() ,執行兩步操作:如果下一項不為空,返回 true,并將指針下移。(和 Iterator 的方法相比,相當于一起執行了 hasNext() 和 next())
- ResultSetMetaData 用于獲取查詢結果 ResultSet 的元數據,在此獲取的是表的 列數、列名。
- 和 getColumnName() 相比,getColumnLabel() 避免了表名和屬性名不統一,方便反射操作。如果表列名和屬性名不一致,則在輸入sql語句時需要將查詢的別名設置為屬性名。
- ORM object relational mapping思想:表的一行代表一個實例,表的一列代表一個屬性。
執行步驟:
4 批量插入操作
使用了兩個技巧:
四 數據庫事務
1 事務
- 事務是一組邏輯操作單元,使數據從一種狀態變換到另一種狀態。
- 事務的操作要么全部執行,要么全不執行。
2 事務的 ACID 性質
- 原子性(DBMS保證):事務的操作要么全部執行,要么全不執行。
- 一致性(用戶保證):如果事務的程序正確,并且事務啟動時數據庫處于一致狀態,則事務結束時數據庫也要處于一致狀態。
- 隔離性(DBMS保證):一個事務的執行不受其它事務的影響。
- 持久性(DBMS保證):事務一旦提交,事務對數據庫的修改一定全部持久地寫到數據庫中。
3 三種導致數據自動提交的操作
- DDL(Data Definition Language) 操作一旦執行,都會自動提交。
- DML(Data Manipulation Language) 默認情況下,一旦執行,就會自動提交。可以通過 conn.setAutoCommit(false) 的方式取消DML操作的自動提交。若此時 Connection 沒有被關閉,還可能被重復使用,則需要恢復其自動提交狀態 setAutoCommit(true)。尤其是在使用數據庫連接池技術時,執行close()方法前,建議恢復自動提交狀態。
- 默認在關閉連接時自動提交。
4 MySQL 的四種隔離級別
- READ UNCOMMITTED 讀未提交:可能發生臟讀、不可重復讀、幻讀;
- READ COMMITTED 讀提交:可能發生不可重復讀、幻讀;
- REPEATABLE READ 可重復讀:可能發生幻讀;
- SERIALIZABLE 串行化:不會發生以上情況,但效率極低。
幻讀舉例:例如第一個事務對一個表中的數據進行了修改,比如這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,就會發生操作第一個事務的用戶發現表中還存在沒有修改的數據行,就好象發生了幻覺一樣。
5 根據 ACID 改進實現
- 執行前關閉 connection 的自動提交。
- 在哪個方法創建的資源,在哪個方法關閉。
- finally 塊負責方法的關閉,事務的提交,以及恢復 connection 的自動提交。
- catch 塊負責 rollback。
五 DAO(Data Access Object) 邏輯架構
- DAO 基類設置為泛型類,因為要供所有的表使用。
- DAO 接口非泛型類,因為一張表對應一個接口,查詢的返回對象類型是確定的。
六 數據庫連接池
1 作用
- 為數據庫連接建立一個“緩沖池”。預先在緩沖池中放入一定數量的連接,當需要建立數據庫連接時,只需從“緩沖池”中取出一個,使用完畢之后再放回去。當數據庫訪問結束后,程序還是像以前一樣關閉數據庫連接:conn.close(); 但沒有關閉數據庫的物理連接,僅僅把數據庫連接釋放,歸還給數據庫連接池。
- 數據庫連接池負責分配、管理和釋放數據庫連接,允許應用程序重復使用一個現有的數據庫連接,而不是重新建立一個。
- 數據庫連接池在初始化時將創建一定數量的數據庫連接放到連接池中。通過限制最小和最大的連接數,保證數據庫對連接數的控制。
- 使用時,數據庫連接池只需創建一個,其中的連接可以創建多個。創建數據庫連接池可以用靜態代碼塊進行初始化。
2 C3P0 數據庫連接池
C3P0 是一個開源組織提供的一個數據庫連接池,速度相對較慢,穩定性較好。
使用 ComboPooledDataSource 類代替之前使用的 DriverManager 進行連接的獲取。
public void xmlTest () throws Exception {ComboPooledDataSource cpds = new ComboPooledDataSource("my_config");Connection conn = cpds.getConnection();}其中 c3p0-config.xml 的內容如下(文件名不能更改)
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config><named-config name="my_config"><!-- 提供獲取連接的4個基本信息 --><property name="driverClass">com.mysql.cj.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql:///test</property><property name="user">root</property><property name="password">123</property><!-- 進行數據庫連接池管理的基本信息 --><!-- 當數據庫連接池中的連接數不夠時,c3p0一次性向數據庫服務器申請的連接數 --><property name="acquireIncrement">5</property><!-- c3p0數據庫連接池中初始化時的連接數 --><property name="initialPoolSize">10</property><!-- c3p0數據庫連接池維護的最少連接數 --><property name="minPoolSize">10</property><!-- c3p0數據庫連接池維護的最多的連接數 --><property name="maxPoolSize">100</property><!-- c3p0數據庫連接池最多維護的Statement的個數 --><property name="maxStatements">50</property><!-- 每個連接中可以最多使用的Statement的個數 --><property name="maxStatementsPerConnection">2</property></named-config> </c3p0-config>3 DBCP 數據庫連接池
- Tomcat 的連接池采用該連接池實現。
- 速度相對C3P0較快,但存在 bug,不夠穩定。
4 Druid 數據庫連接池(實用) *
@Testpublic void getConnectionTest () throws Exception {/* 加載配置文件 */Properties properties = new Properties();InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");properties.load(is);/* 創建連接池 */DataSource source = DruidDataSourceFactory.createDataSource(properties);/* 獲取連接 */Connection conn = source.getConnection();}七 Apache-DBUtils 實現 CRUD 操作 *
1 實現
public class ApacheUtilsTest {// 初始化數據庫連接池private static DataSource source;static {InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");Properties properties = new Properties();try {properties.load(is);ApacheUtilsTest.source = DruidDataSourceFactory.createDataSource(properties);} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}}// 增刪改@Testpublic void insertTest() throws SQLException {QueryRunner queryRunner = new QueryRunner();Connection conn = ApacheUtilsTest.source.getConnection();String sql = "insert into customers (name, email, birth) values (?, ?, ?)";int count = queryRunner.update(conn, sql, "伍佰", "500@gmail.com", "1966-08-08");System.out.println(count);conn.close();}// 查記錄@Testpublic void selectTest() throws SQLException {QueryRunner queryRunner = new QueryRunner();Connection conn = ApacheUtilsTest.source.getConnection();String sql = "select name, email from customers where id > ?";/* 查詢一條記錄 */// ResultSetHandler 接口的一個實現類,用于封裝表的一條記錄// BeanHandler<Customer> beanHandler = new BeanHandler<>(Customer.class);// Customer customer = queryRunner.query(conn, sql, beanHandler, 19);/* 查詢多條記錄 */// ResultSetHandler 接口的一個實現類,用于封裝表的多條記錄,返回結果相應地使用 List<Customer> 類型BeanListHandler<Customer> beanListHandler = new BeanListHandler<>(Customer.class);List<Customer> customers = queryRunner.query(conn, sql, beanListHandler, 18);/** 查詢多條記錄還有 MapListHandler,將一條記錄作為一個 Map,key 是列名,value 是列的具體值* 返回值類型是 List<Map<String, Object>>** 同理查詢一條記錄的 MapHandler 的返回值類型是 Map<String, Object>** 所以:xxHandler 代表查詢的返回值類型是 xx ??* */System.out.println(customers);conn.close();}// 查特殊值:使用 ScalarHandler@Testpublic void selectScalarTest() throws SQLException{QueryRunner queryRunner = new QueryRunner();Connection conn = ApacheUtilsTest.source.getConnection();String sql = "select count(*) from customers";ScalarHandler scalarHandler = new ScalarHandler();Long count = (Long) queryRunner.query(conn, sql, scalarHandler);System.out.println(count);} }2 自定義 Handler
- ResultSetHandler 是所有 Handler 實現的接口。
- 自定義 Handler,重寫 ResultSetHandler 接口下的 handle() 方法即可。
3 資源的關閉
import org.apache.commons.dbutils.DbUtils;DbUtils.closeQuietly(connection); DbUtils.closeQuietly(preparedStatement); DbUtils.closeQuietly(resultSet); 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的后端学习 - JDBC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 后端学习 - Spring5
- 下一篇: 后端学习 - JVM(上)内存与垃圾回收