Java (L2)-- JDBC

JDBC in Action

JDBC作為 Java 與關(guān)系型數(shù)據(jù)庫交互的接口红符,在開發(fā)中扮演著十分重要的角色岩四。

Part I. 簡介

JDBC 即 Java Database Connectivity官份, 中文名為 Java 數(shù)據(jù)庫連接, 是由 Sun 公司定義的一組 API 语婴。 各大數(shù)據(jù)庫廠商根據(jù)定義做了具體的實現(xiàn)孕惜,并提供jar 驅(qū)動包給用戶使用,當(dāng)使用 JDBC 進(jìn)行編程時酌壕,執(zhí)行的是 jar 包中的實現(xiàn)類掏愁。

數(shù)據(jù)庫、JDBC卵牍、驅(qū)動果港、Java 代碼的關(guān)系如下圖所示:

各部分關(guān)系

java.sql 包提供了 JDBC 的類與接口,主要有以下幾種:

  • Driver 接口
  • Connection 接口
  • Statement 接口
  • PreparedStatement 接口
  • CallableStatement 接口
  • ResultSet 接口

Part II. 快速入門

接下來以 MySQL 為例糊昙,介紹 JDBC 快速入門:

  1. 導(dǎo)入驅(qū)動 jar 包辛掠,添加到項目的 libs 目錄下;
  2. 注冊驅(qū)動释牺,MySQL 5 及之后版本的驅(qū)動可省略此步驟萝衩;
  3. 獲取數(shù)據(jù)庫連接對象 Connection;
  4. 獲取執(zhí)行 sql 語句的對象 Statement没咙;
  5. 定義 sql 語句猩谊;
  6. 執(zhí)行 sql 語句,返回結(jié)果祭刚;
  7. 處理結(jié)果并釋放資源牌捷。

代碼如下:

        Class.forName("com.mysql.jdbc.Driver");
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "mysql2018");
        String sql = "update account set balance = 500 where id = 1";
        Statement stmt = conn.createStatement();
        int count = stmt.executeUpdate(sql);
        System.out.println(count);
        stmt.close();
        conn.close();

Part III. 各部分詳解

1. Drivermanager 對象

驅(qū)動管理對象,功能:

  • 注冊驅(qū)動(即告知 JDBC 該使用哪個數(shù)據(jù)庫的驅(qū)動 jar 包):

    static void registerDriver(Driver driver); 
    
    Class.forName("com.mysql.jdbc.Driver");
    通過查看源代碼涡驮,可以看到在com.mysql.jdbc.Driver類中存在靜態(tài)代碼塊
    static {
      try {
          java.sql.DriverManager.registerDriver(new Driver());
      } catch (SQLException E) {
          throw new RuntimeException("Can't register driver!");
      }
    }
    
  • 獲取數(shù)據(jù)庫的連接

    public static Connection getConnection(String url,
                                           String user,
                                           String password)
    /**
      url的格式為 jdbc:mysql://ip地址(域名):端口號/數(shù)據(jù)庫名稱
      如果連接的是本機mysql服務(wù)器宜鸯,并且mysql服務(wù)默認(rèn)端口是3306,則url可以簡寫為:jdbc:mysql:///數(shù)據(jù)庫名稱
    */
    

2. Connection 對象

數(shù)據(jù)庫連接對象遮怜,功能:

  • 獲取 Statement 對象以執(zhí)行 sql 語句

    Statement createStatement();
    PreparedStatement prepareStatement(String sql);
    
  • 管理事務(wù)

    void setAutoCommit(boolean autoCommit); // true-> 關(guān)閉事務(wù)淋袖;false->開啟事務(wù)
    void commit(); //提交事務(wù)
    void rollback(); //回滾事務(wù)
    

3. Statement 對象

執(zhí)行 sql 語句的對象,功能:

  • 執(zhí)行 sql 語句:

    boolean execute(String sql); //執(zhí)行任意語句
    int executeUpdate(String sql); //執(zhí)行 DML(insert, update, delete) 語句和 DDL(create, alter, drop) 語句锯梁;其返回值為操作所影響的行數(shù)即碗,可用于判斷 sql 執(zhí)行是否成功。
    ResultSet executeQuery(String sql); //執(zhí)行 DQL(select) 語句
    

