今天,接着上一篇( mysql 數據庫 )的基礎上,我就寫一下 Java 怎樣連接數據庫,並且操作數據庫。
首先,我們先來准備一下數據庫連接的驅動:
mysql 的 jar 包下載地址:https://dev.mysql.com/downloads/connector/j/
Oracle 的 jar 包下載地址:https://www.oracle.com/technetwork/cn/middleware/goldengate/downloads/index.html
本文章還用到 c3p0 的數據庫連接池
c3p0 的 jar 包下載地址:https://sourceforge.net/projects/c3p0/
接下來,我們就開始正式了解一下什么是 JDBC ,用 Java 怎樣連接數據庫,並且怎樣操作數據庫。
什么是JDBC
JDBC(Java DataBase Connectivity,java數據庫連接)是一種用於執行SQL語句的Java API,可以為多種關系數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基准,據此可以構建更高級的工具和接口,使數據庫開發人員能夠編寫數據庫應用程序。
數據庫驅動
我們安裝好數據庫之后,我們的應用程序也是不能直接使用數據庫的,必須要通過相應的數據庫驅動程序,通過驅動程序去和數據庫打交道。其實也就是數據庫廠商的JDBC接口實現,即對Connection等接口的實現類的jar文件。
Driver接口
Driver接口由數據庫廠家提供,作為java開發人員,只需要使用Driver接口就可以了。在編程中要連接數據庫,必須先裝載特定廠商的數據庫驅動程序,不同的數據庫有不同的裝載方法。如:
裝載MySql驅動:Class.forName("com.mysql.jdbc.Driver");
裝載Oracle驅動:Class.forName("oracle.jdbc.driver.OracleDriver");
Connection接口
Connection與特定數據庫的連接(會話),在連接上下文中執行sql語句並返回結果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定義的數據庫Connection連接上。
連接MySql數據庫:Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
連接Oracle數據庫:Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
連接SqlServer數據庫:Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://host:port; DatabaseName=database", "user", "password");
常用方法:
-
- createStatement():創建向數據庫發送sql的statement對象。
- prepareStatement(sql) :創建向數據庫發送預編譯sql的PrepareSatement對象。
- prepareCall(sql):創建執行存儲過程的callableStatement對象。
- setAutoCommit(boolean autoCommit):設置事務是否自動提交。
- commit() :在鏈接上提交事務。
- rollback() :在此鏈接上回滾事務。
使用JDBC的步驟
加載JDBC驅動程序 → 建立數據庫連接Connection → 創建執行SQL的語句Statement → 處理執行結果ResultSet → 釋放資源
初始化驅動
通過初始化驅動類com.mysql.jdbc.Driver,該類就在 mysql-connector-java-5.0.8-bin.jar中。如果你使用的是oracle數據庫那么該驅動類將不同。
//加載數據庫驅動程序(對應的 Driver 實現類中有注冊驅動的靜態代碼塊.) String driver ="com.mysql.jdbc.Driver"; Class.forName(driver);
建立JDBC和數據庫之間的Connection連接
//通過 DriverManager 的 getConnection() 方法獲取數據庫連接. Connection conn = DriverManager.getConnection(url,user,password);
這里需要提供:url包括以下(jdbc:mysql://ip地址:端口號/需要連接的數據庫名字),例如:jdbc:mysql://localhost:3306/db_database05
數據庫所處於的ip:127.0.0.1 (這里是本機,如果連接其他電腦上的數據庫,請改變該ip)
數據庫的端口號: 3306 (mysql專用端口號)
數據庫名稱: (根據你自己數據庫中的名稱填寫)
數據庫連接的用戶名:user
數據庫的登錄密碼:password
創建數據庫連接的工具方法
/* * 數據庫連接工具類 */ package com.hzc.util; import java.sql.Connection; import java.sql.DriverManager; public class DataBaseUtil { /** * 獲取數據庫連接 * @return Connection 對象 */ public static Connection getConnection(){ Connection conn = null; try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/db_database05"; conn = DriverManager.getConnection(url,user,password); //這里的user和password根據自己的數據庫登錄和密碼填寫 } catch (Exception e) { e.printStackTrace(); } return conn; } /** * 關閉數據庫連接 * @param conn Connection 對象 */ public static void closeConnection(Connection conn){ //判斷 conn 是否為空 if(conn != null){ try { conn.close(); //關閉數據庫連接 } catch (Exception e) { e.printStackTrace(); } } } }
這里在提供 c3p0 數據庫連接池的方法
C3P0 數據庫連接池
package jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; /* * 操作JDBC的工具類,其中封裝了一些工具方法 */ public class JDBCTools { private static DataSource dataSource = null; //數據庫連接池應只初始化一次 static{ dataSource = new ComboPooledDataSource("helloc3p0"); } //數據庫連接:通過讀取配置文件從數據庫服務器獲取一個連接 public static Connection getConnection() throws Exception{ return dataSource.getConnection(); } //關閉 ResultSet、Statement 和 Connection public static void release(ResultSet rs,Statement statement,Connection conn){ if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(statement != null){ try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } //數據庫連接池的 Connection 對象進行 close 時 //並不是真的進行關閉, 而是把該數據庫連接會歸還到數據庫連接池中. if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /* * 處理數據庫的事務 */ //提交事務 public static void commit(Connection conn){ if(conn != null){ try { conn.commit(); } catch (SQLException e) { e.printStackTrace(); } } } //回滾事務 public static void rollback(Connection conn){ if(conn != null){ try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } } //開始事務 public static void beginTx(Connection conn){ if(conn != null){ try { conn.setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } } }
C3P0 要在 src 目錄下配置:創建一個名為 c3p0-config.xml ,並且記住要導入 c3p0 的 jar 包。
c3p0-config.xml 的配置
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <named-config name="helloc3p0"> <!-- 指定連接數據源的基本屬性 --> <property name="user">登錄名</property> <property name="password">密碼</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property> <!-- 若數據庫中連接數不足時, 一次向數據庫服務器申請多少個連接 --> <property name="acquireIncrement">5</property> <!-- 初始化數據庫連接池時連接的數量 --> <property name="initialPoolSize">5</property> <!-- 數據庫連接池中的最小的數據庫連接數 --> <property name="minPoolSize">5</property> <!-- 數據庫連接池中的最大的數據庫連接數 --> <property name="maxPoolSize">10</property> <!-- C3P0 數據庫連接池可以維護的 Statement 的個數 --> <property name="maxStatements">20</property> <!-- 每個連接同時可以使用的 Statement 對象的個數 --> <property name="maxStatementsPerConnection">5</property> </named-config> </c3p0-config>
Statement接口
用於執行靜態SQL語句並返回它所生成結果的對象。
三種Statement類:
-
- Statement:由createStatement創建,用於發送簡單的SQL語句(不帶參數)。
- PreparedStatement :繼承自Statement接口,由preparedStatement創建,用於發送含有一個或多個參數的SQL語句。PreparedStatement對象比Statement對象的效率更高,並且可以防止SQL注入,所以我們一般都使用PreparedStatement。
- CallableStatement:繼承自PreparedStatement接口,由方法prepareCall創建,用於調用存儲過程。
常用Statement方法:
-
- execute(String sql):運行語句,返回是否有結果集
- executeQuery(String sql):運行select語句,返回ResultSet結果集。
- executeUpdate(String sql):運行insert/update/delete操作,返回更新的行數。
- addBatch(String sql) :把多條sql語句放到一個批處理中。
- executeBatch():向數據庫發送一批sql語句執行。
ResultSet接口
ResultSet提供檢索不同類型字段的方法,常用的有:
-
- getString(int index)、getString(String columnName):獲得在數據庫里是varchar、char等類型的數據對象。
- getFloat(int index)、getFloat(String columnName):獲得在數據庫里是Float類型的數據對象。
- getDate(int index)、getDate(String columnName):獲得在數據庫里是Date類型的數據。
- getBoolean(int index)、getBoolean(String columnName):獲得在數據庫里是Boolean類型的數據。
- getObject(int index)、getObject(String columnName):獲取在數據庫里任意類型的數據。
ResultSet還提供了對結果集進行滾動的方法:
-
- next():移動到下一行
- Previous():移動到前一行
- absolute(int row):移動到指定行
- beforeFirst():移動resultSet的最前面。
- afterLast() :移動到resultSet的最后面。
使用后依次關閉對象及連接:ResultSet → Statement → Connection
這里就直接提供數據庫通用的查詢方法,自己寫成一個公用類,需要用查詢方法的時候,就可以繼承它,調用里面的方法。
JDBC 的 Dao 方法
package jdbc; /* * JDBC的工具方法 */ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.BeanUtils; public class Dao { // 執行 SQL 語句,使用 PreparedStatement public void update(String sql, Object... args) { Connection conn = null; PreparedStatement ps = null; try { conn = JDBCTools.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } ps.executeUpdate(); } catch (Exception e) { e.printStackTrace(); } finally { JDBCTools.release(null, ps, conn); } } // 執行SQL的方法,使用 Statement:insert、update、delete,而不包含select public void update(String sql) { Connection conn = null; Statement st = null; try { conn = JDBCTools.getConnection(); st = conn.createStatement(); st.executeUpdate(sql); } catch (Exception e) { e.printStackTrace(); } finally { JDBCTools.release(null, st, conn); } } // 查詢一條記錄,返回對應的對象 public <T> T get(Class<T> clazz, String sql, Object... args)throws Exception { List<T> result = getForList(clazz, sql, args); if (result.size() > 0) { return result.get(0); } return null; } /* * 傳入 SQL 語句和 Class 對象,返回 SQL 語句查詢的記錄對應的 Class 類的對象的集合 * * @param clazz: 對象的類型 * * @param sql: SQL 語句 * * @param args: 填充 SQL 語句的占位符的可變參數 * * @return */ public <T> List<T> getForList(Class<T> clazz, String sql, Object... args)throws Exception { List<T> list = new ArrayList<>(); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 1、得到結果集 conn = JDBCTools.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } rs = ps.executeQuery(); // 2、處理結果集,得到 Map 的 List, 其中一個 Map 對象就是一條記錄. Map 的 key 為 rs 中列的別名, // Map 的 value 為列的值 List<Map<String, Object>> values = handleResultSetToMapList(rs); // 3、把 Map 的 List 轉為 clazz 對應的 List, 其中 Map 的 key 即為 clazz 對應的對象的 // propertyName, // 而 Map 的 value 即為 clazz 對應的對象的 propertyValue list = transfterMapListToBeanList(clazz, values); } catch (Exception e) { e.printStackTrace(); } finally { JDBCTools.release(rs, ps, conn); } return list; } // 把 Map 的 List 轉為 clazz 對應的 List public <T> List<T> transfterMapListToBeanList(Class<T> clazz, List<Map<String, Object>> values)throws Exception { List<T> result = new ArrayList<>(); T bean = null; if (values.size() > 0) { for (Map<String, Object> m : values) { bean = clazz.newInstance(); for (Map.Entry<String, Object> entry : m.entrySet()) { String propertyName = entry.getKey(); Object value = entry.getValue(); BeanUtils.setProperty(bean, propertyName, value); } // 把 Object 對象放入到 list 中 result.add(bean); } } return result; } /** * 處理結果集, 得到 Map 的一個 List, 其中一個 Map 對象對應一條記 * * @param resultSet * @return * @throws SQLException */ public List<Map<String, Object>> handleResultSetToMapList(ResultSet rs) throws Exception { // 准備一個 List<Map<String, Object>>:鍵: 存放列的別名, 值: 存放列的值. 其中一個 Map // 對象對應着一條記錄 List<Map<String, Object>> values = new ArrayList<>(); List<String> columnLabels = getColumnLabels(rs); Map<String, Object> map = null; // 處理 ResultSet, 使用 while 循環 while (rs.next()) { map = new HashMap<>(); for (String columnLabel : columnLabels) { Object value = rs.getObject(columnLabel); map.put(columnLabel, value); } // 把一條記錄的一個 Map 對象放入 List 中 values.add(map); } return values; } /** * 獲取結果集的 ColumnLabel 對應的 List * * @param rs * @return * @throws SQLException */ private List<String> getColumnLabels(ResultSet rs) throws Exception { List<String> labels = new ArrayList<>(); ResultSetMetaData rsmd = rs.getMetaData(); for (int i = 0; i < rsmd.getColumnCount(); i++) { labels.add(rsmd.getColumnLabel(i + 1)); } return labels; } // 返回某條記錄的某一個字段的值 或 一個統計的值(一共有多少條記錄等.) @SuppressWarnings("unchecked") public <E> E getForValue(String sql, Object... args) { // 1. 得到結果集: 該結果集應該只有一行, 且只有一列 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { // 1. 得到結果集 conn = JDBCTools.getConnection(); ps = conn.prepareStatement(sql); for (int i = 0; i < args.length; i++) { ps.setObject(i + 1, args[i]); } rs = ps.executeQuery(); if (rs.next()) { return (E) rs.getObject(1); } } catch (Exception ex) { ex.printStackTrace(); } finally { JDBCTools.release(rs, ps, conn); } // 2. 取得結果 return null; } }
掌握好 JDBC 的連接,Java 寫很多項目都要與數據庫打交道。
自己寫的不是專業,希望大家提一下意見。自己也是在學習 Java 的小白。寫一下博客記錄一下自己的學習情況。寫一下博客,能了解自己學習的水平。