4. ResultSet 對象

查詢結(jié)果集對象陌凳,對查詢結(jié)果進(jìn)行封裝

boolean next(); //將游標(biāo)向下移動一行剥懒,并判斷當(dāng)前行是否有意義(即是否為最后一行的末尾),返回 true合敦、false
getXXX(); //XXX為返回值類型初橘,如 int getInt(); String getString(); 其參數(shù)類型可以是代表列編號的整形或者字符串類型的列名稱

5. PreparedStatement 對象

執(zhí)行預(yù)編譯的 sql 的對象,可防止 SQL 注入問題并提高效率

String sql = "select * from user where username = ? and password = ?;"; //定義 sql 語句,使用 保檐? 作為占位符
PreparedStatement pstmt = Connection.prepareStatement(String sql); //獲取PrepareStatement對象
pstmr.setString(1,"Tom"); //位置編號(從1開始)耕蝉、參數(shù)值
pstmt.setInt(2,5000);
int count = pstmt.executeUpdate();

Part IV. 定義JDBC工具類

0.獲取配置文件所在路徑

對于 Class.getResource() 和 ClassLoader.getResource() 的淺顯理解:

//若定義了一個 Test 類,則:
Test.class.getResource(""); //返回out目錄中表示src根目錄的URL對象
Test.class.getResource("/"); //返回返回out目錄中Test.class所在的目錄的URL對象
Test.class.getResource(String Filename); //返回out目錄中指向Test.class所在目錄下名為Filename文件或目錄的URL對象
//Test.class.getClassLoader.getResource()方法與上述相同夜只,不過不支持"/"垒在。
 //URL.getPath()方法返回絕對路徑的字符串表示   

1. 編寫 jdbc.properties 文件

url = jdbc:mysql:///test?useSSL=false
user=root
password=mysql2018
driver=com.mysql.jdbc.Driver

2.編寫 JDBCUtils.java 文件

public class JDBCUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;
    
    //使用靜態(tài)代碼塊,完成數(shù)據(jù)庫連接參數(shù)的獲取
    static{
         try {
            //1. 創(chuàng)建Properties集合類扔亥。
            Properties pro = new Properties();

            //獲取src路徑下的文件的方式--->ClassLoader 類加載器
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            URL res  = classLoader.getResource("jdbc.properties");
            String path = res.getPath();
             
            //2. 加載文件
            pro.load(new FileReader(path));

            //3. 獲取數(shù)據(jù)场躯,賦值
            url = pro.getProperty("url");
            user = pro.getProperty("user");
            password = pro.getProperty("password");
            driver = pro.getProperty("driver");
             
            //4. 注冊驅(qū)動
            Class.forName(driver);
            } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    //獲取連接
    public static Connection getConnection() throws SQLException {

        return DriverManager.getConnection(url, user, password);
    }
    
    //釋放資源
    public static void close(Statement stmt,Connection conn){
        if( stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if( conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    //方法重載
    public static void close(ResultSet rs,Statement stmt, Connection conn){
        if( rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if( stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if( conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

Part V. JDBC 控制事務(wù)

操作:開啟事務(wù)、提交事務(wù)旅挤、回滾事務(wù)

//1.獲取連接
conn = JDBCUtils.getConnection();
//開啟事務(wù)
conn.setAutoCommit(false);
try{
    /**
    假裝有一些操作
    */
    conn.commit();//提交事務(wù)
}catch (Exception e) {
try {
    if(conn != null) {
        conn.rollback();//事務(wù)回滾
    }
} catch (SQLException e1) {
    e1.printStackTrace();
    }
    e.printStackTrace();
}finally {
    JDBCUtils.close(pstmt1,conn);
    JDBCUtils.close(pstmt2,null);
}

Part VI. 數(shù)據(jù)庫連接池

上文中每次連接數(shù)據(jù)庫都需要加載數(shù)據(jù)庫驅(qū)動踢关、獲取 Connection 對象、獲取 Statement 對象粘茄、執(zhí)行 sql 語句耘成、釋放資源等步驟。而建立數(shù)據(jù)庫連接是一件比較消耗資源和時間的事情驹闰,若使用者較多會給系統(tǒng)造成比較大的負(fù)擔(dān)。一個常見的改良方法是創(chuàng)建一個用來存放數(shù)據(jù)庫連接的容器撒会,容器初始化時會申請一些連接對象嘹朗,用戶對數(shù)據(jù)庫進(jìn)行訪問時會先容器中獲取一個連接對象,用戶訪問完成后會將類诵肛,連接對象歸還給容器屹培。

0.數(shù)據(jù)庫連接池的實現(xiàn)

標(biāo)準(zhǔn)接口:javax.sql 包中的 DataSource 接口。

  • getConnection() 獲取連接對象
  • Connection.close() 如果 Connection 對象為數(shù)據(jù)庫連接池獲取的怔檩,則調(diào)用 close() 方法可以歸還連接對象褪秀。

數(shù)據(jù)庫連接池的實現(xiàn):由數(shù)據(jù)庫廠商實現(xiàn),目前用用比較廣的是以下兩個:

  • C3p0
  • Druid (Alibaba)

1.C3p0

//1. 導(dǎo)入C3p0包和數(shù)據(jù)庫驅(qū)動包p薛训;
//2. 定義配置文件媒吗,在 src 目錄下創(chuàng)建c3p0.properties 或 c3p0-config.xml,需要定義數(shù)據(jù)庫連接參數(shù)和連接池的參數(shù)乙埃;
//3. 創(chuàng)建數(shù)據(jù)庫連接池對象闸英,可根據(jù) configName 選擇配置,若空則為默認(rèn)配置介袜;
DataSource ds  = new ComboPooledDataSource(String configName);
//4. 獲取連接對象
Connection conn = ds.getConnection();
//5. 釋放連接
conn.close()

2. Druid

//1. 導(dǎo)入 druid 包和數(shù)據(jù)庫驅(qū)動包甫何;
//2. 定義配置文件,形式為 *.properties, 可以放在任意目錄下遇伞;
//3. 加載配置文件到 Properties 對象 pro 中辙喂;
Properties pro = new Properties();
InputStream is = DruidDemo.class.getResourceAsStream("druid.properties");
pro.load(is);
//4. 通過工廠獲取數(shù)據(jù)庫連接池對象 
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5. 獲取連接對象
Connection conn = ds.getConnection();
//6. 釋放連接
conn.close() 

以下的使用方法更為常見:

//1. 定義工具類 JDBCUtils,提供靜態(tài)代碼塊加載配置文件,初始化連接池對象巍耗,并提供獲取連接秋麸、釋放資源等方法。
public class JDBCUtils {
    //定義成員變量 DataSource
    private static DataSource ds;
    static{
        try {
            //加載配置文件
            Properties pro = new Properties();                   pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
           //獲取DataSource
           ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e) {
              e.printStackTrace();
        } catch (Exception e) {
              e.printStackTrace();
        }
    }
    //獲取連接
    public static Connection getConnection() throws SQLException {
                return ds.getConnection();
    }   
    //釋放資源
    public static void close(ResultSet rs , Statement stmt, Connection conn){


        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }


        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(conn != null){
            try {
                conn.close();//歸還連接
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    //方法重載
    public static void close(Statement stmt,Connection conn){
       close(null,stmt,conn);
    }
    //獲取連接池
    public static DataSource getDataSource(){
        return  ds;
    }
    
//2. 使用druid
    conn = JDBCUtils.getConnection();
    String sql = "insert into account values(null,?,?)";
    pstmt = conn.prepareStatement(sql);
    pstmt.setString(1,"Tom");
    pstmt.setDouble(2,3000);
    int count = pstmt.executeUpdate();

Part VII. Spring JDBC

Spring JDBC 為 Spring 框架對 JDBC 的簡單封裝芍锦,提供了一個 JDBCTemplate 對象簡化 JDBC 的開發(fā)竹勉。

//1. 導(dǎo)入 jar 包;
//2. 創(chuàng)建 JdbcTemplate 對象娄琉,需要一個 DataSource 對象做參數(shù)次乓;
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
3. 調(diào)用JdbcTemplate的方法來完成CRUD的操作
    update(): 執(zhí)行 DML 語句
    queryForMap():查詢結(jié)果將結(jié)果集封裝為 map 集合,將列名作為 key孽水,將值作為 value
    queryForList():查詢結(jié)果將結(jié)果集封裝為 list 集合(將每一條記錄封裝為一個Map集合票腰,再將Map集合裝載到List集合中)
    query():查詢結(jié)果,將結(jié)果封裝為 JavaBean 對象, query 的參為 RowMapper,一般我們使用BeanPropertyRowMapper 實現(xiàn)類女气,可以完成數(shù)據(jù)到 JavaBean 的自動封裝杏慰,格式為 new BeanPropertyRowMapper<類型>(類型.class)
    queryForObject:查詢結(jié)果,將結(jié)果封裝為對象炼鞠,一般用于聚合函數(shù)的查詢
*/

//查詢預(yù)編譯的 sql
String sql = "update account set balance = 5000 where id = ?";
int count = template.update(sql, 3);

//查詢另一個預(yù)編譯的 sql
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
int count = template.update(sql, 1015, "Tom", 10);

//queryForMap 方法缘滥,將查詢結(jié)果封裝成 Map
String sql = "select * from emp where id = ? or id = ?";
Map<String, Object> map = template.queryForMap(sql, 1001,1002);

//queryForList 方法,將查詢結(jié)果封裝成 Map 再封裝成 List
String sql = "select * from emp";
List<Map<String, Object>> list = template.queryForList(sql);

//以下兩種操作等價
String sql = "select * from emp";
List<Emp> list = template.query(sql, new RowMapper<Emp>() {

      @Override
      public Emp mapRow(ResultSet rs, int i) throws SQLException {
          Emp emp = new Emp();
          int id = rs.getInt("id");
          String ename = rs.getString("ename");
          int job_id = rs.getInt("job_id");
          int mgr = rs.getInt("mgr");
          Date joindate = rs.getDate("joindate");
          double salary = rs.getDouble("salary");
          double bonus = rs.getDouble("bonus");
          int dept_id = rs.getInt("dept_id");

          emp.setId(id);
          emp.setEname(ename);
          emp.setJob_id(job_id);
          emp.setMgr(mgr);
          emp.setJoindate(joindate);
          emp.setSalary(salary);
          emp.setBonus(bonus);
          emp.setDept_id(dept_id);

          return emp;
      }
});

String sql = "select * from emp";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));

關(guān)于 JDBC 就先這樣吧谒主,以后遇到再補充23333

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朝扼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子霎肯,更是在濱河造成了極大的恐慌擎颖,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件观游,死亡現(xiàn)場離奇詭異搂捧,居然都是意外死亡,警方通過查閱死者的電腦和手機懂缕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門允跑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搪柑,你說我怎么就攤上這事吮蛹。” “怎么了拌屏?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵潮针,是天一觀的道長。 經(jīng)常有香客問我倚喂,道長每篷,這世上最難降的妖魔是什么瓣戚? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮焦读,結(jié)果婚禮上子库,老公的妹妹穿的比我還像新娘。我一直安慰自己矗晃,他們只是感情好仑嗅,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著张症,像睡著了一般仓技。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上俗他,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天脖捻,我揣著相機與錄音,去河邊找鬼兆衅。 笑死地沮,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羡亩。 我是一名探鬼主播摩疑,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼畏铆!你這毒婦竟也來了雷袋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤及志,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寨腔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體速侈,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年迫卢,在試婚紗的時候發(fā)現(xiàn)自己被綠了倚搬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡乾蛤,死狀恐怖每界,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情家卖,我是刑警寧澤眨层,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站上荡,受9級特大地震影響趴樱,放射性物質(zhì)發(fā)生泄漏馒闷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一叁征、第九天 我趴在偏房一處隱蔽的房頂上張望纳账。 院中可真熱鬧,春花似錦捺疼、人聲如沸疏虫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卧秘。三九已至,卻和暖如春媳友,著一層夾襖步出監(jiān)牢的瞬間斯议,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工醇锚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留哼御,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓焊唬,卻偏偏與公主長得像恋昼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赶促,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351